summaryrefslogtreecommitdiffstats
path: root/kernel/patches
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-07-31 19:44:51 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-07-31 19:44:51 +0000
commitd6a5d8b6636b49b4ae54385661b434d1277c6d86 (patch)
treeddd81e65e9a91604c327eeb3653b933b94375703 /kernel/patches
parent424ce46da34e4cfdf62ae60c9a2aa646cabf898f (diff)
*** empty log message ***
Diffstat (limited to 'kernel/patches')
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff3998
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff1152
2 files changed, 5150 insertions, 0 deletions
diff --git a/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff
new file mode 100644
index 0000000..269d739
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0-rc1_vs_2.4.18.diff
@@ -0,0 +1,3998 @@
+ebtables-v2.0-rc1 - 31 July
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/br_private.h Wed Jul 31 20:42:36 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0-rc1/include/linux/if_bridge.h Wed Jul 31 20:42:36 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0-rc1/net/core/dev.c Wed Jul 31 20:42:44 2002
+@@ -1384,7 +1384,7 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1394,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1479,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/br_input.c Wed Jul 31 20:42:36 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/bridge/br.c Wed Jul 31 20:44:49 2002
++++ ebt2.0-rc1/net/bridge/br.c Wed Jul 31 20:42:44 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ MOD_DEC_USE_COUNT;
+@@ -82,7 +90,12 @@
+ #endif
+ }
+
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#else
+ EXPORT_NO_SYMBOLS;
++#endif
+
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux/net/bridge/Makefile Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/bridge/Makefile Wed Jul 31 20:42:44 2002
+@@ -7,6 +7,12 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ O_TARGET := bridge.o
+ obj-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0-rc1/include/linux/netfilter_bridge.h Wed Jul 31 20:42:44 2002
+@@ -18,7 +18,18 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0-rc1/net/Makefile Wed Jul 31 20:42:44 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($(CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Wed Jul 31 20:44:50 2002
++++ ebt2.0-rc1/net/Config.in Wed Jul 31 20:42:36 2002
+@@ -60,6 +60,9 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++ source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/Makefile Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,28 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs := ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/Config.in Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,18 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/br_db.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_filter.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,92 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, "INPUT", 0, EBT_ACCEPT, 0},
++ {0, "FORWARD", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_nat.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,100 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++ {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_broute.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,79 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, "BROUTING", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,73 @@
++/*
++ * ebt_mark_t
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++// The target member of the struct ebt_vlan_info provides the same
++// functionality as a separate table
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++ if ((*pskb)->nfmark != infostuff->mark) {
++ (*pskb)->nfmark = infostuff->mark;
++ (*pskb)->nfcache |= NFC_ALTERED;
++ }
++ return infostuff->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
++ if (datalen != sizeof(struct ebt_mark_t_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target mark_target =
++{
++ {NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++ ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark_m.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,62 @@
++/*
++ * ebt_mark_m
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++ const struct net_device *in, const struct net_device *out, const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++ if (info->bitmask & EBT_MARK_OR)
++ return !(!!(skb->nfmark & info->mask) ^ info->invert);
++ return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++ if (info->bitmask & ~EBT_MARK_MASK)
++ return -EINVAL;
++ if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++ return -EINVAL;
++ if (!info->bitmask)
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_mark_m_info)) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++ {NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_redirect.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,74 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if (hooknr != NF_BR_BROUTING)
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ else {
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ }
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
++ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_arp.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_ip.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_vlan.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,318 @@
++/*
++ * Description: EBTables 802.1Q match extension kernelspace module.
++ * Authors: Nick Fedchik <nick@fedchik.org.ua>
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/if_ether.h>
++#include <linux/if_vlan.h>
++#include <linux/module.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++
++static unsigned char debug;
++#define MODULE_VERSION "0.4 (" __DATE__ " " __TIME__ ")"
++
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q match module (ebtables extension), v"
++ MODULE_VERSION);
++MODULE_LICENSE ("GPL");
++
++
++#define DEBUG_MSG(...) if (debug) printk (KERN_DEBUG __FILE__ ":" __FUNCTION__ ": " __VA_ARGS__)
++#define INV_FLAG(_inv_flag_) (infostuff->invflags & _inv_flag_) ? "!" : ""
++#define GET_BITMASK(_BIT_MASK_) infostuff->bitmask & _BIT_MASK_
++#define SET_BITMASK(_BIT_MASK_) infostuff->bitmask |= _BIT_MASK_
++#define EXIT_ON_MISMATCH(_MATCH_,_MASK_) if (!((infostuff->_MATCH_ == _MATCH_)^!!(infostuff->invflags & _MASK_))) return 1;
++
++/*
++ * Function description: ebt_filter_vlan() is main engine for
++ * checking passed 802.1Q frame according to
++ * the passed extension parameters (in the *data buffer)
++ * ebt_filter_vlan() is called after successfull check the rule params
++ * by ebt_check_vlan() function.
++ * Parameters:
++ * const struct sk_buff *skb - pointer to passed ethernet frame buffer
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * const struct net_device *in -
++ * const struct net_device *out -
++ * const struct ebt_counter *c -
++ * Returned values:
++ * 0 - ok (all rule params matched)
++ * 1 - miss (rule params not acceptable to the parsed frame)
++ */
++static int
++ebt_filter_vlan (const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data; /* userspace data */
++ struct vlan_ethhdr *frame = (struct vlan_ethhdr *) skb->mac.raw; /* Passed tagged frame */
++
++ unsigned short TCI; /* Whole TCI, given from parsed frame */
++ unsigned short id; /* VLAN ID, given from frame TCI */
++ unsigned char prio; /* user_priority, given from frame TCI */
++ unsigned short encap; /* VLAN encapsulated Type/Length field, given from orig frame */
++
++ /*
++ * Tag Control Information (TCI) consists of the following elements:
++ * - User_priority. This field allows the tagged frame to carry user_priority
++ * information across Bridged LANs in which individual LAN segments may be unable to signal
++ * priority information (e.g., 802.3/Ethernet segments).
++ * The user_priority field is three bits in length,
++ * interpreted as a binary number. The user_priority is therefore
++ * capable of representing eight priority levels, 0 through 7.
++ * The use and interpretation of this field is defined in ISO/IEC 15802-3.
++ * - Canonical Format Indicator (CFI). This field is used,
++ * in 802.3/Ethernet, to signal the presence or absence
++ * of a RIF field, and, in combination with the Non-canonical Format Indicator (NCFI) carried
++ * in the RIF, to signal the bit order of address information carried in the encapsulated
++ * frame. The Canonical Format Indicator (CFI) is a single bit flag value.
++ * - VLAN Identifier (VID). This field uniquely identifies the VLAN to
++ * which the frame belongs. The twelve-bit VLAN Identifier (VID) field
++ * uniquely identify the VLAN to which the frame belongs.
++ * The VID is encoded as an unsigned binary number.
++ */
++ TCI = ntohs (frame->h_vlan_TCI);
++ id = TCI & 0xFFF;
++ prio = TCI >> 13;
++ encap = frame->h_vlan_encapsulated_proto;
++
++ /*
++ * First step is to check is null VLAN ID present
++ * in the parsed frame
++ */
++ if (!(id)) {
++ /*
++ * Checking VLAN Identifier (VID)
++ */
++ if (GET_BITMASK (EBT_VLAN_ID)) { /* Is VLAN ID parsed? */
++ EXIT_ON_MISMATCH (id, EBT_VLAN_ID);
++ DEBUG_MSG
++ ("matched rule id=%s%d for frame id=%d\n",
++ INV_FLAG (EBT_VLAN_ID), infostuff->id, id);
++ }
++ } else {
++ /*
++ * Checking user_priority
++ */
++ if (GET_BITMASK (EBT_VLAN_PRIO)) { /* Is VLAN user_priority parsed? */
++ EXIT_ON_MISMATCH (prio, EBT_VLAN_PRIO);
++ DEBUG_MSG
++ ("matched rule prio=%s%d for frame prio=%d\n",
++ INV_FLAG (EBT_VLAN_PRIO), infostuff->prio,
++ prio);
++ }
++ }
++ /*
++ * Checking Encapsulated Proto (Length/Type) field
++ */
++ if (GET_BITMASK (EBT_VLAN_ENCAP)) { /* Is VLAN Encap parsed? */
++ EXIT_ON_MISMATCH (encap, EBT_VLAN_ENCAP);
++ DEBUG_MSG ("matched encap=%s%2.4X for frame encap=%2.4X\n",
++ INV_FLAG (EBT_VLAN_ENCAP),
++ ntohs (infostuff->encap), ntohs (encap));
++ }
++ /*
++ * All possible extension parameters was parsed.
++ * If rule never returned by missmatch, then all ok.
++ */
++ return 0;
++}
++
++/*
++ * Function description: ebt_vlan_check() is called when userspace
++ * delivers the table to the kernel,
++ * and to check that userspace doesn't give a bad table.
++ * Parameters:
++ * const char *tablename - table name string
++ * unsigned int hooknr - hook number
++ * const struct ebt_entry *e - ebtables entry basic set
++ * const void *data - pointer to passed extension parameters
++ * unsigned int datalen - length of passed *data buffer
++ * Returned values:
++ * 0 - ok (all delivered rule params are correct)
++ * 1 - miss (rule params is out of range, invalid, incompatible, etc.)
++ */
++static int
++ebt_check_vlan (const char *tablename,
++ unsigned int hooknr,
++ const struct ebt_entry *e, void *data,
++ unsigned int datalen)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++ /*
++ * Parameters buffer overflow check
++ */
++ if (datalen != sizeof (struct ebt_vlan_info)) {
++ DEBUG_MSG
++ ("params size %d is not eq to ebt_vlan_info (%d)\n",
++ datalen, sizeof (struct ebt_vlan_info));
++ return -EINVAL;
++ }
++
++ /*
++ * Is it 802.1Q frame checked?
++ */
++ if (e->ethproto != __constant_htons (ETH_P_8021Q)) {
++ DEBUG_MSG ("passed frame %2.4X is not 802.1Q (8100)\n",
++ (unsigned short) ntohs (e->ethproto));
++ return -EINVAL;
++ }
++
++ /*
++ * Check for bitmask range
++ * True if even one bit is out of mask
++ */
++ if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++ DEBUG_MSG ("bitmask %2X is out of mask (%2X)\n",
++ infostuff->bitmask, EBT_VLAN_MASK);
++ return -EINVAL;
++ }
++
++ /*
++ * Check for inversion flags range
++ */
++ if (infostuff->invflags & ~EBT_VLAN_MASK) {
++ DEBUG_MSG ("inversion flags %2X is out of mask (%2X)\n",
++ infostuff->invflags, EBT_VLAN_MASK);
++ return -EINVAL;
++ }
++
++ /*
++ * Reserved VLAN ID (VID) values
++ * -----------------------------
++ * 0 - The null VLAN ID. Indicates that the tag header contains only user_priority information;
++ * no VLAN identifier is present in the frame. This VID value shall not be
++ * configured as a PVID, configured in any Filtering Database entry, or used in any
++ * Management operation.
++ *
++ * 1 - The default Port VID (PVID) value used for classifying frames on ingress through a Bridge
++ * Port. The PVID value can be changed by management on a per-Port basis.
++ *
++ * 0x0FFF - Reserved for implementation use. This VID value shall not be configured as a
++ * PVID or transmitted in a tag header.
++ *
++ * The remaining values of VID are available for general use as VLAN identifiers.
++ * A Bridge may implement the ability to support less than the full range of VID values;
++ * i.e., for a given implementation,
++ * an upper limit, N, is defined for the VID values supported, where N is less than or equal to 4094.
++ * All implementations shall support the use of all VID values in the range 0 through their defined maximum
++ * VID, N.
++ *
++ * For Linux, N = 4094.
++ */
++ if (GET_BITMASK (EBT_VLAN_ID)) { /* when vlan-id param was spec-ed */
++ if (!!infostuff->id) { /* if id!=0 => check vid range */
++ if (infostuff->id > 4094) { /* check if id > than (0x0FFE) */
++ DEBUG_MSG
++ ("vlan id %d is out of range (1-4094)\n",
++ infostuff->id);
++ return -EINVAL;
++ }
++ /*
++ * Note: This is valid VLAN-tagged frame point.
++ * Any value of user_priority are acceptable, but could be ignored
++ * according to 802.1Q Std.
++ */
++ } else {
++ /*
++ * if id=0 (null VLAN ID) => Check for user_priority range
++ */
++ if (GET_BITMASK (EBT_VLAN_PRIO)) {
++ if ((unsigned char) infostuff->prio > 7) {
++ DEBUG_MSG
++ ("prio %d is out of range (0-7)\n",
++ infostuff->prio);
++ return -EINVAL;
++ }
++ }
++ /*
++ * Note2: This is valid priority-tagged frame point
++ * with null VID field.
++ */
++ }
++ } else { /* VLAN Id not set */
++ if (GET_BITMASK (EBT_VLAN_PRIO)) { /* But user_priority is set - abnormal! */
++ infostuff->id = 0; /* Set null VID (case for Priority-tagged frames) */
++ SET_BITMASK (EBT_VLAN_ID); /* and set id flag */
++ }
++ }
++ /*
++ * Check for encapsulated proto range - it is possible to be any value for u_short range.
++ * When relaying a tagged frame between 802.3/Ethernet MACs,
++ * a Bridge may adjust the padding field such that
++ * the minimum size of a transmitted tagged frame is 68 octets (7.2).
++ * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS
++ */
++ if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++ if ((unsigned short) ntohs (infostuff->encap) < ETH_ZLEN) {
++ DEBUG_MSG
++ ("encap packet length %d is less than minimal %d\n",
++ ntohs (infostuff->encap), ETH_ZLEN);
++ return -EINVAL;
++ }
++ }
++
++ /*
++ * Otherwise is all correct
++ */
++ DEBUG_MSG ("802.1Q tagged frame checked (%s table, %d hook)\n",
++ tablename, hooknr);
++ return 0;
++}
++
++static struct ebt_match filter_vlan = {
++ {NULL, NULL},
++ EBT_VLAN_MATCH,
++ ebt_filter_vlan,
++ ebt_check_vlan,
++ NULL,
++ THIS_MODULE
++};
++
++/*
++ * Module initialization function.
++ * Called when module is loaded to kernelspace
++ */
++static int __init init (void)
++{
++ DEBUG_MSG ("ebtables 802.1Q extension module v"
++ MODULE_VERSION "\n");
++ DEBUG_MSG ("module debug=%d\n", !!debug);
++ return ebt_register_match (&filter_vlan);
++}
++
++/*
++ * Module "finalization" function
++ * Called when download module from kernelspace
++ */
++static void __exit fini (void)
++{
++ ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_log.c Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_snat.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,68 @@
++/*
++ * ebt_snat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_dnat.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,68 @@
++/*
++ * ebt_dnat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
++ if ( (strcmp(tablename, "nat") ||
++ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebtables.c Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,1503 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, July, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++ COUNTER_OFFSET(n) * cpu))
++
++
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return !!strncmp(entry, device->name, IFNAMSIZ);
++}
++
++#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++ const struct net_device *in, const struct net_device *out)
++{
++ int verdict, i;
++
++ if (p->bitmask & EBT_802_3) {
++ if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++ return 1;
++ } else if (!(p->bitmask & EBT_NOPROTO) &&
++ FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++ return 1;
++
++ if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++ return 1;
++ if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++ return 1;
++ if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
++ p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++ return 1;
++ if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
++ (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++ return 1;
++
++ if (p->bitmask & EBT_SOURCEMAC) {
++ verdict = 0;
++ for (i = 0; i < 6; i++)
++ verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
++ p->sourcemsk[i];
++ if (FWINV(verdict != 0, EBT_ISOURCE) )
++ return 1;
++ }
++ if (p->bitmask & EBT_DESTMAC) {
++ verdict = 0;
++ for (i = 0; i < 6; i++)
++ verdict |= (h->h_dest[i] ^ p->destmac[i]) &
++ p->destmsk[i];
++ if (FWINV(verdict != 0, EBT_IDEST) )
++ return 1;
++ }
++ return 0;
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base, *cb_base;
++ struct ebt_entry_target *t;
++ int verdict, sp = 0;
++ struct ebt_chainstack *cs;
++ struct ebt_entries *chaininfo;
++ char *base;
++ struct ebt_table_info *private = table->private;
++
++ read_lock_bh(&table->lock);
++ cb_base = COUNTER_BASE(private->counters, private->nentries, \
++ cpu_number_map(smp_processor_id()));
++ if (private->chainstack)
++ cs = private->chainstack[cpu_number_map(smp_processor_id())];
++ else
++ cs = NULL;
++ chaininfo = private->hook_entry[hook];
++ nentries = private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(private->hook_entry[hook]->data);
++ counter_base = cb_base + private->hook_entry[hook]->counter_offset;
++ // base for chain jumps
++ base = (char *)chaininfo;
++ i = 0;
++ while (i < nentries) {
++ if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++ goto letscontinue;
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict == EBT_RETURN) {
++letsreturn:
++ if (sp == 0) {
++ BUGPRINT("RETURN on base chain");
++ // act like this is EBT_CONTINUE
++ goto letscontinue;
++ }
++ sp--;
++ // put all the local variables right
++ i = cs[sp].n;
++ chaininfo = cs[sp].chaininfo;
++ nentries = chaininfo->nentries;
++ point = cs[sp].e;
++ counter_base = cb_base +
++ chaininfo->counter_offset;
++ continue;
++ }
++ if (verdict == EBT_CONTINUE)
++ goto letscontinue;
++ if (verdict < 0) {
++ BUGPRINT("bogus standard verdict\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ // jump to a udc
++ cs[sp].n = i + 1;
++ cs[sp].chaininfo = chaininfo;
++ cs[sp].e = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i = 0;
++ chaininfo = (struct ebt_entries *) (base + verdict);
++ if (chaininfo->distinguisher) {
++ BUGPRINT("jump to non-chain\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ nentries = chaininfo->nentries;
++ point = (struct ebt_entry *)chaininfo->data;
++ counter_base = cb_base + chaininfo->counter_offset;
++ sp++;
++ continue;
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i++;
++ }
++
++ // I actually like this :)
++ if (chaininfo->policy == EBT_RETURN)
++ goto letsreturn;
++ if (chaininfo->policy == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++ const char *name,
++ int *error,
++ struct semaphore *mutex)
++{
++ void *ret;
++
++ *error = down_interruptible(mutex);
++ if (*error != 0)
++ return NULL;
++
++ ret = list_named_find(head, name);
++ if (!ret) {
++ *error = -ENOENT;
++ up(mutex);
++ }
++ return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++ const char *name,
++ const char *prefix,
++ int *error,
++ struct semaphore *mutex)
++{
++ void *ret;
++
++ ret = find_inlist_lock_noload(head, name, error, mutex);
++ if (!ret) {
++ char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++ strcpy(modulename, prefix);
++ strcat(modulename, name);
++ request_module(modulename);
++ ret = find_inlist_lock_noload(head, name, error, mutex);
++ }
++ return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++ ((char *)e) + e->watchers_offset)
++ return -EINVAL;
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++ if (!match)
++ return ret;
++ m->u.match = match;
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ if (match->check &&
++ match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ if (match->me)
++ __MOD_DEC_USE_COUNT(match->me);
++ return -EINVAL;
++ }
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++ ((char *)e) + e->target_offset)
++ return -EINVAL;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++ if (!watcher)
++ return ret;
++ w->u.watcher = watcher;
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ if (watcher->check &&
++ watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ if (watcher->me)
++ __MOD_DEC_USE_COUNT(watcher->me);
++ return -EINVAL;
++ }
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ // if i == NF_BR_NUMHOOKS it must be a user defined chain
++ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ // only RETURN from udc
++ if (i != NF_BR_NUMHOOKS ||
++ ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ }
++ if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++ (*udc_cnt)++;
++ else
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++ BUGPRINT("counter_offset != totalcnt");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ // this is not checked anywhere else
++ if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++ BUGPRINT("target size too small\n");
++ return -EINVAL;
++ }
++
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++struct ebt_cl_stack
++{
++ struct ebt_chainstack cs;
++ int from;
++ unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++ struct ebt_cl_stack *udc)
++{
++ int i;
++
++ // we're only interested in chain starts
++ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++ return 0;
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++ break;
++ }
++ // only care about udc
++ if (i != NF_BR_NUMHOOKS)
++ return 0;
++
++ udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++ // these initialisations are depended on later in check_chainloops()
++ udc[*n].cs.n = 0;
++ udc[*n].hookmask = 0;
++
++ (*n)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks,
++ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0, hookmask = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ // (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++ // a base chain
++ if (i < NF_BR_NUMHOOKS)
++ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++ else {
++ for (i = 0; i < udc_cnt; i++)
++ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++ break;
++ if (i == 0)
++ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
++ else
++ hookmask = cl_s[i - 1].hookmask;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++ if (!target)
++ goto cleanup_watchers;
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict <
++ -NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if ((e->target_offset + t->target_size +
++ sizeof(struct ebt_entry_target) > e->next_offset) ||
++ (t->u.target->check &&
++ t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++ unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++ int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++ struct ebt_entry *e = (struct ebt_entry *)chain->data;
++ struct ebt_entry_target *t;
++
++ while (pos < nentries || chain_nr != -1) {
++ // end of udc, go back one 'recursion' step
++ if (pos == nentries) {
++ // put back values of the time when this chain was called
++ e = cl_s[chain_nr].cs.e;
++ if (cl_s[chain_nr].from != -1)
++ nentries =
++ cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++ else
++ nentries = chain->nentries;
++ pos = cl_s[chain_nr].cs.n;
++ // make sure we won't see a loop that isn't one
++ cl_s[chain_nr].cs.n = 0;
++ chain_nr = cl_s[chain_nr].from;
++ if (pos == nentries)
++ continue;
++ }
++ t = (struct ebt_entry_target *)
++ (((char *)e) + e->target_offset);
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++ goto letscontinue;
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ return -1;
++ }
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ if (verdict >= 0) { // jump to another chain
++ struct ebt_entries *hlp2 =
++ (struct ebt_entries *)(base + verdict);
++ for (i = 0; i < udc_cnt; i++)
++ if (hlp2 == cl_s[i].cs.chaininfo)
++ break;
++ // bad destination or loop
++ if (i == udc_cnt) {
++ BUGPRINT("bad destination\n");
++ return -1;
++ }
++ if (cl_s[i].cs.n) {
++ BUGPRINT("loop\n");
++ return -1;
++ }
++ // this can't be 0, so the above test is correct
++ cl_s[i].cs.n = pos + 1;
++ pos = 0;
++ cl_s[i].cs.e = ((void *)e + e->next_offset);
++ e = (struct ebt_entry *)(hlp2->data);
++ nentries = hlp2->nentries;
++ cl_s[i].from = chain_nr;
++ chain_nr = i;
++ // this udc is accessible from the base chain for hooknr
++ cl_s[i].hookmask |= (1 << hooknr);
++ continue;
++ }
++letscontinue:
++ e = (void *)e + e->next_offset;
++ pos++;
++ }
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k, udc_cnt;
++ int ret;
++ struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ newinfo->hook_entry[i] = NULL;
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ &udc_cnt, repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))) {
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // Get the location of the udc, put them in an array
++ // While we're at it, allocate the chainstack
++ if (udc_cnt) {
++ // this will get free'd in do_replace()/ebt_register_table()
++ // if an error occurs
++ newinfo->chainstack = (struct ebt_chainstack **)
++ vmalloc(smp_num_cpus * sizeof(struct ebt_chainstack));
++ if (!newinfo->chainstack)
++ return -ENOMEM;
++ for (i = 0; i < smp_num_cpus; i++) {
++ newinfo->chainstack[i] =
++ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++ if (!newinfo->chainstack[i]) {
++ while (i)
++ vfree(newinfo->chainstack[--i]);
++ vfree(newinfo->chainstack);
++ newinfo->chainstack = NULL;
++ return -ENOMEM;
++ }
++ }
++
++ cl_s = (struct ebt_cl_stack *)
++ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++ if (!cl_s)
++ return -ENOMEM;
++ i = 0; // the i'th udc
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++ repl->valid_hooks, cl_s);
++ // sanity check
++ if (i != udc_cnt) {
++ BUGPRINT("i != udc_cnt\n");
++ vfree(cl_s);
++ return -EFAULT;
++ }
++ }
++
++ // Check for loops
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ if (repl->valid_hooks & (1 << i))
++ if (check_chainloops(newinfo->hook_entry[i],
++ cl_s, udc_cnt, i, newinfo->entries)) {
++ if (cl_s)
++ vfree(cl_s);
++ return -EINVAL;
++ }
++
++ // we now know the following (along with E=mc˛):
++ // - the nr of entries in each chain is right
++ // - the size of the allocated space is right
++ // - all valid hooks have a corresponding chain
++ // - there are no loops
++ // - wrong data can still be on the level of a single entry
++ // - could be there are jumps to places that are not the
++ // beginning of a chain. This can only occur in chains that
++ // are not accessible from any base chains, so we don't care.
++
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++ cl_s, udc_cnt);
++ if (ret != 0) {
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, &i);
++ }
++ if (cl_s)
++ vfree(cl_s);
++ return ret;
++}
++
++// called under write_lock
++static void get_counters(struct ebt_counter *oldcounters,
++ struct ebt_counter *counters, unsigned int nentries)
++{
++ int i, cpu;
++ struct ebt_counter *counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, oldcounters,
++ sizeof(struct ebt_counter) * nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
++ for (i = 0; i < nentries; i++)
++ counters[i].pcnt += counter_base[i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret, i, countersize;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info) + countersize);
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (countersize)
++ memset(newinfo->counters, 0, countersize);
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ // this can get initialized by translate_table()
++ newinfo->chainstack = NULL;
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++ if (!t)
++ goto free_iterate;
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private->counters, counterstmp,
++ t->private->nentries);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->chainstack) {
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(table->chainstack[i]);
++ vfree(table->chainstack);
++ }
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_iterate:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++ // can be initialized in translate_table()
++ if (newinfo->chainstack) {
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(newinfo->chainstack[i]);
++ vfree(newinfo->chainstack);
++ }
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret, i, countersize;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info) + countersize);
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (countersize)
++ memset(newinfo->counters, 0, countersize);
++
++ // fill in newinfo and parse the entries
++ newinfo->chainstack = NULL;
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_chainstack;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_chainstack;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_chainstack:
++ if (newinfo->chainstack) {
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(newinfo->chainstack[i]);
++ vfree(newinfo->chainstack);
++ }
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ int i;
++
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ if (table->private->chainstack) {
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(table->private->chainstack[i]);
++ vfree(table->private->chainstack);
++ }
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++ if (!t)
++ goto free_tmp;
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user,
++ int *len, int cmd)
++{
++ struct ebt_replace tmp;
++ struct ebt_counter *counterstmp, *oldcounters;
++ unsigned int entries_size, nentries;
++ char *entries;
++
++ if (cmd == EBT_SO_GET_ENTRIES) {
++ entries_size = t->private->entries_size;
++ nentries = t->private->nentries;
++ entries = t->private->entries;
++ oldcounters = t->private->counters;
++ } else {
++ entries_size = t->table->entries_size;
++ nentries = t->table->nentries;
++ entries = t->table->entries;
++ oldcounters = t->table->counters;
++ }
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + entries_size +
++ (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ MEMPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(oldcounters, counterstmp, nentries);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, entries, entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(entries, entries_size,
++ ebt_make_names, entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++ if (!t)
++ return ret;
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ case EBT_SO_GET_INIT_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ if (cmd == EBT_SO_GET_INFO) {
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ tmp.valid_hooks = t->valid_hooks;
++ } else {
++ tmp.nentries = t->table->nentries;
++ tmp.entries_size = t->table->entries_size;
++ tmp.valid_hooks = t->table->valid_hooks;
++ }
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ case EBT_SO_GET_INIT_ENTRIES:
++ ret = copy_everything_to_user(t, user, len, cmd);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebtables.h Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,347 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h>
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_INIT_INFO (EBT_SO_GET_ENTRIES+1)
++#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_INIT_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT -1
++#define EBT_DROP -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN -4
++#define NUM_STANDARD_TARGETS 4
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entries {
++ // this field is always set to zero
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ unsigned int distinguisher;
++ // the chain name
++ char name[EBT_CHAIN_MAXNAMELEN];
++ // counter offset for this chain
++ unsigned int counter_offset;
++ // one standard (accept, drop, return) per hook
++ int policy;
++ // nr. of entries
++ unsigned int nentries;
++ // entry list
++ char data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ int verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ unsigned int bitmask;
++ unsigned int invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ char in[IFNAMSIZ];
++ // the logical in-dev
++ char logical_in[IFNAMSIZ];
++ // the physical out-dev
++ char out[IFNAMSIZ];
++ // the logical out-dev
++ char logical_out[IFNAMSIZ];
++ unsigned char sourcemac[ETH_ALEN];
++ unsigned char sourcemsk[ETH_ALEN];
++ unsigned char destmac[ETH_ALEN];
++ unsigned char destmsk[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ unsigned int watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ unsigned int target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ unsigned int next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ int (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++ struct ebt_entries *chaininfo; // pointer to chain data
++ struct ebt_entry *e; // pointer to entry data
++ unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // room to maintain the stack used for jumping from and into udc
++ struct ebt_chainstack **chainstack;
++ char *entries;
++ struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_arp.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_ip.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_vlan.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID 0x01
++#define EBT_VLAN_PRIO 0x02
++#define EBT_VLAN_ENCAP 0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++ __u16 id; /* VLAN ID {1-4095} */
++ __u8 prio; /* VLAN User Priority {0-7} */
++ __u16 encap; /* VLAN Encapsulated frame code {0-65535} */
++ __u8 bitmask; /* Args bitmask bit 1=1 - ID arg,
++ bit 2=1 User-Priority arg, bit 3=1 encap*/
++ __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg,
++ bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_log.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_nat.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_redirect.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_m.h Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++ unsigned long mark, mask;
++ __u8 invert;
++ __u8 bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_t.h Wed Jul 31 20:42:44 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++ unsigned long mark;
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/br_db.h Wed Jul 31 20:42:36 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff
new file mode 100644
index 0000000..6e03a05
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18-rc1.001.diff
@@ -0,0 +1,1152 @@
+--- linux/net/core/dev.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/core/dev.c Wed Jul 31 19:04:30 2002
+@@ -1385,13 +1385,6 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+- defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
+- const struct net_device *in,
+- const struct net_device *out,
+- int (*okfn)(struct sk_buff *)) = NULL;
+-#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+--- linux/net/bridge/br.c Wed Jul 31 19:10:53 2002
++++ ebt2.0-rc1/net/bridge/br.c Wed Jul 31 19:04:30 2002
+@@ -28,6 +28,14 @@
+ #include "../atm/lec.h"
+ #endif
+
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
++
+ void br_dec_use_count()
+ {
+ MOD_DEC_USE_COUNT;
+@@ -82,7 +90,12 @@
+ #endif
+ }
+
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#else
+ EXPORT_NO_SYMBOLS;
++#endif
+
+ module_init(br_init)
+ module_exit(br_deinit)
+--- linux/net/bridge/Makefile Wed Jul 31 19:10:53 2002
++++ ebt2.0-rc1/net/bridge/Makefile Wed Jul 31 19:04:30 2002
+@@ -7,6 +7,12 @@
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),n)
++ifneq ($(CONFIG_BRIDGE_EBT_BROUTE),)
++export-objs := br.o
++endif
++endif
++
+ O_TARGET := bridge.o
+ obj-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
+ br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
+--- linux/include/linux/netfilter_bridge.h Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/include/linux/netfilter_bridge.h Wed Jul 31 19:04:30 2002
+@@ -28,8 +28,7 @@
+ NF_BR_PRI_FILTER_OTHER = 200,
+ NF_BR_PRI_NAT_DST_BRIDGED = -300,
+ NF_BR_PRI_NAT_DST_OTHER = 100,
+- NF_BR_PRI_NAT_SRC_BRIDGED = -100,
+- NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_NAT_SRC = 300,
+ NF_BR_PRI_LAST = INT_MAX,
+ };
+
+--- linux/net/netsyms.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/netsyms.c Mon Feb 25 20:38:14 2002
+@@ -228,10 +228,6 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
+-#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
+- defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
+-EXPORT_SYMBOL(broute_decision);
+-#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/net/Makefile Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/Makefile Wed Jul 31 19:04:30 2002
+@@ -28,7 +28,7 @@
+ endif
+
+ ifneq ($(CONFIG_BRIDGE),n)
+-ifneq ($CONFIG_BRIDGE),)
++ifneq ($(CONFIG_BRIDGE),)
+ subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+--- linux/net/bridge/netfilter/Makefile Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/Makefile Wed Jul 31 19:04:30 2002
+@@ -9,7 +9,7 @@
+
+ O_TARGET := netfilter.o
+
+-export-objs = ebtables.o
++export-objs := ebtables.o
+
+ obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
+ obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
+@@ -19,8 +19,10 @@
+ obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
+ obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
+ obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_MARKF) += ebt_mark_m.o
+ obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
+ obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
+ obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
+ obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++obj-$(CONFIG_BRIDGE_EBT_MARK_T) += ebt_mark.o
+ include $(TOPDIR)/Rules.make
+--- linux/net/bridge/netfilter/Config.in Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/Config.in Wed Jul 31 19:04:30 2002
+@@ -9,8 +9,10 @@
+ dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
+ dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
+ dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: mark filter support' CONFIG_BRIDGE_EBT_MARKF $CONFIG_BRIDGE_EBT
+ dep_tristate ' ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
+ dep_tristate ' ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
+ dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: mark target support' CONFIG_BRIDGE_EBT_MARK_T $CONFIG_BRIDGE_EBT
+ dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
+
+--- linux/net/bridge/netfilter/ebtable_nat.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebtable_nat.c Wed Jul 31 19:04:30 2002
+@@ -42,16 +42,6 @@
+ RW_LOCK_UNLOCKED, check, NULL
+ };
+
+-// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
+-// needed because of the bridge-nf patch (that allows use of iptables
+-// on bridged traffic)
+-// if the packet is routed, we want the ebtables stuff on POSTROUTING
+-// to be executed _after_ the iptables stuff. when it's bridged, it's
+-// the way around
+-static struct net_device __fake_net_device = {
+- hard_header_len: ETH_HLEN
+-};
+-
+ static unsigned int
+ ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+@@ -60,50 +50,11 @@
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+
+-// let snat know this frame is routed
+-static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
+- const struct net_device *in, const struct net_device *out,
+- int (*okfn)(struct sk_buff *))
+-{
+- (*pskb)->physindev = NULL;
+- return NF_ACCEPT;
+-}
+-
+-// let snat know this frame is bridged
+-static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
+- const struct net_device *in, const struct net_device *out,
+- int (*okfn)(struct sk_buff *))
+-{
+- (*pskb)->physindev = &__fake_net_device;
+- return NF_ACCEPT;
+-}
+-
+ static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+ {
+- // this is a routed packet
+- if ((*pskb)->physindev == NULL)
+- return NF_ACCEPT;
+- if ((*pskb)->physindev != &__fake_net_device)
+- printk("ebtables (br_nat_src): physindev hack "
+- "doesn't work - BUG\n");
+-
+- return ebt_do_table(hook, pskb, in, out, &frame_nat);
+-}
+-
+-static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
+- const struct net_device *in, const struct net_device *out,
+- int (*okfn)(struct sk_buff *))
+-{
+- // this is a bridged packet
+- if ((*pskb)->physindev == &__fake_net_device)
+- return NF_ACCEPT;
+- if ((*pskb)->physindev)
+- printk("ebtables (br_nat_src_route): physindev hack "
+- "doesn't work - BUG\n");
+-
+ return ebt_do_table(hook, pskb, in, out, &frame_nat);
+ }
+
+@@ -111,15 +62,9 @@
+ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
+ NF_BR_PRI_NAT_DST_OTHER},
+ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
+- NF_BR_PRI_NAT_SRC_BRIDGED},
+- { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
+- NF_BR_PRI_NAT_SRC_OTHER},
++ NF_BR_PRI_NAT_SRC},
+ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
+ NF_BR_PRI_NAT_DST_BRIDGED},
+- { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
+- NF_BR_PRI_FILTER_OTHER + 1},
+- { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
+- NF_BR_PRI_FILTER_OTHER + 1}
+ };
+
+ static int __init init(void)
+--- linux/net/bridge/netfilter/ebt_redirect.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_redirect.c Wed Jul 31 19:04:30 2002
+@@ -38,6 +38,10 @@
+ {
+ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark.c Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,73 @@
++/*
++ * ebt_mark_t
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * July, 2002
++ *
++ */
++
++// The mark target can be used in any chain
++// I believe adding a mangle table just for marking is total overkill
++// Marking a frame doesn't really change anything in the frame anyway
++// The target member of the struct ebt_vlan_info provides the same
++// functionality as a separate table
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_t.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_mark(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++ if ((*pskb)->nfmark != infostuff->mark) {
++ (*pskb)->nfmark = infostuff->mark;
++ (*pskb)->nfcache |= NFC_ALTERED;
++ }
++ return infostuff->target;
++}
++
++static int ebt_target_mark_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_mark_t_info *infostuff = (struct ebt_mark_t_info *) data;
++
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
++ if (datalen != sizeof(struct ebt_mark_t_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target mark_target =
++{
++ {NULL, NULL}, EBT_MARK_TARGET, ebt_target_mark,
++ ebt_target_mark_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&mark_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&mark_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_mark_m.c Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,62 @@
++/*
++ * ebt_mark_m
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * July, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_mark_m.h>
++#include <linux/module.h>
++
++static int ebt_filter_mark(const struct sk_buff *skb,
++ const struct net_device *in, const struct net_device *out, const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++ if (info->bitmask & EBT_MARK_OR)
++ return !(!!(skb->nfmark & info->mask) ^ info->invert);
++ return !(((skb->nfmark & info->mask) == info->mark) ^ info->invert);
++}
++
++static int ebt_mark_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_mark_m_info *info = (struct ebt_mark_m_info *) data;
++
++ if (info->bitmask & ~EBT_MARK_MASK)
++ return -EINVAL;
++ if ((info->bitmask & EBT_MARK_OR) && (info->bitmask & EBT_MARK_AND))
++ return -EINVAL;
++ if (!info->bitmask)
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_mark_m_info)) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_mark =
++{
++ {NULL, NULL}, EBT_MARK_MATCH, ebt_filter_mark, ebt_mark_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_mark);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_mark);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- linux/net/bridge/netfilter/ebt_snat.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_snat.c Wed Jul 31 19:04:30 2002
+@@ -31,6 +31,10 @@
+ {
+ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ if (strcmp(tablename, "nat"))
+ return -EINVAL;
+ if (datalen != sizeof(struct ebt_nat_info))
+--- linux/net/bridge/netfilter/ebt_dnat.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebt_dnat.c Wed Jul 31 19:04:30 2002
+@@ -31,6 +31,10 @@
+ {
+ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+
++ if ((hookmask & (1 << NF_BR_NUMHOOKS)) &&
++ infostuff->target == EBT_RETURN)
++ return -EINVAL;
++ hookmask &= ~(1 << NF_BR_NUMHOOKS);
+ if ( (strcmp(tablename, "nat") ||
+ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
+ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+--- linux/net/bridge/netfilter/ebtables.c Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/net/bridge/netfilter/ebtables.c Wed Jul 31 19:04:30 2002
+@@ -4,7 +4,7 @@
+ * Author:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+- * ebtables.c,v 2.0, April, 2002
++ * ebtables.c,v 2.0, July, 2002
+ *
+ * This code is stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+@@ -50,6 +50,21 @@
+ ": out of memory: "format, ## args)
+ // #define MEMPRINT(format, args...)
+
++
++
++// Each cpu has its own set of counters, so there is no need for write_lock in
++// the softirq
++// For reading or updating the counters, the user context needs to
++// get a write_lock
++
++// The size of each set of counters is altered to get cache alignment
++#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
++#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
++#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
++ COUNTER_OFFSET(n) * cpu))
++
++
++
+ static void print_string(char *str);
+
+ static DECLARE_MUTEX(ebt_mutex);
+@@ -62,10 +77,8 @@
+ { {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
+
+ static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
+- const struct sk_buff *skb,
+- const struct net_device *in,
+- const struct net_device *out,
+- const struct ebt_counter *c)
++ const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const struct ebt_counter *c)
+ {
+ w->u.watcher->watcher(skb, in, out, w->data,
+ w->watcher_size, c);
+@@ -74,10 +87,8 @@
+ }
+
+ static inline int ebt_do_match (struct ebt_entry_match *m,
+- const struct sk_buff *skb,
+- const struct net_device *in,
+- const struct net_device *out,
+- const struct ebt_counter *c)
++ const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const struct ebt_counter *c)
+ {
+ return m->u.match->match(skb, in, out, m->data,
+ m->match_size, c);
+@@ -89,7 +100,51 @@
+ return 0;
+ if (!device)
+ return 1;
+- return strncmp(entry, device->name, IFNAMSIZ);
++ return !!strncmp(entry, device->name, IFNAMSIZ);
++}
++
++#define FWINV(bool,invflg) ((bool) ^ !!(p->invflags & invflg))
++// process standard matches
++static inline int ebt_basic_match(struct ebt_entry *p, struct ethhdr *h,
++ const struct net_device *in, const struct net_device *out)
++{
++ int verdict, i;
++
++ if (p->bitmask & EBT_802_3) {
++ if (FWINV(ntohs(h->h_proto) >= 1536, EBT_IPROTO))
++ return 1;
++ } else if (!(p->bitmask & EBT_NOPROTO) &&
++ FWINV(p->ethproto != h->h_proto, EBT_IPROTO))
++ return 1;
++
++ if (FWINV(ebt_dev_check(p->in, in), EBT_IIN))
++ return 1;
++ if (FWINV(ebt_dev_check(p->out, out), EBT_IOUT))
++ return 1;
++ if ((!in || !in->br_port) ? 0 : FWINV(ebt_dev_check(
++ p->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
++ return 1;
++ if ((!out || !out->br_port) ? 0 : FWINV(ebt_dev_check(
++ (p->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++ return 1;
++
++ if (p->bitmask & EBT_SOURCEMAC) {
++ verdict = 0;
++ for (i = 0; i < 6; i++)
++ verdict |= (h->h_source[i] ^ p->sourcemac[i]) &
++ p->sourcemsk[i];
++ if (FWINV(verdict != 0, EBT_ISOURCE) )
++ return 1;
++ }
++ if (p->bitmask & EBT_DESTMAC) {
++ verdict = 0;
++ for (i = 0; i < 6; i++)
++ verdict |= (h->h_dest[i] ^ p->destmac[i]) &
++ p->destmsk[i];
++ if (FWINV(verdict != 0, EBT_IDEST) )
++ return 1;
++ }
++ return 0;
+ }
+
+ // Do some firewalling
+@@ -97,9 +152,9 @@
+ const struct net_device *in, const struct net_device *out,
+ struct ebt_table *table)
+ {
+- int i, j, nentries;
++ int i, nentries;
+ struct ebt_entry *point;
+- struct ebt_counter *counter_base;
++ struct ebt_counter *counter_base, *cb_base;
+ struct ebt_entry_target *t;
+ int verdict, sp = 0;
+ struct ebt_chainstack *cs;
+@@ -108,6 +163,8 @@
+ struct ebt_table_info *private = table->private;
+
+ read_lock_bh(&table->lock);
++ cb_base = COUNTER_BASE(private->counters, private->nentries, \
++ cpu_number_map(smp_processor_id()));
+ if (private->chainstack)
+ cs = private->chainstack[cpu_number_map(smp_processor_id())];
+ else
+@@ -115,117 +172,83 @@
+ chaininfo = private->hook_entry[hook];
+ nentries = private->hook_entry[hook]->nentries;
+ point = (struct ebt_entry *)(private->hook_entry[hook]->data);
+- #define cb_base table->private->counters + \
+- cpu_number_map(smp_processor_id()) * table->private->nentries
+ counter_base = cb_base + private->hook_entry[hook]->counter_offset;
+- #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
+ // base for chain jumps
+ base = (char *)chaininfo;
+ i = 0;
+- while (i < nentries) {
+- if ( ( point->bitmask & EBT_NOPROTO ||
+- FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
+- EBT_IPROTO)
+- || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
+- (point->bitmask & EBT_802_3), EBT_IPROTO) )
+- && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
+- && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
+- && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
+- (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
+- && ((!out || !out->br_port) ? 1 :
+- FWINV(!ebt_dev_check((char *)
+- (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+-
+- ) {
+- if (point->bitmask & EBT_SOURCEMAC) {
+- verdict = 0;
+- for (j = 0; j < 6; j++)
+- verdict |= (((**pskb).mac.ethernet)->
+- h_source[j] ^ point->sourcemac[j]) &
+- point->sourcemsk[j];
+- if (FWINV(!!verdict, EBT_ISOURCE) )
+- goto letscontinue;
+- }
++ while (i < nentries) {
++ if (ebt_basic_match(point, (**pskb).mac.ethernet, in, out))
++ goto letscontinue;
+
+- if (point->bitmask & EBT_DESTMAC) {
+- verdict = 0;
+- for (j = 0; j < 6; j++)
+- verdict |= (((**pskb).mac.ethernet)->
+- h_dest[j] ^ point->destmac[j]) &
+- point->destmsk[j];
+- if (FWINV(!!verdict, EBT_IDEST) )
+- goto letscontinue;
+- }
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
+
+- if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
+- out, counter_base + i) != 0)
+- goto letscontinue;
++ // increase counter
++ (*(counter_base + i)).pcnt++;
+
+- // increase counter
+- (*(counter_base + i)).pcnt++;
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
+
+- // these should only watch: not modify, nor tell us
+- // what to do with the packet
+- EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
+- out, counter_base + i);
+-
+- t = (struct ebt_entry_target *)
+- (((char *)point) + point->target_offset);
+- // standard target
+- if (!t->u.target->target)
+- verdict =
+- ((struct ebt_standard_target *)t)->verdict;
+- else
+- verdict = t->u.target->target(pskb, hook,
+- in, out, t->data, t->target_size);
+- if (verdict == EBT_ACCEPT) {
+- read_unlock_bh(&table->lock);
+- return NF_ACCEPT;
+- }
+- if (verdict == EBT_DROP) {
+- read_unlock_bh(&table->lock);
+- return NF_DROP;
+- }
+- if (verdict == EBT_RETURN) {
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict == EBT_RETURN) {
+ letsreturn:
+- if (sp == 0)
+- // act like this is EBT_CONTINUE
+- goto letscontinue;
+- sp--;
+- // put all the local variables right
+- i = cs[sp].n;
+- chaininfo = cs[sp].chaininfo;
+- nentries = chaininfo->nentries;
+- point = cs[sp].e;
+- counter_base = cb_base +
+- chaininfo->counter_offset;
+- continue;
+- }
+- if (verdict == EBT_CONTINUE)
++ if (sp == 0) {
++ BUGPRINT("RETURN on base chain");
++ // act like this is EBT_CONTINUE
+ goto letscontinue;
+- if (verdict < 0) {
+- BUGPRINT("bogus standard verdict\n");
+- read_unlock_bh(&table->lock);
+- return NF_DROP;
+- }
+- // jump to a udc
+- cs[sp].n = i + 1;
+- cs[sp].chaininfo = chaininfo;
+- cs[sp].e = (struct ebt_entry *)
+- (((char *)point) + point->next_offset);
+- i = 0;
+- chaininfo = (struct ebt_entries *) (base + verdict);
+- if (chaininfo->distinguisher) {
+- BUGPRINT("jump to non-chain\n");
+- read_unlock_bh(&table->lock);
+- return NF_DROP;
+ }
++ sp--;
++ // put all the local variables right
++ i = cs[sp].n;
++ chaininfo = cs[sp].chaininfo;
+ nentries = chaininfo->nentries;
+- point = (struct ebt_entry *)chaininfo->data;
+- counter_base = cb_base + chaininfo->counter_offset;
+- sp++;
++ point = cs[sp].e;
++ counter_base = cb_base +
++ chaininfo->counter_offset;
+ continue;
+ }
++ if (verdict == EBT_CONTINUE)
++ goto letscontinue;
++ if (verdict < 0) {
++ BUGPRINT("bogus standard verdict\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ // jump to a udc
++ cs[sp].n = i + 1;
++ cs[sp].chaininfo = chaininfo;
++ cs[sp].e = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i = 0;
++ chaininfo = (struct ebt_entries *) (base + verdict);
++ if (chaininfo->distinguisher) {
++ BUGPRINT("jump to non-chain\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ nentries = chaininfo->nentries;
++ point = (struct ebt_entry *)chaininfo->data;
++ counter_base = cb_base + chaininfo->counter_offset;
++ sp++;
++ continue;
+ letscontinue:
+ point = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
+@@ -284,7 +307,6 @@
+ request_module(modulename);
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ }
+-
+ return ret;
+ }
+ #endif
+@@ -320,9 +342,12 @@
+ struct ebt_match *match;
+ int ret;
+
++ if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
++ ((char *)e) + e->watchers_offset)
++ return -EINVAL;
+ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ match = find_match_lock(m->u.name, &ret, &ebt_mutex);
+- if (!match)
++ if (!match)
+ return ret;
+ m->u.match = match;
+ if (match->me)
+@@ -346,9 +371,12 @@
+ struct ebt_watcher *watcher;
+ int ret;
+
++ if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
++ ((char *)e) + e->target_offset)
++ return -EINVAL;
+ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
+- if (!watcher)
++ if (!watcher)
+ return ret;
+ w->u.watcher = watcher;
+ if (watcher->me)
+@@ -547,14 +575,16 @@
+ else
+ break;
+ }
++ // (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
++ // a base chain
+ if (i < NF_BR_NUMHOOKS)
+- hookmask = (1 << hook);
++ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else {
+ for (i = 0; i < udc_cnt; i++)
+ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
+ break;
+ if (i == 0)
+- hookmask = (1 << hook);
++ hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
+ else
+ hookmask = cl_s[i - 1].hookmask;
+ }
+@@ -589,9 +619,10 @@
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+- } else if (t->u.target->check &&
+- t->u.target->check(name, hookmask, e, t->data,
+- t->target_size) != 0) {
++ } else if ((e->target_offset + t->target_size +
++ sizeof(struct ebt_entry_target) > e->next_offset) ||
++ (t->u.target->check &&
++ t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+ ret = -EFAULT;
+@@ -611,7 +642,7 @@
+ {
+ struct ebt_entry_target *t;
+
+- if (e->bitmask == 0)
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+ return 0;
+ // we're done
+ if (cnt && (*cnt)-- == 0)
+@@ -643,7 +674,8 @@
+ // put back values of the time when this chain was called
+ e = cl_s[chain_nr].cs.e;
+ if (cl_s[chain_nr].from != -1)
+- nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++ nentries =
++ cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
+ else
+ nentries = chain->nentries;
+ pos = cl_s[chain_nr].cs.n;
+@@ -679,6 +711,7 @@
+ BUGPRINT("loop\n");
+ return -1;
+ }
++ // this can't be 0, so the above test is correct
+ cl_s[i].cs.n = pos + 1;
+ pos = 0;
+ cl_s[i].cs.e = ((void *)e + e->next_offset);
+@@ -784,7 +817,7 @@
+ vfree(newinfo->chainstack[--i]);
+ vfree(newinfo->chainstack);
+ newinfo->chainstack = NULL;
+- break;
++ return -ENOMEM;
+ }
+ }
+
+@@ -824,7 +857,6 @@
+ // beginning of a chain. This can only occur in chains that
+ // are not accessible from any base chains, so we don't care.
+
+- // we just don't trust anything
+ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ // used to know what we need to clean up if something goes wrong
+ i = 0;
+@@ -841,27 +873,27 @@
+ }
+
+ // called under write_lock
+-static inline void get_counters(struct ebt_counter *oldcounters,
++static void get_counters(struct ebt_counter *oldcounters,
+ struct ebt_counter *counters, unsigned int nentries)
+ {
+- int i, cpu, counter_base;
++ int i, cpu;
++ struct ebt_counter *counter_base;
+
+ // counters of cpu 0
+ memcpy(counters, oldcounters,
+ sizeof(struct ebt_counter) * nentries);
+ // add other counters to those of cpu 0
+ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
+- counter_base = cpu * nentries;
++ counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
+ for (i = 0; i < nentries; i++)
+- counters[i].pcnt +=
+- oldcounters[counter_base + i].pcnt;
++ counters[i].pcnt += counter_base[i].pcnt;
+ }
+ }
+
+ // replace the table
+ static int do_replace(void *user, unsigned int len)
+ {
+- int ret, i;
++ int ret, i, countersize;
+ struct ebt_table_info *newinfo;
+ struct ebt_replace tmp;
+ struct ebt_table *t;
+@@ -869,7 +901,7 @@
+ // used to be able to unlock earlier
+ struct ebt_table_info *table;
+
+- if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ if (len != sizeof(tmp) + tmp.entries_size) {
+@@ -881,28 +913,19 @@
+ BUGPRINT("Entries_size never zero\n");
+ return -EINVAL;
+ }
++ countersize = COUNTER_OFFSET(tmp.nentries) * smp_num_cpus;
+ newinfo = (struct ebt_table_info *)
+- vmalloc(sizeof(struct ebt_table_info));
++ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ if (!newinfo)
+ return -ENOMEM;
+
+- if (tmp.nentries) {
+- newinfo->counters = (struct ebt_counter *)vmalloc(
+- sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
+- if (!newinfo->counters) {
+- ret = -ENOMEM;
+- goto free_newinfo;
+- }
+- memset(newinfo->counters, 0,
+- sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
+- }
+- else
+- newinfo->counters = NULL;
++ if (countersize)
++ memset(newinfo->counters, 0, countersize);
+
+ newinfo->entries = (char *)vmalloc(tmp.entries_size);
+ if (!newinfo->entries) {
+ ret = -ENOMEM;
+- goto free_counters;
++ goto free_newinfo;
+ }
+ if (copy_from_user(
+ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+@@ -933,7 +956,7 @@
+
+ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
+ if (!t)
+- goto free_unlock;
++ goto free_iterate;
+
+ // the table doesn't like it
+ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+@@ -974,8 +997,6 @@
+ ebt_cleanup_entry, NULL);
+
+ vfree(table->entries);
+- if (table->counters)
+- vfree(table->counters);
+ if (table->chainstack) {
+ for (i = 0; i < smp_num_cpus; i++)
+ vfree(table->chainstack[i]);
+@@ -989,6 +1010,7 @@
+
+ free_unlock:
+ up(&ebt_mutex);
++free_iterate:
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, NULL);
+ free_counterstmp:
+@@ -1003,9 +1025,6 @@
+ free_entries:
+ if (newinfo->entries)
+ vfree(newinfo->entries);
+-free_counters:
+- if (newinfo->counters)
+- vfree(newinfo->counters);
+ free_newinfo:
+ if (newinfo)
+ vfree(newinfo);
+@@ -1090,7 +1109,7 @@
+ int ebt_register_table(struct ebt_table *table)
+ {
+ struct ebt_table_info *newinfo;
+- int ret, i;
++ int ret, i, countersize;
+
+ if (!table || !table->table ||!table->table->entries ||
+ table->table->entries_size == 0 ||
+@@ -1099,8 +1118,9 @@
+ return -EINVAL;
+ }
+
++ countersize = COUNTER_OFFSET(table->table->nentries) * smp_num_cpus;
+ newinfo = (struct ebt_table_info *)
+- vmalloc(sizeof(struct ebt_table_info));
++ vmalloc(sizeof(struct ebt_table_info) + countersize);
+ ret = -ENOMEM;
+ if (!newinfo)
+ return -ENOMEM;
+@@ -1112,24 +1132,15 @@
+ memcpy(newinfo->entries, table->table->entries,
+ table->table->entries_size);
+
+- if (table->table->nentries) {
+- newinfo->counters = (struct ebt_counter *)
+- vmalloc(table->table->nentries *
+- sizeof(struct ebt_counter) * smp_num_cpus);
+- if (!newinfo->counters)
+- goto free_entries;
+- memset(newinfo->counters, 0, table->table->nentries *
+- sizeof(struct ebt_counter) * smp_num_cpus);
+- }
+- else
+- newinfo->counters = NULL;
++ if (countersize)
++ memset(newinfo->counters, 0, countersize);
+
+ // fill in newinfo and parse the entries
+ newinfo->chainstack = NULL;
+ ret = translate_table(table->table, newinfo);
+ if (ret != 0) {
+ BUGPRINT("Translate_table failed\n");
+- goto free_counters;
++ goto free_chainstack;
+ }
+
+ if (table->check && table->check(newinfo, table->valid_hooks)) {
+@@ -1141,7 +1152,7 @@
+ table->lock = RW_LOCK_UNLOCKED;
+ ret = down_interruptible(&ebt_mutex);
+ if (ret != 0)
+- goto free_counters;
++ goto free_chainstack;
+
+ if (list_named_find(&ebt_tables, table->name)) {
+ ret = -EEXIST;
+@@ -1155,15 +1166,12 @@
+ return 0;
+ free_unlock:
+ up(&ebt_mutex);
+-free_counters:
+- if (newinfo->counters)
+- vfree(newinfo->counters);
++free_chainstack:
+ if (newinfo->chainstack) {
+ for (i = 0; i < smp_num_cpus; i++)
+ vfree(newinfo->chainstack[i]);
+ vfree(newinfo->chainstack);
+ }
+-free_entries:
+ vfree(newinfo->entries);
+ free_newinfo:
+ vfree(newinfo);
+@@ -1183,8 +1191,6 @@
+ up(&ebt_mutex);
+ EBT_ENTRY_ITERATE(table->private->entries,
+ table->private->entries_size, ebt_cleanup_entry, NULL);
+- if (table->private->counters)
+- vfree(table->private->counters);
+ if (table->private->entries)
+ vfree(table->private->entries);
+ if (table->private->chainstack) {
+@@ -1219,7 +1225,6 @@
+ }
+
+ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+-
+ t = find_table_lock(hlp.name, &ret, &ebt_mutex);
+ if (!t)
+ goto free_tmp;
+@@ -1344,7 +1349,7 @@
+ counterstmp = (struct ebt_counter *)
+ vmalloc(nentries * sizeof(struct ebt_counter));
+ if (!counterstmp) {
+- BUGPRINT("Couldn't copy counters, out of memory\n");
++ MEMPRINT("Couldn't copy counters, out of memory\n");
+ return -ENOMEM;
+ }
+ write_lock_bh(&t->lock);
+--- linux/include/linux/netfilter_bridge/ebtables.h Wed Jul 31 19:15:22 2002
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebtables.h Wed Jul 31 19:04:30 2002
+@@ -12,9 +12,9 @@
+
+ #ifndef __LINUX_BRIDGE_EFF_H
+ #define __LINUX_BRIDGE_EFF_H
+-#include <linux/if.h> // IFNAMSIZ
++#include <linux/if.h>
+ #include <linux/netfilter_bridge.h>
+-#include <linux/if_ether.h> // ETH_ALEN
++#include <linux/if_ether.h>
+
+ #define EBT_TABLE_MAXNAMELEN 32
+ #define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+@@ -46,10 +46,10 @@
+ };
+
+ struct ebt_entries {
+- // this field is always set to zero (including userspace).
++ // this field is always set to zero
+ // See EBT_ENTRY_OR_ENTRIES.
+ // Must be same size as ebt_entry.bitmask
+- __u32 distinguisher;
++ unsigned int distinguisher;
+ // the chain name
+ char name[EBT_CHAIN_MAXNAMELEN];
+ // counter offset for this chain
+@@ -133,27 +133,27 @@
+ // one entry
+ struct ebt_entry {
+ // this needs to be the first field
+- __u32 bitmask;
+- __u32 invflags;
++ unsigned int bitmask;
++ unsigned int invflags;
+ __u16 ethproto;
+ // the physical in-dev
+- __u8 in[IFNAMSIZ];
++ char in[IFNAMSIZ];
+ // the logical in-dev
+- __u8 logical_in[IFNAMSIZ];
++ char logical_in[IFNAMSIZ];
+ // the physical out-dev
+- __u8 out[IFNAMSIZ];
++ char out[IFNAMSIZ];
+ // the logical out-dev
+- __u8 logical_out[IFNAMSIZ];
+- __u8 sourcemac[ETH_ALEN];
+- __u8 sourcemsk[ETH_ALEN];
+- __u8 destmac[ETH_ALEN];
+- __u8 destmsk[ETH_ALEN];
++ char logical_out[IFNAMSIZ];
++ unsigned char sourcemac[ETH_ALEN];
++ unsigned char sourcemsk[ETH_ALEN];
++ unsigned char destmac[ETH_ALEN];
++ unsigned char destmsk[ETH_ALEN];
+ // sizeof ebt_entry + matches
+- __u16 watchers_offset;
++ unsigned int watchers_offset;
+ // sizeof ebt_entry + matches + watchers
+- __u16 target_offset;
++ unsigned int target_offset;
+ // sizeof ebt_entry + matches + watchers + target
+- __u16 next_offset;
++ unsigned int next_offset;
+ unsigned char elems[0];
+ };
+
+@@ -238,10 +238,10 @@
+ unsigned int nentries;
+ // pointers to the start of the chains
+ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+- struct ebt_counter *counters;
+ // room to maintain the stack used for jumping from and into udc
+ struct ebt_chainstack **chainstack;
+ char *entries;
++ struct ebt_counter counters[0] __attribute__((aligned(SMP_CACHE_BYTES)));
+ };
+
+ struct ebt_table
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_t.h Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,12 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
++#define __LINUX_BRIDGE_EBT_MARK_T_H
++
++struct ebt_mark_t_info
++{
++ unsigned long mark;
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_MARK_TARGET "mark"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0-rc1/include/linux/netfilter_bridge/ebt_mark_m.h Wed Jul 31 19:04:30 2002
+@@ -0,0 +1,15 @@
++#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
++#define __LINUX_BRIDGE_EBT_MARK_M_H
++
++#define EBT_MARK_AND 0x01
++#define EBT_MARK_OR 0x02
++#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
++struct ebt_mark_m_info
++{
++ unsigned long mark, mask;
++ __u8 invert;
++ __u8 bitmask;
++};
++#define EBT_MARK_MATCH "mark_m"
++
++#endif