summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
commitd891e9e5bc309d5aeb2ab774c76b34a92085b3e7 (patch)
tree94b68fc1c01f90cad62a171c27007ff317031008
Initial revision
-rw-r--r--br-nf-bds/README11
-rw-r--r--br-nf-bds/linux/include/linux/netfilter.h189
-rw-r--r--br-nf-bds/linux/include/linux/netfilter_ipv4.h80
-rw-r--r--br-nf-bds/linux/include/linux/skbuff.h1152
-rw-r--r--br-nf-bds/linux/net/Config.in95
-rw-r--r--br-nf-bds/linux/net/bridge/Makefile18
-rw-r--r--br-nf-bds/linux/net/bridge/br.c89
-rw-r--r--br-nf-bds/linux/net/bridge/br_forward.c152
-rw-r--r--br-nf-bds/linux/net/bridge/br_input.c168
-rw-r--r--br-nf-bds/linux/net/bridge/br_netfilter.c567
-rw-r--r--br-nf-bds/linux/net/bridge/br_private.h212
-rw-r--r--br-nf-bds/linux/net/ipv4/ip_output.c1016
-rw-r--r--br-nf-bds/linux/net/ipv4/netfilter/ip_tables.c1811
-rw-r--r--br-nf-bds/linux/net/ipv4/netfilter/ipt_LOG.c363
-rw-r--r--br-nf-bds/patches/bridge-nf-0.0.7-bds-against-2.4.18.diff975
-rw-r--r--br-nf-bds/patches/bridge-nf-0.0.8-bds-against-2.4.18.diff983
-rw-r--r--kernel/README6
-rw-r--r--kernel/linux/include/linux/br_db.h53
-rw-r--r--kernel/linux/include/linux/if_bridge.h114
-rw-r--r--kernel/linux/include/linux/netfilter_bridge.h36
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_arp.h26
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_ip.h24
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_log.h17
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_nat.h13
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_redirect.h11
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h18
-rw-r--r--kernel/linux/include/linux/netfilter_bridge/ebtables.h332
-rw-r--r--kernel/linux/net/Config.in98
-rw-r--r--kernel/linux/net/Makefile63
-rw-r--r--kernel/linux/net/bridge/br_input.c178
-rw-r--r--kernel/linux/net/bridge/br_private.h212
-rw-r--r--kernel/linux/net/bridge/netfilter/Config.in15
-rw-r--r--kernel/linux/net/bridge/netfilter/Makefile25
-rw-r--r--kernel/linux/net/bridge/netfilter/br_db.c357
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_arp.c107
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_ip.c81
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_log.c111
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_nat.c106
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_redirect.c65
-rw-r--r--kernel/linux/net/bridge/netfilter/ebt_vlan.c124
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_broute.c80
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_filter.c93
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtable_nat.c156
-rw-r--r--kernel/linux/net/bridge/netfilter/ebtables.c1189
-rw-r--r--kernel/linux/net/netsyms.c595
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff2621
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff2752
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff3108
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff3122
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff3133
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff3135
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff3285
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff11
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff166
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff66
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff367
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff252
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff22
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff14
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff51
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff12
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff172
-rw-r--r--userspace/ebtables2/COPYING339
-rw-r--r--userspace/ebtables2/ChangeLog51
-rw-r--r--userspace/ebtables2/INSTALL27
-rw-r--r--userspace/ebtables2/Makefile58
-rw-r--r--userspace/ebtables2/THANKS9
-rw-r--r--userspace/ebtables2/communication.c454
-rw-r--r--userspace/ebtables2/ebtables.8434
-rw-r--r--userspace/ebtables2/ebtables.c1655
-rw-r--r--userspace/ebtables2/ethertypes34
-rw-r--r--userspace/ebtables2/extensions/Makefile12
-rw-r--r--userspace/ebtables2/extensions/ebt_arp.c289
-rw-r--r--userspace/ebtables2/extensions/ebt_ip.c318
-rw-r--r--userspace/ebtables2/extensions/ebt_log.c197
-rw-r--r--userspace/ebtables2/extensions/ebt_nat.c222
-rw-r--r--userspace/ebtables2/extensions/ebt_redirect.c109
-rw-r--r--userspace/ebtables2/extensions/ebt_standard.c70
-rw-r--r--userspace/ebtables2/extensions/ebt_vlan.c231
-rw-r--r--userspace/ebtables2/extensions/ebtable_broute.c25
-rw-r--r--userspace/ebtables2/extensions/ebtable_filter.c32
-rw-r--r--userspace/ebtables2/extensions/ebtable_nat.c32
-rw-r--r--userspace/ebtables2/include/ebtables_u.h206
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre2.001.diff121
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre2.002.diff2204
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre2.003.diff40
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre2.004.diff50
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre3.001.diff245
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre3.002.diff194
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre3.003.diff66
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre3.004.diff483
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre4.001.diff522
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre5.001.diff50
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre5.002.diff61
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre6.001.diff314
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre1.tar.gzbin0 -> 32529 bytes
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre2.tar.gzbin0 -> 49417 bytes
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre3.tar.gzbin0 -> 51235 bytes
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre4.tar.gzbin0 -> 52135 bytes
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre5.tar.gzbin0 -> 35558 bytes
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre6.tar.gzbin0 -> 36908 bytes
101 files changed, 43629 insertions, 0 deletions
diff --git a/br-nf-bds/README b/br-nf-bds/README
new file mode 100644
index 0000000..235524d
--- /dev/null
+++ b/br-nf-bds/README
@@ -0,0 +1,11 @@
+---
+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
+---
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..eb1adc6
--- /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..257b586
--- /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)
+{
+ 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)
+{
+ 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) 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)
+{
+ 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())
+ 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/Config.in b/br-nf-bds/linux/net/Config.in
new file mode 100644
index 0000000..946679f
--- /dev/null
+++ b/br-nf-bds/linux/net/Config.in
@@ -0,0 +1,95 @@
+#
+# 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
+ fi
+
+ dep_tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_8021Q $CONFIG_EXPERIMENTAL
+
+fi
+
+comment ' '
+tristate 'The IPX protocol' CONFIG_IPX
+if [ "$CONFIG_IPX" != "n" ]; then
+ source net/ipx/Config.in
+fi
+tristate 'Appletalk protocol support' CONFIG_ATALK
+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_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
+ 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
+ fi
+ if [ "$CONFIG_ECONET" != "n" ]; then
+ bool ' AUN over UDP' CONFIG_ECONET_AUNUDP
+ bool ' Native Econet' CONFIG_ECONET_NATIVE
+ 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/br-nf-bds/linux/net/bridge/Makefile b/br-nf-bds/linux/net/bridge/Makefile
new file mode 100644
index 0000000..25c2314
--- /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...
+
+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)
+
+obj-$(CONFIG_BRIDGE_NF) += br_netfilter.o
+
+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..2ef8028
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br.c
@@ -0,0 +1,89 @@
+/*
+ * Generic parts
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br.c,v 1.1 2002/06/01 19:23: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.
+ */
+
+#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
+
+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_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)
+ 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)
+{
+#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);
+#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+ br_fdb_get_hook = NULL;
+ br_fdb_put_hook = NULL;
+#endif
+}
+
+EXPORT_NO_SYMBOLS;
+
+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..05d1587
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_forward.c
@@ -0,0 +1,152 @@
+/*
+ * Forwarding decision
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_forward.c,v 1.1 2002/06/01 19:23: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.
+ */
+
+#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)
+{
+ struct net_device *indev;
+
+ indev = skb->dev;
+ skb->dev = to->dev;
+
+ NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, indev, 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..b9487dc
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_input.c
@@ -0,0 +1,168 @@
+/*
+ * Handle incoming frames
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_input.c,v 1.1 2002/06/01 19:23: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.
+ */
+
+#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)
+{
+ 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;
+}
+
+void 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) {
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+ return;
+ }
+
+err:
+ read_unlock(&br->lock);
+err_nolock:
+ kfree_skb(skb);
+ return;
+
+handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+ return;
+ }
+
+ kfree_skb(skb);
+}
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..6f0981d
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_netfilter.c
@@ -0,0 +1,567 @@
+/*
+ * 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.1 2002/06/01 19:23: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.
+ *
+ * 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/linux/net/bridge/br_private.h b/br-nf-bds/linux/net/bridge/br_private.h
new file mode 100644
index 0000000..155afc9
--- /dev/null
+++ b/br-nf-bds/linux/net/bridge/br_private.h
@@ -0,0 +1,212 @@
+/*
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_private.h,v 1.1 2002/06/01 19:23:55 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 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);
+
+/* 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 void 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/ipv4/ip_output.c b/br-nf-bds/linux/net/ipv4/ip_output.c
new file mode 100644
index 0000000..c8d6fc4
--- /dev/null
+++ b/br-nf-bds/linux/net/ipv4/ip_output.c
@@ -0,0 +1,1016 @@
+/*
+ * 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.1 2002/06/01 19:23:49 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;
+ iph->frag_off = 0;
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ iph->frag_off |= htons(IP_DF);
+ 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;
+
+ if (ip_dont_fragment(sk, &rt->u.dst))
+ iph->frag_off |= __constant_htons(IP_DF);
+
+ 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.
+ */
+ iph->frag_off |= __constant_htons(IP_DF);
+ NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));
+
+ 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);
+ 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.
+ */
+
+ skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, flags&MSG_DONTWAIT, &err);
+ 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);
+ 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..a840f66
--- /dev/null
+++ b/br-nf-bds/linux/net/ipv4/netfilter/ip_tables.c
@@ -0,0 +1,1811 @@
+/*
+ * 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/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] __attribute__((aligned(SMP_CACHE_BYTES)));
+};
+
+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;
+ unsigned long 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/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-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/kernel/README b/kernel/README
new file mode 100644
index 0000000..617a201
--- /dev/null
+++ b/kernel/README
@@ -0,0 +1,6 @@
+Here are the source code and patches for the kernel files ebtables
+changes/creates.
+
+Bart De Schuymer,
+June 1, 2002
+
diff --git a/kernel/linux/include/linux/br_db.h b/kernel/linux/include/linux/br_db.h
new file mode 100644
index 0000000..fae1279
--- /dev/null
+++ b/kernel/linux/include/linux/br_db.h
@@ -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/linux/include/linux/if_bridge.h b/kernel/linux/include/linux/if_bridge.h
new file mode 100644
index 0000000..926b45f
--- /dev/null
+++ b/kernel/linux/include/linux/if_bridge.h
@@ -0,0 +1,114 @@
+/*
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: if_bridge.h,v 1.1 2002/06/01 19:24:08 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);
+#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
diff --git a/kernel/linux/include/linux/netfilter_bridge.h b/kernel/linux/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..2c950c2
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge.h
@@ -0,0 +1,36 @@
+#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_BRIDGED = -100,
+ NF_BR_PRI_NAT_SRC_OTHER = 300,
+ NF_BR_PRI_LAST = INT_MAX,
+};
+
+#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..a29f926
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_arp.h
@@ -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
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..f4f9ed1
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_ip.h
@@ -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
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..9343d11
--- /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
+{
+ __u8 loglevel;
+ __u8 prefix[EBT_LOG_PREFIX_SIZE];
+ __u32 bitmask;
+};
+
+#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..53c81d2
--- /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 or EBT_CONTINUE
+ __u8 target;
+};
+#define EBT_SNAT_TARGET "snat"
+#define EBT_DNAT_TARGET "dnat"
+
+#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..82cd309
--- /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
+ __u8 target;
+};
+#define EBT_REDIRECT_TARGET "redirect"
+
+#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..079112b
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebt_vlan.h
@@ -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 --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..f4f9e90
--- /dev/null
+++ b/kernel/linux/include/linux/netfilter_bridge/ebtables.h
@@ -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
diff --git a/kernel/linux/net/Config.in b/kernel/linux/net/Config.in
new file mode 100644
index 0000000..76b32b4
--- /dev/null
+++ b/kernel/linux/net/Config.in
@@ -0,0 +1,98 @@
+#
+# 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
+ fi
+
+ dep_tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_8021Q $CONFIG_EXPERIMENTAL
+
+fi
+
+comment ' '
+tristate 'The IPX protocol' CONFIG_IPX
+if [ "$CONFIG_IPX" != "n" ]; then
+ source net/ipx/Config.in
+fi
+tristate 'Appletalk protocol support' CONFIG_ATALK
+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
+ 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
+ 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
+ fi
+ if [ "$CONFIG_ECONET" != "n" ]; then
+ bool ' AUN over UDP' CONFIG_ECONET_AUNUDP
+ bool ' Native Econet' CONFIG_ECONET_NATIVE
+ 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/linux/net/Makefile b/kernel/linux/net/Makefile
new file mode 100644
index 0000000..25d02b2
--- /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 := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
+ bluetooth atm netlink sched
+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/br_input.c b/kernel/linux/net/bridge/br_input.c
new file mode 100644
index 0000000..0a5d3eb
--- /dev/null
+++ b/kernel/linux/net/bridge/br_input.c
@@ -0,0 +1,178 @@
+/*
+ * Handle incoming frames
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_input.c,v 1.1 2002/06/01 19:24: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/etherdevice.h>
+#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)
+{
+ 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 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 0;
+ }
+
+err:
+ read_unlock(&br->lock);
+err_nolock:
+ kfree_skb(skb);
+ return 0;
+
+handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+ return 0;
+ }
+
+ 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..5e33c9b
--- /dev/null
+++ b/kernel/linux/net/bridge/br_private.h
@@ -0,0 +1,212 @@
+/*
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_private.h,v 1.1 2002/06/01 19:24: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.
+ */
+
+#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 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);
+
+/* 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/kernel/linux/net/bridge/netfilter/Config.in b/kernel/linux/net/bridge/netfilter/Config.in
new file mode 100644
index 0000000..69c176d
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/Config.in
@@ -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
+
diff --git a/kernel/linux/net/bridge/netfilter/Makefile b/kernel/linux/net/bridge/netfilter/Makefile
new file mode 100644
index 0000000..12eefaa
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/Makefile
@@ -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
diff --git a/kernel/linux/net/bridge/netfilter/br_db.c b/kernel/linux/net/bridge/netfilter/br_db.c
new file mode 100644
index 0000000..ad63647
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/br_db.c
@@ -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);
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..44c65c4
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_arp.c
@@ -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");
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..c34b1b5
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_ip.c
@@ -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");
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..e7f0506
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_log.c
@@ -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");
diff --git a/kernel/linux/net/bridge/netfilter/ebt_nat.c b/kernel/linux/net/bridge/netfilter/ebt_nat.c
new file mode 100644
index 0000000..16694c5
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_nat.c
@@ -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");
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..c26d57b
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_redirect.c
@@ -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");
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..8ad921d
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebt_vlan.c
@@ -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/linux/net/bridge/netfilter/ebtable_broute.c b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
new file mode 100644
index 0000000..ce880a2
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_broute.c
@@ -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");
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..e16f696
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_filter.c
@@ -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");
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..b99db09
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtable_nat.c
@@ -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");
diff --git a/kernel/linux/net/bridge/netfilter/ebtables.c b/kernel/linux/net/bridge/netfilter/ebtables.c
new file mode 100644
index 0000000..3f8d550
--- /dev/null
+++ b/kernel/linux/net/bridge/netfilter/ebtables.c
@@ -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");
diff --git a/kernel/linux/net/netsyms.c b/kernel/linux/net/netsyms.c
new file mode 100644
index 0000000..14fa1bd
--- /dev/null
+++ b/kernel/linux/net/netsyms.c
@@ -0,0 +1,595 @@
+/*
+ * linux/net/netsyms.c
+ *
+ * Symbol table for the linux networking subsystem. Moved here to
+ * make life simpler in ksyms.c.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/trdevice.h>
+#include <linux/fcdevice.h>
+#include <linux/ioport.h>
+#include <linux/tty.h>
+#include <net/neighbour.h>
+#include <net/snmp.h>
+#include <net/dst.h>
+#include <net/checksum.h>
+#include <linux/etherdevice.h>
+#include <net/route.h>
+#ifdef CONFIG_HIPPI
+#include <linux/hippidevice.h>
+#endif
+#include <net/pkt_sched.h>
+#include <net/scm.h>
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
+#include <linux/random.h>
+#ifdef CONFIG_NET_DIVERT
+#include <linux/divert.h>
+#endif /* CONFIG_NET_DIVERT */
+
+#ifdef CONFIG_NET
+extern __u32 sysctl_wmem_max;
+extern __u32 sysctl_rmem_max;
+#endif
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <net/icmp.h>
+#include <net/inet_common.h>
+#include <linux/inet.h>
+#include <linux/mroute.h>
+#include <linux/igmp.h>
+
+extern struct net_proto_family inet_family_ops;
+
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/transp_v6.h>
+#include <net/addrconf.h>
+
+extern int sysctl_local_port_range[2];
+extern int tcp_port_rover;
+extern int udp_port_rover;
+#endif
+
+#endif
+
+extern int netdev_finish_unregister(struct net_device *dev);
+
+#include <linux/rtnetlink.h>
+
+#ifdef CONFIG_IPX_MODULE
+extern struct datalink_proto *make_EII_client(void);
+extern struct datalink_proto *make_8023_client(void);
+extern void destroy_EII_client(struct datalink_proto *);
+extern void destroy_8023_client(struct datalink_proto *);
+#endif
+
+#ifdef CONFIG_ATALK_MODULE
+#include <net/sock.h>
+#endif
+
+#ifdef CONFIG_SYSCTL
+extern int sysctl_max_syn_backlog;
+#endif
+
+/* Skbuff symbols. */
+EXPORT_SYMBOL(skb_over_panic);
+EXPORT_SYMBOL(skb_under_panic);
+
+/* Socket layer registration */
+EXPORT_SYMBOL(sock_register);
+EXPORT_SYMBOL(sock_unregister);
+
+/* Socket locking */
+EXPORT_SYMBOL(__lock_sock);
+EXPORT_SYMBOL(__release_sock);
+
+/* Socket layer support routines */
+EXPORT_SYMBOL(memcpy_fromiovec);
+EXPORT_SYMBOL(memcpy_tokerneliovec);
+EXPORT_SYMBOL(sock_create);
+EXPORT_SYMBOL(sock_alloc);
+EXPORT_SYMBOL(sock_release);
+EXPORT_SYMBOL(sock_setsockopt);
+EXPORT_SYMBOL(sock_getsockopt);
+EXPORT_SYMBOL(sock_sendmsg);
+EXPORT_SYMBOL(sock_recvmsg);
+EXPORT_SYMBOL(sk_alloc);
+EXPORT_SYMBOL(sk_free);
+EXPORT_SYMBOL(sock_wake_async);
+EXPORT_SYMBOL(sock_alloc_send_skb);
+EXPORT_SYMBOL(sock_alloc_send_pskb);
+EXPORT_SYMBOL(sock_init_data);
+EXPORT_SYMBOL(sock_no_release);
+EXPORT_SYMBOL(sock_no_bind);
+EXPORT_SYMBOL(sock_no_connect);
+EXPORT_SYMBOL(sock_no_socketpair);
+EXPORT_SYMBOL(sock_no_accept);
+EXPORT_SYMBOL(sock_no_getname);
+EXPORT_SYMBOL(sock_no_poll);
+EXPORT_SYMBOL(sock_no_ioctl);
+EXPORT_SYMBOL(sock_no_listen);
+EXPORT_SYMBOL(sock_no_shutdown);
+EXPORT_SYMBOL(sock_no_getsockopt);
+EXPORT_SYMBOL(sock_no_setsockopt);
+EXPORT_SYMBOL(sock_no_sendmsg);
+EXPORT_SYMBOL(sock_no_recvmsg);
+EXPORT_SYMBOL(sock_no_mmap);
+EXPORT_SYMBOL(sock_no_sendpage);
+EXPORT_SYMBOL(sock_rfree);
+EXPORT_SYMBOL(sock_wfree);
+EXPORT_SYMBOL(sock_wmalloc);
+EXPORT_SYMBOL(sock_rmalloc);
+EXPORT_SYMBOL(skb_linearize);
+EXPORT_SYMBOL(skb_checksum);
+EXPORT_SYMBOL(skb_checksum_help);
+EXPORT_SYMBOL(skb_recv_datagram);
+EXPORT_SYMBOL(skb_free_datagram);
+EXPORT_SYMBOL(skb_copy_datagram);
+EXPORT_SYMBOL(skb_copy_datagram_iovec);
+EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec);
+EXPORT_SYMBOL(skb_copy_bits);
+EXPORT_SYMBOL(skb_copy_and_csum_bits);
+EXPORT_SYMBOL(skb_copy_and_csum_dev);
+EXPORT_SYMBOL(skb_copy_expand);
+EXPORT_SYMBOL(___pskb_trim);
+EXPORT_SYMBOL(__pskb_pull_tail);
+EXPORT_SYMBOL(pskb_expand_head);
+EXPORT_SYMBOL(pskb_copy);
+EXPORT_SYMBOL(skb_realloc_headroom);
+EXPORT_SYMBOL(datagram_poll);
+EXPORT_SYMBOL(put_cmsg);
+EXPORT_SYMBOL(sock_kmalloc);
+EXPORT_SYMBOL(sock_kfree_s);
+
+#ifdef CONFIG_FILTER
+EXPORT_SYMBOL(sk_run_filter);
+EXPORT_SYMBOL(sk_chk_filter);
+#endif
+
+EXPORT_SYMBOL(neigh_table_init);
+EXPORT_SYMBOL(neigh_table_clear);
+EXPORT_SYMBOL(neigh_resolve_output);
+EXPORT_SYMBOL(neigh_connected_output);
+EXPORT_SYMBOL(neigh_update);
+EXPORT_SYMBOL(neigh_create);
+EXPORT_SYMBOL(neigh_lookup);
+EXPORT_SYMBOL(__neigh_event_send);
+EXPORT_SYMBOL(neigh_event_ns);
+EXPORT_SYMBOL(neigh_ifdown);
+#ifdef CONFIG_ARPD
+EXPORT_SYMBOL(neigh_app_ns);
+#endif
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(neigh_sysctl_register);
+#endif
+EXPORT_SYMBOL(pneigh_lookup);
+EXPORT_SYMBOL(pneigh_enqueue);
+EXPORT_SYMBOL(neigh_destroy);
+EXPORT_SYMBOL(neigh_parms_alloc);
+EXPORT_SYMBOL(neigh_parms_release);
+EXPORT_SYMBOL(neigh_rand_reach_time);
+EXPORT_SYMBOL(neigh_compat_output);
+
+/* dst_entry */
+EXPORT_SYMBOL(dst_alloc);
+EXPORT_SYMBOL(__dst_free);
+EXPORT_SYMBOL(dst_destroy);
+
+/* misc. support routines */
+EXPORT_SYMBOL(net_ratelimit);
+EXPORT_SYMBOL(net_random);
+EXPORT_SYMBOL(net_srandom);
+
+/* Needed by smbfs.o */
+EXPORT_SYMBOL(__scm_destroy);
+EXPORT_SYMBOL(__scm_send);
+
+/* Needed by unix.o */
+EXPORT_SYMBOL(scm_fp_dup);
+EXPORT_SYMBOL(files_stat);
+EXPORT_SYMBOL(memcpy_toiovec);
+
+#ifdef CONFIG_IPX_MODULE
+EXPORT_SYMBOL(make_8023_client);
+EXPORT_SYMBOL(destroy_8023_client);
+EXPORT_SYMBOL(make_EII_client);
+EXPORT_SYMBOL(destroy_EII_client);
+#endif
+
+/* for 801q VLAN support */
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+EXPORT_SYMBOL(dev_change_flags);
+EXPORT_SYMBOL(vlan_ioctl_hook);
+#endif
+
+EXPORT_SYMBOL(sklist_destroy_socket);
+EXPORT_SYMBOL(sklist_insert_socket);
+
+EXPORT_SYMBOL(scm_detach_fds);
+
+#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
+#endif
+
+#ifdef CONFIG_NET_DIVERT
+EXPORT_SYMBOL(alloc_divert_blk);
+EXPORT_SYMBOL(free_divert_blk);
+EXPORT_SYMBOL(divert_ioctl);
+#endif /* CONFIG_NET_DIVERT */
+
+#ifdef CONFIG_INET
+/* Internet layer registration */
+EXPORT_SYMBOL(inetdev_lock);
+EXPORT_SYMBOL(inet_add_protocol);
+EXPORT_SYMBOL(inet_del_protocol);
+EXPORT_SYMBOL(inet_register_protosw);
+EXPORT_SYMBOL(inet_unregister_protosw);
+EXPORT_SYMBOL(ip_route_output_key);
+EXPORT_SYMBOL(ip_route_input);
+EXPORT_SYMBOL(icmp_send);
+EXPORT_SYMBOL(ip_options_compile);
+EXPORT_SYMBOL(ip_options_undo);
+EXPORT_SYMBOL(arp_send);
+EXPORT_SYMBOL(arp_broken_ops);
+EXPORT_SYMBOL(__ip_select_ident);
+EXPORT_SYMBOL(ip_send_check);
+EXPORT_SYMBOL(ip_fragment);
+EXPORT_SYMBOL(inet_family_ops);
+EXPORT_SYMBOL(in_aton);
+EXPORT_SYMBOL(ip_mc_inc_group);
+EXPORT_SYMBOL(ip_mc_dec_group);
+EXPORT_SYMBOL(ip_finish_output);
+EXPORT_SYMBOL(inet_stream_ops);
+EXPORT_SYMBOL(inet_dgram_ops);
+EXPORT_SYMBOL(ip_cmsg_recv);
+EXPORT_SYMBOL(inet_addr_type);
+EXPORT_SYMBOL(inet_select_addr);
+EXPORT_SYMBOL(ip_dev_find);
+EXPORT_SYMBOL(inetdev_by_index);
+EXPORT_SYMBOL(in_dev_finish_destroy);
+EXPORT_SYMBOL(ip_defrag);
+
+/* Route manipulation */
+EXPORT_SYMBOL(ip_rt_ioctl);
+EXPORT_SYMBOL(devinet_ioctl);
+EXPORT_SYMBOL(register_inetaddr_notifier);
+EXPORT_SYMBOL(unregister_inetaddr_notifier);
+
+/* needed for ip_gre -cw */
+EXPORT_SYMBOL(ip_statistics);
+
+#ifdef CONFIG_DLCI_MODULE
+extern int (*dlci_ioctl_hook)(unsigned int, void *);
+EXPORT_SYMBOL(dlci_ioctl_hook);
+#endif
+
+
+#ifdef CONFIG_IPV6
+EXPORT_SYMBOL(ipv6_addr_type);
+EXPORT_SYMBOL(icmpv6_send);
+EXPORT_SYMBOL(ndisc_mc_map);
+EXPORT_SYMBOL(register_inet6addr_notifier);
+EXPORT_SYMBOL(unregister_inet6addr_notifier);
+#include <net/ip6_route.h>
+EXPORT_SYMBOL(ip6_route_output);
+#endif
+#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
+/* inet functions common to v4 and v6 */
+EXPORT_SYMBOL(inet_release);
+EXPORT_SYMBOL(inet_stream_connect);
+EXPORT_SYMBOL(inet_dgram_connect);
+EXPORT_SYMBOL(inet_accept);
+EXPORT_SYMBOL(inet_listen);
+EXPORT_SYMBOL(inet_shutdown);
+EXPORT_SYMBOL(inet_setsockopt);
+EXPORT_SYMBOL(inet_getsockopt);
+EXPORT_SYMBOL(inet_sendmsg);
+EXPORT_SYMBOL(inet_recvmsg);
+#ifdef INET_REFCNT_DEBUG
+EXPORT_SYMBOL(inet_sock_nr);
+#endif
+EXPORT_SYMBOL(inet_sock_destruct);
+EXPORT_SYMBOL(inet_sock_release);
+
+/* Socket demultiplexing. */
+EXPORT_SYMBOL(tcp_hashinfo);
+EXPORT_SYMBOL(tcp_listen_wlock);
+EXPORT_SYMBOL(udp_hash);
+EXPORT_SYMBOL(udp_hash_lock);
+
+EXPORT_SYMBOL(tcp_destroy_sock);
+EXPORT_SYMBOL(ip_queue_xmit);
+EXPORT_SYMBOL(memcpy_fromiovecend);
+EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
+EXPORT_SYMBOL(tcp_v4_lookup_listener);
+/* UDP/TCP exported functions for TCPv6 */
+EXPORT_SYMBOL(udp_ioctl);
+EXPORT_SYMBOL(udp_connect);
+EXPORT_SYMBOL(udp_disconnect);
+EXPORT_SYMBOL(udp_sendmsg);
+EXPORT_SYMBOL(tcp_close);
+EXPORT_SYMBOL(tcp_disconnect);
+EXPORT_SYMBOL(tcp_accept);
+EXPORT_SYMBOL(tcp_write_wakeup);
+EXPORT_SYMBOL(tcp_write_space);
+EXPORT_SYMBOL(tcp_poll);
+EXPORT_SYMBOL(tcp_ioctl);
+EXPORT_SYMBOL(tcp_shutdown);
+EXPORT_SYMBOL(tcp_setsockopt);
+EXPORT_SYMBOL(tcp_getsockopt);
+EXPORT_SYMBOL(tcp_recvmsg);
+EXPORT_SYMBOL(tcp_send_synack);
+EXPORT_SYMBOL(tcp_check_req);
+EXPORT_SYMBOL(tcp_child_process);
+EXPORT_SYMBOL(tcp_parse_options);
+EXPORT_SYMBOL(tcp_rcv_established);
+EXPORT_SYMBOL(tcp_init_xmit_timers);
+EXPORT_SYMBOL(tcp_clear_xmit_timers);
+EXPORT_SYMBOL(tcp_statistics);
+EXPORT_SYMBOL(tcp_rcv_state_process);
+EXPORT_SYMBOL(tcp_timewait_state_process);
+EXPORT_SYMBOL(tcp_timewait_cachep);
+EXPORT_SYMBOL(tcp_timewait_kill);
+EXPORT_SYMBOL(tcp_sendmsg);
+EXPORT_SYMBOL(tcp_v4_rebuild_header);
+EXPORT_SYMBOL(tcp_v4_send_check);
+EXPORT_SYMBOL(tcp_v4_conn_request);
+EXPORT_SYMBOL(tcp_create_openreq_child);
+EXPORT_SYMBOL(tcp_bucket_create);
+EXPORT_SYMBOL(__tcp_put_port);
+EXPORT_SYMBOL(tcp_put_port);
+EXPORT_SYMBOL(tcp_inherit_port);
+EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
+EXPORT_SYMBOL(tcp_v4_do_rcv);
+EXPORT_SYMBOL(tcp_v4_connect);
+EXPORT_SYMBOL(tcp_v4_hash_connecting);
+EXPORT_SYMBOL(tcp_unhash);
+EXPORT_SYMBOL(udp_prot);
+EXPORT_SYMBOL(tcp_prot);
+EXPORT_SYMBOL(tcp_openreq_cachep);
+EXPORT_SYMBOL(ipv4_specific);
+EXPORT_SYMBOL(tcp_simple_retransmit);
+EXPORT_SYMBOL(tcp_transmit_skb);
+EXPORT_SYMBOL(tcp_connect);
+EXPORT_SYMBOL(tcp_make_synack);
+EXPORT_SYMBOL(tcp_tw_deschedule);
+EXPORT_SYMBOL(tcp_delete_keepalive_timer);
+EXPORT_SYMBOL(tcp_reset_keepalive_timer);
+EXPORT_SYMBOL(sysctl_local_port_range);
+EXPORT_SYMBOL(tcp_port_rover);
+EXPORT_SYMBOL(udp_port_rover);
+EXPORT_SYMBOL(tcp_sync_mss);
+EXPORT_SYMBOL(net_statistics);
+EXPORT_SYMBOL(__tcp_mem_reclaim);
+EXPORT_SYMBOL(tcp_sockets_allocated);
+EXPORT_SYMBOL(sysctl_tcp_reordering);
+EXPORT_SYMBOL(sysctl_tcp_rmem);
+EXPORT_SYMBOL(sysctl_tcp_wmem);
+EXPORT_SYMBOL(sysctl_tcp_ecn);
+EXPORT_SYMBOL(tcp_cwnd_application_limited);
+EXPORT_SYMBOL(tcp_sendpage);
+
+EXPORT_SYMBOL(tcp_write_xmit);
+
+EXPORT_SYMBOL(tcp_v4_remember_stamp);
+
+extern int sysctl_tcp_tw_recycle;
+
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(sysctl_tcp_tw_recycle);
+EXPORT_SYMBOL(sysctl_max_syn_backlog);
+#endif
+
+#if defined (CONFIG_IPV6_MODULE)
+EXPORT_SYMBOL(secure_tcpv6_sequence_number);
+EXPORT_SYMBOL(secure_ipv6_id);
+#endif
+
+#endif
+
+EXPORT_SYMBOL(netlink_set_err);
+EXPORT_SYMBOL(netlink_broadcast);
+EXPORT_SYMBOL(netlink_unicast);
+EXPORT_SYMBOL(netlink_kernel_create);
+EXPORT_SYMBOL(netlink_dump_start);
+EXPORT_SYMBOL(netlink_ack);
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+EXPORT_SYMBOL(netlink_attach);
+EXPORT_SYMBOL(netlink_detach);
+EXPORT_SYMBOL(netlink_post);
+#endif
+
+EXPORT_SYMBOL(rtattr_parse);
+EXPORT_SYMBOL(rtnetlink_links);
+EXPORT_SYMBOL(__rta_fill);
+EXPORT_SYMBOL(rtnetlink_dump_ifinfo);
+EXPORT_SYMBOL(rtnetlink_put_metrics);
+EXPORT_SYMBOL(rtnl);
+EXPORT_SYMBOL(neigh_delete);
+EXPORT_SYMBOL(neigh_add);
+EXPORT_SYMBOL(neigh_dump_info);
+
+EXPORT_SYMBOL(dev_set_allmulti);
+EXPORT_SYMBOL(dev_set_promiscuity);
+EXPORT_SYMBOL(sklist_remove_socket);
+EXPORT_SYMBOL(rtnl_sem);
+EXPORT_SYMBOL(rtnl_lock);
+EXPORT_SYMBOL(rtnl_unlock);
+
+/* ABI emulation layers need this */
+EXPORT_SYMBOL(move_addr_to_kernel);
+EXPORT_SYMBOL(move_addr_to_user);
+
+/* Used by at least ipip.c. */
+EXPORT_SYMBOL(ipv4_config);
+EXPORT_SYMBOL(dev_open);
+
+/* Used by other modules */
+EXPORT_SYMBOL(in_ntoa);
+EXPORT_SYMBOL(xrlim_allow);
+
+EXPORT_SYMBOL(ip_rcv);
+EXPORT_SYMBOL(arp_rcv);
+EXPORT_SYMBOL(arp_tbl);
+EXPORT_SYMBOL(arp_find);
+
+#endif /* CONFIG_INET */
+
+#ifdef CONFIG_TR
+EXPORT_SYMBOL(tr_type_trans);
+#endif
+
+/* Device callback registration */
+EXPORT_SYMBOL(register_netdevice_notifier);
+EXPORT_SYMBOL(unregister_netdevice_notifier);
+
+/* support for loadable net drivers */
+#ifdef CONFIG_NET
+EXPORT_SYMBOL(loopback_dev);
+EXPORT_SYMBOL(register_netdevice);
+EXPORT_SYMBOL(unregister_netdevice);
+EXPORT_SYMBOL(netdev_state_change);
+EXPORT_SYMBOL(dev_new_index);
+EXPORT_SYMBOL(dev_get_by_index);
+EXPORT_SYMBOL(__dev_get_by_index);
+EXPORT_SYMBOL(dev_get_by_name);
+EXPORT_SYMBOL(__dev_get_by_name);
+EXPORT_SYMBOL(netdev_finish_unregister);
+EXPORT_SYMBOL(netdev_set_master);
+EXPORT_SYMBOL(eth_type_trans);
+#ifdef CONFIG_FDDI
+EXPORT_SYMBOL(fddi_type_trans);
+#endif /* CONFIG_FDDI */
+#if 0
+EXPORT_SYMBOL(eth_copy_and_sum);
+#endif
+EXPORT_SYMBOL(alloc_skb);
+EXPORT_SYMBOL(__kfree_skb);
+EXPORT_SYMBOL(skb_clone);
+EXPORT_SYMBOL(skb_copy);
+EXPORT_SYMBOL(netif_rx);
+EXPORT_SYMBOL(dev_add_pack);
+EXPORT_SYMBOL(dev_remove_pack);
+EXPORT_SYMBOL(dev_get);
+EXPORT_SYMBOL(dev_alloc);
+EXPORT_SYMBOL(dev_alloc_name);
+EXPORT_SYMBOL(__netdev_watchdog_up);
+#ifdef CONFIG_KMOD
+EXPORT_SYMBOL(dev_load);
+#endif
+EXPORT_SYMBOL(dev_ioctl);
+EXPORT_SYMBOL(dev_queue_xmit);
+#ifdef CONFIG_NET_HW_FLOWCONTROL
+EXPORT_SYMBOL(netdev_dropping);
+EXPORT_SYMBOL(netdev_register_fc);
+EXPORT_SYMBOL(netdev_unregister_fc);
+EXPORT_SYMBOL(netdev_fc_xoff);
+#endif
+EXPORT_SYMBOL(dev_base);
+EXPORT_SYMBOL(dev_base_lock);
+EXPORT_SYMBOL(dev_close);
+EXPORT_SYMBOL(dev_mc_add);
+EXPORT_SYMBOL(dev_mc_delete);
+EXPORT_SYMBOL(dev_mc_upload);
+EXPORT_SYMBOL(__kill_fasync);
+
+EXPORT_SYMBOL(if_port_text);
+
+#ifdef CONFIG_HIPPI
+EXPORT_SYMBOL(hippi_type_trans);
+#endif
+
+#ifdef CONFIG_NET_FASTROUTE
+EXPORT_SYMBOL(netdev_fastroute);
+#endif
+
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(sysctl_wmem_max);
+EXPORT_SYMBOL(sysctl_rmem_max);
+#ifdef CONFIG_INET
+EXPORT_SYMBOL(sysctl_ip_default_ttl);
+#endif
+#endif
+
+/* Packet scheduler modules want these. */
+EXPORT_SYMBOL(qdisc_destroy);
+EXPORT_SYMBOL(qdisc_reset);
+EXPORT_SYMBOL(qdisc_restart);
+EXPORT_SYMBOL(qdisc_create_dflt);
+EXPORT_SYMBOL(noop_qdisc);
+EXPORT_SYMBOL(qdisc_tree_lock);
+#ifdef CONFIG_NET_SCHED
+PSCHED_EXPORTLIST;
+EXPORT_SYMBOL(pfifo_qdisc_ops);
+EXPORT_SYMBOL(register_qdisc);
+EXPORT_SYMBOL(unregister_qdisc);
+EXPORT_SYMBOL(qdisc_get_rtab);
+EXPORT_SYMBOL(qdisc_put_rtab);
+EXPORT_SYMBOL(qdisc_copy_stats);
+#ifdef CONFIG_NET_ESTIMATOR
+EXPORT_SYMBOL(qdisc_new_estimator);
+EXPORT_SYMBOL(qdisc_kill_estimator);
+#endif
+#ifdef CONFIG_NET_CLS_POLICE
+EXPORT_SYMBOL(tcf_police);
+EXPORT_SYMBOL(tcf_police_locate);
+EXPORT_SYMBOL(tcf_police_destroy);
+EXPORT_SYMBOL(tcf_police_dump);
+#endif
+#endif
+#ifdef CONFIG_NET_CLS
+EXPORT_SYMBOL(register_tcf_proto_ops);
+EXPORT_SYMBOL(unregister_tcf_proto_ops);
+#endif
+#ifdef CONFIG_NETFILTER
+#include <linux/netfilter.h>
+EXPORT_SYMBOL(nf_register_hook);
+EXPORT_SYMBOL(nf_unregister_hook);
+EXPORT_SYMBOL(nf_register_sockopt);
+EXPORT_SYMBOL(nf_unregister_sockopt);
+EXPORT_SYMBOL(nf_reinject);
+EXPORT_SYMBOL(nf_register_queue_handler);
+EXPORT_SYMBOL(nf_unregister_queue_handler);
+EXPORT_SYMBOL(nf_hook_slow);
+EXPORT_SYMBOL(nf_hooks);
+EXPORT_SYMBOL(nf_setsockopt);
+EXPORT_SYMBOL(nf_getsockopt);
+EXPORT_SYMBOL(ip_ct_attach);
+#ifdef CONFIG_INET
+#include <linux/netfilter_ipv4.h>
+EXPORT_SYMBOL(ip_route_me_harder);
+#endif
+#endif
+
+EXPORT_SYMBOL(register_gifconf);
+
+EXPORT_SYMBOL(net_call_rx_atomic);
+EXPORT_SYMBOL(softnet_data);
+
+#endif /* CONFIG_NET */
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/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/userspace/ebtables2/COPYING b/userspace/ebtables2/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/userspace/ebtables2/COPYING
@@ -0,0 +1,339 @@
+ 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..3a58b07
--- /dev/null
+++ b/userspace/ebtables2/ChangeLog
@@ -0,0 +1,51 @@
+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..dc17499
--- /dev/null
+++ b/userspace/ebtables2/INSTALL
@@ -0,0 +1,27 @@
+FOLLOW THESE SIMPLE GUIDELINES:
+-------------------------------
+
+If /usr/src/linux contains the patched kernel for ebtables:
+ %make install
+Otherwise:
+ %make KERNEL_DIR=<<where-the-patched-kernel-is>> install
+
+optional:
+If your /usr/include/linux directory is a symbolic link, use the SYMLINK=y
+option. When unsure, don't use this. If the cp program produces errors
+when executing make install, use this option.
+
+WHAT GETS INSTALLED?
+--------------------
+
+- The needed kernel headers are placed in /usr/include/linux/netfilter_bridge/
+ That's why it needs the KERNEL_DIR. If the SYMLINK=y option is supplied,
+ /usr/include/linux will be a symbolic link.
+- 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 placed in /etc/
+- the userspace program ebtables is compiled.
+
+That's all
diff --git a/userspace/ebtables2/Makefile b/userspace/ebtables2/Makefile
new file mode 100644
index 0000000..c16801e
--- /dev/null
+++ b/userspace/ebtables2/Makefile
@@ -0,0 +1,58 @@
+# ebtables Makefile
+
+KERNEL_DIR?=/usr/src/linux
+PROGNAME:=ebtables
+PROGVERSION:="2.0pre6 (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
+
+all: ebtables
+
+.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/br_db.h \
+ /usr/include/linux/br_db.h
+ 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 $@ $<
+
+ebtables.o: ebtables.c include/ebtables_u.h
+ $(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \
+ -DPROGNAME=\"$(PROGNAME)\" -c -o $@ $<
+
+ebtables: ebtables.o communication.o $(EXT_OBJS)
+ $(CC) $(CFLAGS) -o $@ $^
+
+$(MANDIR)/man8/ebtables.8: ebtables.8
+ mkdir -p $(@D)
+ install -m 0644 -o root -g root $< $@
+
+/etc/ethertypes: ethertypes
+ mkdir -p $(@D)
+ install -m 0644 -o root -g root $< $@
+
+install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
+ ebtables /etc/ethertypes
+
+clean:
+ -rm -f ebtables
+ rm -f *.o *.c~
+ rm -f extensions/*.o extensions/*.c~
diff --git a/userspace/ebtables2/THANKS b/userspace/ebtables2/THANKS
new file mode 100644
index 0000000..830ad5d
--- /dev/null
+++ b/userspace/ebtables2/THANKS
@@ -0,0 +1,9 @@
+Thanks go out to:
+
+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..02aff3f
--- /dev/null
+++ b/userspace/ebtables2/communication.c
@@ -0,0 +1,454 @@
+/*
+ * communication.c, v2.0 April 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, lots of linked lists.
+// So, the translation is done here.
+
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h> // the database
+#include <netinet/in.h> // IPPROTO_IP
+#include <asm/types.h>
+#include "include/ebtables_u.h"
+
+extern char* hooknames[NF_BR_NUMHOOKS];
+
+int sockfd = -1;
+
+void get_sockfd()
+{
+ if (sockfd == -1) {
+ sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+ if (sockfd < 0)
+ print_error("Problem getting a socket");
+ }
+}
+
+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;
+ char *p, *base;
+ int i, j;
+ unsigned int entries_size = 0;
+
+ new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+ if (!new)
+ print_memory();
+ new->valid_hooks = u_repl->valid_hooks;
+ memcpy(new->name, u_repl->name, sizeof(new->name));
+ 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 size
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (!(new->valid_hooks & (1 << i)))
+ continue;
+ entries_size += sizeof(struct ebt_entries);
+ j = 0;
+ e = u_repl->hook_entry[i]->entries;
+ while (e) {
+ 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 != u_repl->hook_entry[i]->nentries)
+ print_bug("Wrong nentries: %d != %d, hook = %s", j,
+ u_repl->hook_entry[i]->nentries, hooknames[i]);
+ }
+
+ new->entries_size = entries_size;
+ new->entries = (char *)malloc(entries_size);
+ if (!new->entries)
+ print_memory();
+
+ // put everything in one block
+ p = new->entries;
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ 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;
+ hlp->distinguisher = 0; // make the kernel see the light
+ p += sizeof(struct ebt_entries);
+ e = u_repl->hook_entry[i]->entries;
+ while (e) {
+ struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+ 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));
+ 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));
+ p += e->t->target_size +
+ sizeof(struct ebt_entry_target);
+ tmp->next_offset = p - base;
+ e = e->next;
+ }
+ }
+
+ // sanity check
+ if (p - new->entries != new->entries_size)
+ print_bug("Entries_size bug");
+ return new;
+}
+
+void 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);
+ get_sockfd();
+ // 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");
+}
+
+// gets executed after deliver_table
+void
+deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
+{
+ unsigned short *point;
+ struct ebt_counter *old, *new, *newcounters;
+ socklen_t optlen;
+ struct ebt_replace repl;
+
+ if (u_repl->nentries == 0)
+ return;
+
+ 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));
+ old = u_repl->counters;
+ new = newcounters;
+ point = counterchanges;
+ while (*point != CNT_END) {
+ if (*point == CNT_NORM) {
+ // 'normal' rule, meaning we didn't do anything to it
+ // So, we just copy
+ new->pcnt = old->pcnt;
+ // we've used an old counter
+ old++;
+ // we've set a new counter
+ new++;
+ } 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;
+ old++;
+ new++;
+ }
+ point++;
+ }
+
+ 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);
+ // 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));
+
+ get_sockfd();
+ if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+ 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;
+
+ 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 + sizeof(struct ebt_entry_match));
+ if (!new->m)
+ print_memory();
+ memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+ new->next = NULL;
+ **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);
+ return 0;
+}
+
+static int
+ebt_translate_watcher(struct ebt_entry_watcher *w,
+ struct ebt_u_watcher_list ***l)
+{
+ struct ebt_u_watcher_list *new;
+
+ 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 + sizeof(struct ebt_entry_watcher));
+ if (!new->w)
+ print_memory();
+ memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+ new->next = NULL;
+ **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);
+ 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)
+{
+ // 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)
+ 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;
+ 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->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;
+ 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)
+ 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 +
+ sizeof(struct ebt_entry_target));
+
+ // I love pointers
+ **u_e = new;
+ *u_e = &new->next;
+ (*cnt)++;
+ (*totalcnt)++;
+ return 0;
+ } else { // a new chain
+ int i;
+ struct ebt_entries *entries = (struct ebt_entries *)e;
+ struct ebt_u_entries *new;
+
+ 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();
+ new->nentries = entries->nentries;
+ new->policy = entries->policy;
+ new->entries = NULL;
+ u_repl->hook_entry[*hook] = new;
+ *u_e = &new->entries;
+ return 0;
+ }
+}
+
+// talk with kernel to receive the kernel's table
+void get_table(struct ebt_u_replace *u_repl)
+{
+ int i, j, k, hook;
+ socklen_t optlen;
+ struct ebt_replace repl;
+ struct ebt_u_entry **u_e;
+
+ get_sockfd();
+
+ 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);
+
+ 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))) )
+ print_memory();
+ }
+ else
+ repl.counters = NULL;
+
+ // we want to receive the 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))
+ print_bug("hmm, what is wrong??? bug#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;
+ u_repl->counters = repl.counters;
+ 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);
+ if (k != u_repl->nentries)
+ print_bug("Wrong total nentries");
+}
+
+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");
+}
diff --git a/userspace/ebtables2/ebtables.8 b/userspace/ebtables2/ebtables.8
new file mode 100644
index 0000000..d0d7a18
--- /dev/null
+++ b/userspace/ebtables2/ebtables.8
@@ -0,0 +1,434 @@
+.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.
+.\"
+.\" 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.2.0) \- ethernet bridge packet table administration
+.SH SYNOPSIS
+.BR "ebtables -[ADI] " "chain rule-specification [options]"
+.br
+.BR "ebtables -P " "chain target"
+.br
+.BR "ebtables -[FLZ] [" "chain" "]"
+.br
+.B "ebtables -L DB"
+.br
+.BR "ebtables -[b] [" "y/n" "]"
+.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.
+
+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 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:
+.IR ACCEPT ,
+.IR DROP ,
+.IR CONTINUE ,
+an extention.
+.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. For the
+other targets see the
+.B "TARGET EXTENSIONS"
+section.
+.SS TABLES
+There are three tables.
+.TP
+.B "-t, --table"
+This option specifies the frame matching table which the command should
+operate on. The tables are:
+.BR filter ,
+this is the default table and contains three chains:
+.B INPUT
+(for frames destined for the bridge itself),
+.B OUTPUT
+(for locally-generated frames) and
+.B FORWARD
+(for frames being bridged).
+.BR nat ,
+this table is used to change the mac addresses and contains three chains:
+.B PREROUTING
+(for altering frames as soon as they come in),
+.B OUTPUT
+(for altering locally generated 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 POSTROUTING and PREROUTING: it would be more accurate to call them
+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
+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
+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.
+.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 versions
+of this command. A rule number (starting at 1) or the complete rule can be
+specified.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number (1 meaning
+the head of the chain).
+.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.
+.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 "-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 is either
+.B ACCEPT
+, either
+.BR DROP .
+.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" .
+.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
+.B 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 via which a frame is received (for the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains). 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 (for the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains).
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface via which a frame is going to be sent (for the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+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[/\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[/\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 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
+.BR "-b --db " "[\fIy/n\fP]"
+.IR "" "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" .
+.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.
+.SS ip
+Specify ip specific fields. These will only work if the protocol equals
+.BR 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.
+.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 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.
+.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.
+.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.
+.TP
+.B "--log"
+.br
+Use this if you won't specify any other log options, so if you want to use the default
+settings: log-prefix="", no arp logging, no ip logging, log-level=info.
+.TP
+.B --log-level "\fIlevel\fP"
+.br
+defines the logging level. For the possible values: ebtables -h log.
+The default level is
+.IR info .
+.TP
+.BR --log-prefix " \fItext\fP"
+.br
+defines the prefix to be printed before 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-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 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.
+.TP
+.B 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.
+.br
+.BR "--to-destination " "\fIaddress\fP"
+.br
+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
+.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
+there.
+.TP
+.B 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."
+.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
+.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/ethertypes
+.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 >
+.SH SEE ALSO
+.BR iptables "(8), " brctl (8)
diff --git a/userspace/ebtables2/ebtables.c b/userspace/ebtables2/ebtables.c
new file mode 100644
index 0000000..e28fd96
--- /dev/null
+++ b/userspace/ebtables2/ebtables.c
@@ -0,0 +1,1655 @@
+/*
+ * ebtables.c, v2.0 April 2002
+ *
+ * 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 <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 <asm/types.h>
+#include "include/ebtables_u.h"
+
+// here are the number-name correspondences kept for the ethernet
+// frame type field
+#define PROTOCOLFILE "/etc/ethertypes"
+
+#define DATABASEHOOKNR NF_BR_NUMHOOKS
+#define DATABASEHOOKNAME "DB"
+
+static char *prog_name = PROGNAME;
+static char *prog_version = PROGVERSION;
+char* 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"
+};
+
+// default command line options
+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' },
+ { "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' },
+ { "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' },
+ { 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+// yup, all the possible target names
+char* standard_targets[NUM_STANDARD_TARGETS] = {
+ "ACCEPT",
+ "DROP",
+ "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
+static struct ebt_u_replace replace;
+
+// the chosen table
+static struct ebt_u_table *table = NULL;
+// 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;
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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.
+// 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)
+{
+ 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;
+ // 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, 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;
+ 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);
+}
+
+// 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 *
+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)
+ 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)
+ 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;
+}
+
+void register_match(struct ebt_u_match *m)
+{
+ 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 = m->size;
+ ebt_options = merge_options
+ (ebt_options, m->extra_ops, &(m->option_offset));
+ m->init(m->m);
+
+ for (i = &matches; *i; i = &((*i)->next));
+ m->next = NULL;
+ *i = m;
+}
+
+void register_watcher(struct ebt_u_watcher *w)
+{
+ 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 = w->size;
+ ebt_options = merge_options
+ (ebt_options, w->extra_ops, &(w->option_offset));
+ w->init(w->w);
+
+ for (i = &watchers; *i; i = &((*i)->next));
+ w->next = NULL;
+ *i = w;
+}
+
+void register_target(struct ebt_u_target *t)
+{
+ 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 = t->size;
+ 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;
+}
+
+void register_table(struct ebt_u_table *t)
+{
+ t->next = tables;
+ tables = t;
+}
+
+// used to parse /etc/etherproto
+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/etherproto
+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/etherproto
+int get_a_line(char *buffer, char *value, FILE *ifp)
+{
+ int i, hlp;
+ char anotherhlp;
+
+ /* discard comment lines && 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;
+ }
+ 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;
+}
+
+// helper function for list_em()
+int number_to_name(unsigned short proto, char *name)
+{
+ FILE *ifp;
+ char buffer[21], value[5], *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;
+ }
+}
+
+// helper function for list_rules()
+static void list_em(int hooknr)
+{
+ 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;
+ 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);
+
+ i = replace.hook_entry[hooknr]->nentries;
+ while (i >9) {
+ space++;
+ i /= 10;
+ }
+
+ for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
+ digits = 0;
+ // A little work to get nice rule numbers.
+ while (j > 9) {
+ digits++;
+ j /= 10;
+ }
+ 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.
+ if (!(hlp->bitmask & EBT_NOPROTO)) {
+ printf("eth proto: ");
+ if (hlp->invflags & EBT_IPROTO)
+ printf("! ");
+ if (hlp->bitmask & EBT_802_3)
+ printf("Length, ");
+ else {
+ if (number_to_name(ntohs(hlp->ethproto), name))
+ printf("0x%x, ", ntohs(hlp->ethproto));
+ else
+ printf("%s, ", name);
+ }
+ }
+ 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) ? "" : ":");
+ 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) ? "" : ":");
+ 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)
+ 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("! ");
+ printf("out-if: %s, ", hlp->out);
+ }
+
+ m_l = hlp->m_list;
+ while (m_l) {
+ m = find_match(m_l->m->u.name);
+ if (!m)
+ 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 = find_watcher(w_l->w->u.name);
+ if (!w)
+ print_bug("Watcher not found");
+ w->print(hlp, w_l->w);
+ w_l = w_l->next;
+ }
+
+ printf("target: ");
+ 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);
+ printf("\n");
+ hlp = hlp->next;
+ }
+}
+
+// parse the chain name and return the corresponding nr
+int get_hooknr(char* arg)
+{
+ int i;
+
+ // 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]))
+ return i;
+ return -1;
+}
+
+// yup, print out help
+void print_help()
+{
+ struct ebt_u_match_list *m_l;
+ 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[/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"
+"--version -V : print package version\n"
+"\n" ,
+ prog_name,
+ prog_version);
+
+ 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(hooknames);
+ exit(0);
+}
+
+// execute command L
+static void list_rules()
+{
+ 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;
+}
+
+// execute command P
+static void change_policy(int policy)
+{
+ int i;
+
+ // 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;
+ replace.num_counters = replace.nentries;
+ if (replace.nentries) {
+ // '+ 1' for the CNT_END
+ 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++)
+ counterchanges[i] = CNT_NORM;
+ counterchanges[replace.nentries] = CNT_END;
+ }
+ else
+ counterchanges = NULL;
+ }
+ else
+ exit(0);
+}
+
+// flush one chain or the complete table
+static void flush_chains()
+{
+ int i, j, oldnentries;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *tmp;
+
+ // flush whole table
+ if (replace.selected_hook == -1) {
+ if (replace.nentries == 0)
+ exit(0);
+ 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;
+ 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;
+ }
+
+ 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;
+
+ // 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)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ if (i != replace.selected_hook)
+ *cnt = CNT_NORM;
+ else
+ *cnt = CNT_DEL;
+ cnt++;
+ }
+ }
+ *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;
+ 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;
+}
+
+// -1 == no match
+static int check_rule_exists(int rule_nr)
+{
+ 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;
+ 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;
+ // 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) {
+ 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) {
+ if (new_entry->bitmask & EBT_SOURCEMAC &&
+ strcmp(u_e->sourcemac, new_entry->sourcemac))
+ continue;
+ 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)
+ 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;
+}
+
+// execute command A
+static void add_rule(int rule_nr)
+{
+ int i, j;
+ struct ebt_u_entry *u_e, *u_e2;
+ unsigned short *cnt;
+ struct ebt_u_match_list *m_l;
+ 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);
+ } else
+ rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
+ // we're adding one rule
+ replace.num_counters = replace.nentries;
+ replace.nentries++;
+ replace.hook_entry[replace.selected_hook]->nentries++;
+
+ // handle counter stuff
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)
+ malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < replace.selected_hook; i++) {
+ if (!(replace.valid_hooks & (1 << i)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
+ for (i = 0; i < rule_nr; i++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_ADD;
+ cnt++;
+ while (cnt != counterchanges + replace.nentries) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+ u_e = replace.hook_entry[replace.selected_hook]->entries;
+ for (i = 0; i < rule_nr; i++) {
+ u_e2 = u_e;
+ u_e = u_e->next;
+ }
+ // insert the rule
+ if (u_e2)
+ u_e2->next = new_entry;
+ else
+ replace.hook_entry[replace.selected_hook]->entries = new_entry;
+ new_entry->next = u_e;
+
+ // 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;
+}
+
+// execute command D
+static void delete_rule(int rule_nr)
+{
+ int i, j, lentmp = 0;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *u_e2;
+
+ if ( (i = check_rule_exists(rule_nr)) == -1 )
+ print_error("Sorry, rule does not exists");
+
+ // we're deleting a rule
+ replace.num_counters = replace.nentries;
+ replace.nentries--;
+
+ if (replace.nentries) {
+ for (j = 0; j < replace.selected_hook; j++) {
+ if (!(replace.valid_hooks & (1 << j)))
+ continue;
+ lentmp += replace.hook_entry[j]->nentries;
+ }
+ lentmp += i;
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)malloc(
+ (replace.num_counters + 1) * sizeof(unsigned short))) )
+ print_memory();
+ cnt = counterchanges;
+ for (j = 0; j < lentmp; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_DEL;
+ cnt++;
+ for (j = 0; j < replace.num_counters - lentmp; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+ }
+ else
+ replace.num_counters = 0;
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+ u_e = replace.hook_entry[replace.selected_hook]->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
+ replace.hook_entry[replace.selected_hook]->entries = u_e->next;
+
+ replace.hook_entry[replace.selected_hook]->nentries--;
+ // free everything
+ free_u_entry(u_e);
+ free(u_e);
+}
+
+// execute command Z
+void zero_counters(int zerochain)
+{
+
+ 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
+ counterchanges = NULL;
+ replace.num_counters = 0;
+ } else {
+ int i, j;
+ unsigned short *cnt;
+
+ if (replace.hook_entry[zerochain]->nentries == 0)
+ exit(0);
+ 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)))
+ continue;
+ for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
+ for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
+ *cnt = CNT_ZERO;
+ cnt++;
+ }
+ while (cnt != counterchanges + replace.nentries) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ *cnt = CNT_END;
+ }
+}
+
+// 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);
+}
+
+// set ethproto
+int name_to_protocol(char *name)
+{
+ FILE *ifp;
+ char buffer[21], value[5], *bfr;
+ unsigned short i;
+
+ if (!strcasecmp("LENGTH", name)) {
+ new_entry->ethproto = 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;
+ i = (unsigned short) strtol(value, &bfr, 16);
+ if (*bfr != '\0')
+ return -1;
+ new_entry->ethproto = i;
+ fclose(ifp);
+ return 0;
+ }
+ return -1;
+}
+
+// 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;
+
+ 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) {
+ optind++;
+ return 1;
+ }
+ return 0;
+}
+
+void check_option(unsigned int *flags, unsigned int mask)
+{
+ if (*flags & mask)
+ print_error("Multiple use of same option not allowed");
+ *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_LOGICALIN 0x200
+#define OPT_LOGICALOUT 0x400
+// the main thing
+int main(int argc, char *argv[])
+{
+ char *buffer, allowbc = 'n';
+ int c, i;
+ // 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, selected hook and command
+ strcpy(replace.name, "filter");
+ replace.flags = 0;
+ replace.selected_hook = -1;
+ replace.command = 'h';
+
+ 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
+ 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) {
+ switch (c) {
+
+ case 'A': // add a rule
+ case 'D': // delete a rule
+ case 'P': // define policy
+ case 'I': // insert a rule
+ replace.command = c;
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.flags |= OPT_COMMAND;
+ if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+ 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");
+ optind++;
+ }
+ if (c == 'P') {
+ if (optind >= argc)
+ print_error("No policy specified");
+ for (i = 0; i < 2; i++)
+ if (!strcmp(argv[optind],
+ standard_targets[i])) {
+ policy = i;
+ break;
+ }
+ if (policy == -1)
+ 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
+ case 'F': // flush
+ 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");
+ replace.flags |= OPT_ZERO;
+ } else {
+ replace.command = c;
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands"
+ " not allowed");
+ replace.flags |= OPT_COMMAND;
+ }
+ 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)
+ print_error("Bad chain");
+ optind++;
+ }
+ if (i != -1) {
+ if (c == 'Z')
+ zerochain = i;
+ else
+ replace.selected_hook = i;
+ }
+ break;
+
+ case 'V': // version
+ replace.command = 'V';
+ if (replace.flags & OPT_COMMAND)
+ 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");
+ 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])))
+ 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]);
+ if (replace.flags & OPT_JUMP)
+ 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;
+ }
+ optind++;
+ }
+ break;
+
+ case 't': // table
+ check_option(&replace.flags, OPT_TABLE);
+ if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
+ print_error("Table name too long");
+ 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
+ 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");
+ if (c == 'i') {
+ check_option(&replace.flags, OPT_IN);
+ if (replace.selected_hook > 2 &&
+ replace.selected_hook < NF_BR_BROUTING)
+ print_error("Use in-interface only in "
+ "INPUT, FORWARD, PREROUTING and"
+ "BROUTING chains");
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IIN;
+
+ if (optind > argc)
+ print_error("No in-interface "
+ "specified");
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interfacelength");
+ strcpy(new_entry->in, argv[optind - 1]);
+ break;
+ }
+ if (c == 2) {
+ check_option(&replace.flags, OPT_LOGICALIN);
+ if (replace.selected_hook > 2 &&
+ replace.selected_hook < NF_BR_BROUTING)
+ print_error("Use logical in-interface "
+ "only in INPUT, FORWARD, "
+ "PREROUTING and BROUTING 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)
+ 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("No out-interface "
+ "specified");
+
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+ print_error("Illegal interface "
+ "length");
+ strcpy(new_entry->out, argv[optind - 1]);
+ break;
+ }
+ if (c == 3) {
+ 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') {
+
+ 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;
+ break;
+ }
+ // must be an extension then
+ if (i == NUM_STANDARD_TARGETS) {
+ struct ebt_u_target *t;
+ t = find_target(optarg);
+ // -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') {
+ check_option(&replace.flags, OPT_SOURCE);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_ISOURCE;
+
+ if (optind > argc)
+ print_error("No source mac "
+ "specified");
+ 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;
+ break;
+ }
+ if (c == 'd') {
+ check_option(&replace.flags, OPT_DEST);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IDEST;
+
+ if (optind > argc)
+ print_error("No destination mac "
+ "specified");
+ 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;
+ break;
+ }
+ check_option(&replace.flags, OPT_PROTOCOL);
+ if (check_inverse(optarg))
+ new_entry->invflags |= EBT_IPROTO;
+
+ if (optind > argc)
+ 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");
+ 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)");
+ break;
+
+ case 'b': // allow database?
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.command = c;
+ allowbc = *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,
+ new_entry, &t->flags, &t->t)))
+ continue;
+
+ // 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 (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);
+ }
+ }
+
+ // 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 )
+ 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");
+ }
+
+ 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;
+ }
+ 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') {
+ list_rules();
+ if (replace.flags & OPT_ZERO)
+ zero_counters(zerochain);
+ else
+ exit(0);
+ }
+ if (replace.flags & OPT_ZERO)
+ zero_counters(zerochain);
+ else if (replace.command == 'F')
+ flush_chains();
+ else if (replace.command == 'A' || replace.command == 'I')
+ add_rule(rule_nr);
+ else if (replace.command == 'D')
+ delete_rule(rule_nr);
+
+ if (table->check)
+ table->check(&replace);
+
+ deliver_table(&replace);
+
+ if (counterchanges)
+ deliver_counters(&replace, counterchanges);
+ return 0;
+}
diff --git a/userspace/ebtables2/ethertypes b/userspace/ebtables2/ethertypes
new file mode 100644
index 0000000..0123bf3
--- /dev/null
+++ b/userspace/ebtables2/ethertypes
@@ -0,0 +1,34 @@
+# 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
+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
+
+
diff --git a/userspace/ebtables2/extensions/Makefile b/userspace/ebtables2/extensions/Makefile
new file mode 100644
index 0000000..109af35
--- /dev/null
+++ b/userspace/ebtables2/extensions/Makefile
@@ -0,0 +1,12 @@
+#! /usr/bin/make
+
+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)
+
+extensions/ebt_%.o: extensions/ebt_%.c include/ebtables_u.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+extensions/ebtable_%.o: extensions/ebtable_%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
diff --git a/userspace/ebtables2/extensions/ebt_arp.c b/userspace/ebtables2/extensions/ebt_arp.c
new file mode 100644
index 0000000..0e22b0b
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_arp.c
@@ -0,0 +1,289 @@
+#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_arp.h>
+
+#define ARP_OPCODE '1'
+#define ARP_HTYPE '2'
+#define ARP_PTYPE '3'
+#define ARP_IP_S '4'
+#define ARP_IP_D '5'
+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 },
+ { 0 }
+};
+
+// 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 = 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");
+ 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");
+}
+
+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;
+}
+
+// defined in ebt_ip.c
+void parse_ip_address(char *address, __u32 *addr, __u32 *msk);
+
+#define OPT_OPCODE 0x01
+#define OPT_HTYPE 0x02
+#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)
+{
+ struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+ int i;
+ char *end;
+ __u32 *addr;
+ __u32 *mask;
+
+ 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");
+ i = strtol(argv[optind - 1], &end, 10);
+ if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+ i = 0;
+ while (strcmp(opcodes[i], "")) {
+ if (!strcasecmp(opcodes[i], optarg))
+ break;
+ i++;
+ }
+ if (!strcmp(opcodes[i], ""))
+ print_error("Problem with specified "
+ "arp opcode");
+ }
+ arpinfo->opcode = htons(i);
+ arpinfo->bitmask |= EBT_ARP_OPCODE;
+ break;
+
+ case ARP_HTYPE:
+ check_option(flags, OPT_HTYPE);
+ if (check_inverse(optarg))
+ arpinfo->invflags |= EBT_ARP_HTYPE;
+
+ if (optind > argc)
+ 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 "
+ "hardware type");
+ }
+ arpinfo->htype = htons(i);
+ arpinfo->bitmask |= EBT_ARP_HTYPE;
+ break;
+
+ case ARP_PTYPE:
+ check_option(flags, OPT_PTYPE);
+ if (check_inverse(optarg))
+ arpinfo->invflags |= EBT_ARP_PTYPE;
+
+ if (optind > argc)
+ 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 "
+ "protocol type");
+ }
+ arpinfo->ptype = htons(i);
+ arpinfo->bitmask |= EBT_ARP_PTYPE;
+ break;
+
+ case ARP_IP_S:
+ case ARP_IP_D:
+ if (c == ARP_IP_S) {
+ check_option(flags, OPT_IP_S);
+ addr = &arpinfo->saddr;
+ mask = &arpinfo->smsk;
+ arpinfo->bitmask |= EBT_ARP_SRC_IP;
+ } else {
+ check_option(flags, OPT_IP_D);
+ addr = &arpinfo->daddr;
+ mask = &arpinfo->dmsk;
+ arpinfo->bitmask |= EBT_ARP_DST_IP;
+ }
+ if (check_inverse(optarg)) {
+ if (c == ARP_IP_S)
+ arpinfo->invflags |= EBT_ARP_SRC_IP;
+ else
+ arpinfo->invflags |= EBT_ARP_DST_IP;
+ }
+ if (optind > argc)
+ print_error("Missing ip address argument");
+ parse_ip_address(argv[optind - 1], addr, mask);
+ 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)
+{
+ 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");
+}
+
+// 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;
+
+ if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+ printf("arp opcode: ");
+ if (arpinfo->invflags & EBT_ARP_OPCODE)
+ printf("! ");
+ printf("%d ", ntohs(arpinfo->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) {
+ 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 ");
+ 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));
+ }
+ if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+ printf("arp dst IP ");
+ 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));
+ }
+}
+
+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;
+ }
+ return 1;
+}
+
+static struct ebt_u_match arp_match =
+{
+ EBT_ARP_MATCH,
+ sizeof(struct ebt_arp_info),
+ print_help,
+ init,
+ parse,
+ final_check,
+ print,
+ compare,
+ opts,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_match(&arp_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_ip.c b/userspace/ebtables2/extensions/ebt_ip.c
new file mode 100644
index 0000000..5d62d3a
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_ip.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <stdlib.h>
+#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>
+
+#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'
+
+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 },
+ { 0 }
+};
+
+// put the ip string into 4 bytes
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+ char *p, *q, *end;
+ int onebyte, 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;
+ int bits;
+ __u32 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
+void parse_ip_address(char *address, __u32 *addr, __u32 *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");
+ }
+ else
+ *msk = 0xFFFFFFFF;
+
+ i = undot_ip(address, (unsigned char *)addr);
+ if (i)
+ 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)
+{
+ int i;
+ static char buf[20];
+ __u32 maskaddr, bits;
+
+ maskaddr = ntohl(mask);
+
+ // don't print /32
+ if (mask == 0xFFFFFFFFL)
+ return "";
+
+ 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;
+}
+
+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");
+}
+
+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
+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;
+ int i;
+
+ switch (c) {
+ case IP_SOURCE:
+ check_option(flags, OPT_SOURCE);
+ ipinfo->bitmask |= EBT_IP_SOURCE;
+
+ case IP_DEST:
+ if (c == IP_DEST) {
+ check_option(flags, OPT_DEST);
+ ipinfo->bitmask |= EBT_IP_DEST;
+ }
+ if (check_inverse(optarg)) {
+ if (c == IP_SOURCE)
+ ipinfo->invflags |= EBT_IP_SOURCE;
+ else
+ ipinfo->invflags |= EBT_IP_DEST;
+ }
+
+ if (optind > argc)
+ print_error("Missing ip address argument");
+ if (c == IP_SOURCE)
+ parse_ip_address(argv[optind - 1], &ipinfo->saddr,
+ &ipinfo->smsk);
+ else
+ 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");
+ i = strtol(argv[optind - 1], &end, 16);
+ if (i < 0 || i > 255 || *buffer != '\0')
+ 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");
+ i = strtol(argv[optind - 1], &end, 10);
+ if (i < 0 || i > 255 || *end != '\0')
+ print_error("Problem with specified ip protocol");
+ ipinfo->protocol = 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 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");
+}
+
+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("source ip: ");
+ 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));
+ }
+ if (ipinfo->bitmask & EBT_IP_DEST) {
+ printf("dest ip: ");
+ 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));
+ }
+ 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) {
+ printf("ip proto: ");
+ if (ipinfo->invflags & EBT_IP_DEST)
+ printf("! ");
+ printf("%d, ", ipinfo->protocol);
+ }
+}
+
+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;
+ }
+ return 1;
+}
+
+static struct ebt_u_match ip_match =
+{
+ EBT_IP_MATCH,
+ sizeof(struct ebt_ip_info),
+ print_help,
+ init,
+ parse,
+ final_check,
+ print,
+ compare,
+ opts,
+};
+
+static void _init(void) __attribute((constructor));
+static void _init(void)
+{
+ register_match(&ip_match);
+}
diff --git a/userspace/ebtables2/extensions/ebt_log.c b/userspace/ebtables2/extensions/ebt_log.c
new file mode 100644
index 0000000..6dff952
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_log.c
@@ -0,0 +1,197 @@
+#include <stdio.h>
+#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>
+
+// 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 },
+ { NULL, -1 }
+};
+
+static int name_to_loglevel(char* arg)
+{
+ int i = 0, c_val = eight_priority[0].c_val;
+
+ while (c_val != -1) {
+ if (!strcmp(arg, eight_priority[i].c_name))
+ return c_val;
+ i++;
+ c_val = 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'
+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 },
+ { 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"
+ , 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
+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;
+ char *end;
+
+ switch (c) {
+ case LOG_PREFIX:
+ check_option(flags, OPT_PREFIX);
+ if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+ print_error("Prefix too long");
+ strcpy(loginfo->prefix, optarg);
+ break;
+
+ case LOG_LEVEL:
+ check_option(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)
+ 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;
+ 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 hook)
+{
+ return;
+}
+
+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\"",
+ 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");
+ 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(loginfo1->prefix, loginfo2->prefix);
+}
+
+static struct ebt_u_watcher log_watcher =
+{
+ EBT_LOG_WATCHER,
+ sizeof(struct ebt_log_info),
+ print_help,
+ init,
+ parse,
+ final_check,
+ print,
+ compare,
+ opts,
+};
+
+#undef _init
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_watcher(&log_watcher);
+}
diff --git a/userspace/ebtables2/extensions/ebt_nat.c b/userspace/ebtables2/extensions/ebt_nat.c
new file mode 100644
index 0000000..dbdb5a4
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_nat.c
@@ -0,0 +1,222 @@
+#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_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 },
+ { "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 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 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_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) {
+ case NAT_S:
+ check_option(flags, OPT_SNAT);
+ to_source_supplied = 1;
+ 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 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)
+{
+ int i;
+ struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+
+ switch (c) {
+ case NAT_D:
+ check_option(flags, OPT_DNAT);
+ to_dest_supplied = 1;
+ if (getmac(optarg, natinfo->mac))
+ 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 1;
+}
+
+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");
+ 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")) &&
+ (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
+ print_error("Wrong chain for dnat");
+ if (to_dest_supplied == 0)
+ 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;
+ int i;
+
+ printf("snat - to: ");
+ 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,
+ 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(" --dnat-target %s", standard_targets[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 =
+{
+ EBT_SNAT_TARGET,
+ sizeof(struct ebt_nat_info),
+ print_help_s,
+ init_s,
+ parse_s,
+ final_check_s,
+ print_s,
+ compare,
+ opts_s,
+};
+
+static struct ebt_u_target dnat_target =
+{
+ EBT_DNAT_TARGET,
+ sizeof(struct ebt_nat_info),
+ print_help_d,
+ init_d,
+ parse_d,
+ final_check_d,
+ print_d,
+ compare,
+ opts_d,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_target(&snat_target);
+ register_target(&dnat_target);
+}
diff --git a/userspace/ebtables2/extensions/ebt_redirect.c b/userspace/ebtables2/extensions/ebt_redirect.c
new file mode 100644
index 0000000..3dff790
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_redirect.c
@@ -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);
+}
diff --git a/userspace/ebtables2/extensions/ebt_standard.c b/userspace/ebtables2/extensions/ebt_standard.c
new file mode 100644
index 0000000..983d055
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_standard.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+
+static struct option opts[] =
+{
+ {0}
+};
+
+static void print_help()
+{
+ printf("Standard targets: DROP, ACCEPT and CONTINUE\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 hook)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+ const struct ebt_entry_target *target)
+{
+ __u8 verdict = ((struct ebt_standard_target *)target)->verdict;
+
+ if (verdict == EBT_CONTINUE)
+ printf("Continue ");
+ else if (verdict == EBT_ACCEPT)
+ printf("Accept ");
+ else
+ printf("Drop ");
+}
+
+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 =
+{
+ EBT_STANDARD_TARGET,
+ sizeof(struct ebt_standard_target) - sizeof(struct ebt_entry_target),
+ print_help,
+ init,
+ parse,
+ final_check,
+ print,
+ compare,
+ opts
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_target(&standard);
+}
diff --git a/userspace/ebtables2/extensions/ebt_vlan.c b/userspace/ebtables2/extensions/ebt_vlan.c
new file mode 100644
index 0000000..ad3e6f7
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebt_vlan.c
@@ -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);
+}
diff --git a/userspace/ebtables2/extensions/ebtable_broute.c b/userspace/ebtables2/extensions/ebtable_broute.c
new file mode 100644
index 0000000..2abfcb6
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_broute.c
@@ -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/ebtables2/extensions/ebtable_filter.c b/userspace/ebtables2/extensions/ebtable_filter.c
new file mode 100644
index 0000000..cf26983
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_filter.c
@@ -0,0 +1,32 @@
+#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) | \
+ (1 << NF_BR_LOCAL_OUT))
+
+static void print_help(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 =
+{
+ "filter",
+ NULL,
+ print_help,
+ NULL
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ 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..4b4ca48
--- /dev/null
+++ b/userspace/ebtables2/extensions/ebtable_nat.c
@@ -0,0 +1,32 @@
+#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) | \
+ (1 << NF_BR_POST_ROUTING))
+
+static void print_help(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 =
+{
+ "nat",
+ NULL,
+ print_help,
+ NULL
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+ register_table(&table);
+}
diff --git a/userspace/ebtables2/include/ebtables_u.h b/userspace/ebtables2/include/ebtables_u.h
new file mode 100644
index 0000000..d31186c
--- /dev/null
+++ b/userspace/ebtables2/include/ebtables_u.h
@@ -0,0 +1,206 @@
+/*
+ * $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 <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h>
+
+struct ebt_u_entries
+{
+ __u8 policy;
+ __u32 nentries;
+ struct ebt_u_entry *entries;
+};
+
+
+struct ebt_u_replace
+{
+ char name[EBT_TABLE_MAXNAMELEN];
+ unsigned int valid_hooks;
+ // 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];
+ // 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);
+ void (*help)(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
+{
+ __u32 bitmask;
+ __u32 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];
+ struct ebt_u_match_list *m_list;
+ struct ebt_u_watcher_list *w_list;
+ struct ebt_entry_target *t;
+ struct ebt_u_entry *next;
+};
+
+struct ebt_u_match
+{
+ char name[EBT_FUNCTION_MAXNAMELEN];
+ // 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);
+ 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
+ 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 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;
+ 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 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;
+ struct ebt_entry_target *t;
+ unsigned int used;
+ struct ebt_u_target *next;
+};
+
+void register_table(struct ebt_u_table *);
+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);
+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_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 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_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
+#define CNT_ADD 2
+#define CNT_END 3
+#define CNT_ZERO 4
+
+#endif /* EBTABLES_U_H */
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/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.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