summaryrefslogtreecommitdiffstats
path: root/kernel/patches
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
commitd891e9e5bc309d5aeb2ab774c76b34a92085b3e7 (patch)
tree94b68fc1c01f90cad62a171c27007ff317031008 /kernel/patches
Initial revision
Diffstat (limited to 'kernel/patches')
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff2621
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff2752
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff3108
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff3122
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff3133
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff3135
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff3285
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre2.001.diff11
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.001.diff166
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.002.diff66
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.003.diff367
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.004.diff252
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre3.005.diff22
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre4.001.diff14
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre5.001.diff51
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre6.001.diff12
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre7.001.diff172
17 files changed, 22289 insertions, 0 deletions
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff
new file mode 100644
index 0000000..bab3e11
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre1_vs_2.4.18.diff
@@ -0,0 +1,2621 @@
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre1/net/Makefile Wed Apr 3 19:57:30 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Wed Apr 3 21:50:19 2002
++++ ebt2.0pre1/net/Config.in Wed Apr 3 18:47:51 2002
+@@ -60,6 +60,7 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++ source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/Makefile Wed Apr 3 18:48:52 2002
+@@ -0,0 +1,23 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/Config.in Wed Apr 3 18:48:59 2002
+@@ -0,0 +1,12 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/br_db.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtable_filter.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,89 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtable_nat.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,149 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT
++// needed because of the bridge-nf patch (that allows use of iptables on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING to be executed _after_ the iptables stuff
++// when it's bridged, it's the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING, 300},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT, 200 + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_arp.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,95 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode != ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype != ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype != ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw)+sizeof(struct arphdr)+((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk), EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk), EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_ip.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,74 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS && FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol != ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE && FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) != infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) != infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_log.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,103 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++ void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");// max length: 29 + 10 + 2 * 16
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));// length: 14
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto == htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,", NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));// max length: 46
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);// max length: 26
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro), ntohs(arph->ar_op));// max length: 40
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebt_nat.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,114 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (skb_cloned(*pskb)) {
++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++ if (!nskb)
++ return EBT_DROP;
++ if ((*pskb)->sk)
++ skb_set_owner_w(nskb, (*pskb)->sk);
++ kfree_skb(*pskb);
++ *pskb = nskb;
++ }
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac, ETH_ALEN * sizeof(unsigned char));
++ return EBT_ACCEPT;
++}
++
++__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (skb_cloned(*pskb)) {
++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++ if (!nskb)
++ return EBT_DROP;
++ if ((*pskb)->sk)
++ skb_set_owner_w(nskb, (*pskb)->sk);
++ kfree_skb(*pskb);
++ *pskb = nskb;
++ }
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac, ETH_ALEN * sizeof(unsigned char));
++ return EBT_ACCEPT;
++}
++
++int ebt_target_snat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ return 0;
++}
++
++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr, const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
++ return -EINVAL;
++ return 0;
++}
++
++struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check, NULL, THIS_MODULE
++};
++
++struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/net/bridge/netfilter/ebtables.c Wed Apr 3 18:48:47 2002
+@@ -0,0 +1,1088 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++/* used for print_string */
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h> /* list_named_find */
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data, w->watcher_size - sizeof(struct ebt_entry_watcher), c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data, m->match_size - sizeof(struct ebt_entry_match), c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters + cpu_number_map(smp_processor_id()) * table->private->nentries + table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto, EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 && (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT) ){
++
++ if ( (point->bitmask & EBT_SOURCEMAC) &&
++ FWINV(!!memcmp(point->sourcemac, ((**pskb).mac.ethernet)->h_source, ETH_ALEN), EBT_ISOURCE) )
++ goto letscontinue;
++
++ if ( (point->bitmask & EBT_DESTMAC) &&
++ FWINV(!!memcmp(point->destmac, ((**pskb).mac.ethernet)->h_dest, ETH_ALEN), EBT_IDEST) )
++ goto letscontinue;
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in, out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in, out, counter_base + i);
++
++ t = (struct ebt_entry_target *) (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook, in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)(((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)list_named_find(&ebt_matches, m->u.name))) {
++ BUGPRINT("match does not exist: %s\n", m->u.name);
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data, m->match_size - sizeof(*m)) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e, const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)list_named_find(&ebt_watchers, w->u.name))) {
++ BUGPRINT("watcher does not exist: %s\n", w->u.name);
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data, w->watcher_size - sizeof(*w)) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ char *base, char *limit, struct ebt_entries **hook_entries, unsigned int *n,
++ unsigned int *cnt, unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base == (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right, so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries) > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP && ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset || e->watchers_offset > e->target_offset || e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size - sizeof(*m));
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w));
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo, const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)list_named_find(&ebt_targets, t->u.name))) {
++ BUGPRINT("Target does not exist: %s\n", t->u.name);
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) > e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >= NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data, t->target_size - sizeof(*t)) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size - sizeof(*t));
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k, repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the (last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL && (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ i = 0; // used to know what we need to clean up if something goes wrong
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called with under write_lock
++void get_counters(struct ebt_table_info *info, struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters, sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt += info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0, sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter allocation
++ // Only reason why I do this is because this way the lock is held only once,
++ // while this doesn't bring the kernel into a dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp, tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size, ebt_cleanup_entry, NULL);
++ vfree(table->entries);
++
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries, table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries * sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++/* userspace just supplied us with counters */
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ BUGPRINT("Table not found for update_counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters, hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m, char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w, char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size + (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp, info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry, NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry, NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *) (((char *)(info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size, ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts
++= { { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebtables.h Wed Apr 3 20:44:38 2002
+@@ -0,0 +1,304 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> /* IFNAMSIZ */
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> /* ETH_ALEN */
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++/* [gs]etsockopt numbers */
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses different
++// techniques for naming the policy and such. So, iptables doesn't need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ unsigned int match_size;// size of this struct + sizee of data
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ unsigned int watcher_size;// size of this struct + sizee of data
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ unsigned int target_size;// size of this struct + sizee of data
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target;
++ __u8 verdict;
++};
++
++/* one entry */
++struct ebt_entry {
++ __u32 bitmask; // this needs to be the first field
++ __u32 invflags;
++ __u16 ethproto;
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ unsigned int nentries; // nr of rules in the table
++ unsigned int entries_size; // total size of the entries
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; // start of the chains
++ unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it?
++ unsigned int num_counters; // nr of counters userspace expects back
++ struct ebt_counter *counters; // where the kernel will put the old counters
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // true == let it in
++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++ void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // true == let it in
++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++ void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // true == let it in
++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++ void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ unsigned int entries_size; // total size of the entries
++ unsigned int nentries;
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ unsigned int counter_entry[NF_BR_NUMHOOKS]; // how many counters in front of it?
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain matches, targets, ...
++ int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks);
++ struct ebt_table_info *private;// the data used by the kernel
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out, struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++/* fn returns 0 to continue iteration */
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_arp.h Wed Apr 3 18:50:31 2002
+@@ -0,0 +1,25 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_ip.h Wed Apr 3 18:50:31 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_log.h Wed Apr 3 18:50:31 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/netfilter_bridge/ebt_nat.h Wed Apr 3 18:50:31 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre1/include/linux/br_db.h Wed Apr 3 20:45:01 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff
new file mode 100644
index 0000000..23ecce4
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre2_vs_2.4.18.diff
@@ -0,0 +1,2752 @@
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre2/net/Makefile Wed Apr 3 21:50:43 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Sun Apr 14 15:19:26 2002
++++ ebt2.0pre2/net/Config.in Wed Apr 3 21:50:43 2002
+@@ -60,6 +60,7 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++ source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/Makefile Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,23 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/Config.in Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,12 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/br_db.c Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtable_filter.c Sat Apr 13 21:51:47 2002
+@@ -0,0 +1,90 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN, -200},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD, -200},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT, 200}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtable_nat.c Sat Apr 13 21:54:58 2002
+@@ -0,0 +1,153 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT, 100},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING, -100},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,300},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING, -300},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,200 + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD, 200 + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_arp.c Sat Apr 13 21:45:34 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_ip.c Sat Apr 13 21:47:18 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_log.c Sat Apr 13 21:49:45 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebt_nat.c Sat Apr 13 21:51:32 2002
+@@ -0,0 +1,118 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++__u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (skb_cloned(*pskb)) {
++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++ if (!nskb)
++ return EBT_DROP;
++ if ((*pskb)->sk)
++ skb_set_owner_w(nskb, (*pskb)->sk);
++ kfree_skb(*pskb);
++ *pskb = nskb;
++ }
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return EBT_ACCEPT;
++}
++
++__u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (skb_cloned(*pskb)) {
++ struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
++
++ if (!nskb)
++ return EBT_DROP;
++ if ((*pskb)->sk)
++ skb_set_owner_w(nskb, (*pskb)->sk);
++ kfree_skb(*pskb);
++ *pskb = nskb;
++ }
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return EBT_ACCEPT;
++}
++
++int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ return 0;
++}
++
++int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)
++ return -EINVAL;
++ return 0;
++}
++
++struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/net/bridge/netfilter/ebtables.c Sat Apr 13 21:36:18 2002
+@@ -0,0 +1,1168 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size - sizeof(struct ebt_entry_watcher), c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size - sizeof(struct ebt_entry_match), c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ ) {
++ if ( (point->bitmask & EBT_SOURCEMAC) &&
++ FWINV(!!memcmp(point->sourcemac,
++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++ EBT_ISOURCE) )
++ goto letscontinue;
++
++ if ( (point->bitmask & EBT_DESTMAC) &&
++ FWINV(!!memcmp(point->destmac,
++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++ EBT_IDEST) )
++ goto letscontinue;
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size - sizeof(*m)) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size - sizeof(*w)) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++ "bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size - sizeof(*m));
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size - sizeof(*w));
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)
++ list_named_find(&ebt_targets, t->u.name))) {
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >=
++ NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data,
++ t->target_size - sizeof(*t)) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size - sizeof(*t));
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry,
++ NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebtables.h Sat Apr 13 16:06:20 2002
+@@ -0,0 +1,318 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++/* [gs]etsockopt numbers */
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of this struct + size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of this struct + size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of this struct + size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ __u8 verdict;
++};
++
++/* one entry */
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of it?
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr, const struct ebt_entry *e,
++ void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of the counters bolonging to a chain
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info, unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_arp.h Sat Apr 13 16:11:46 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_ip.h Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_log.h Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/netfilter_bridge/ebt_nat.h Wed Apr 3 21:50:43 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre2/include/linux/br_db.h Sat Apr 13 22:43:11 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff
new file mode 100644
index 0000000..72e80fe
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre3_vs_2.4.18.diff
@@ -0,0 +1,3108 @@
+ebtables-v2.0pre3 - 27 April
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/bridge/br_private.h Sat Apr 27 21:52:48 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre3/include/linux/if_bridge.h Sat Apr 27 21:39:14 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/core/dev.c Sat Apr 27 21:05:16 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/bridge/br_input.c Sat Apr 27 21:05:16 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/netsyms.c Sat Apr 27 21:05:16 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre3/include/linux/netfilter_bridge.h Sat Apr 27 21:53:07 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre3/net/Makefile Sat Apr 27 21:05:16 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Sat Apr 27 22:55:42 2002
++++ ebt2.0pre3/net/Config.in Sat Apr 27 21:05:16 2002
+@@ -60,6 +60,7 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++ source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/Makefile Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/Config.in Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/br_db.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_filter.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,93 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_nat.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,156 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtable_broute.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,80 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, {},
++ 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_redirect.c Sat Apr 27 22:48:52 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_arp.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_ip.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_log.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebt_nat.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,106 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/net/bridge/netfilter/ebtables.c Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,1180 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ if ( (point->bitmask & EBT_SOURCEMAC) &&
++ FWINV(!!memcmp(point->sourcemac,
++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++ EBT_ISOURCE) )
++ goto letscontinue;
++
++ if ( (point->bitmask & EBT_DESTMAC) &&
++ FWINV(!!memcmp(point->destmac,
++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++ EBT_IDEST) )
++ goto letscontinue;
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++ "bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)
++ list_named_find(&ebt_targets, t->u.name))) {
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >=
++ NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data,
++ t->target_size) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry,
++ NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebtables.h Sat Apr 27 21:54:11 2002
+@@ -0,0 +1,330 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ __u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ __u8 in[IFNAMSIZ];
++ // the logical in-dev
++ __u8 logical_in[IFNAMSIZ];
++ // the physical out-dev
++ __u8 out[IFNAMSIZ];
++ // the logical out-dev
++ __u8 logical_out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of it?
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of the counters bolonging to a chain
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_arp.h Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_ip.h Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_log.h Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/netfilter_bridge/ebt_nat.h Sat Apr 27 21:05:16 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre3/include/linux/br_db.h Sat Apr 27 21:54:40 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff
new file mode 100644
index 0000000..e50fba4
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre4_vs_2.4.18.diff
@@ -0,0 +1,3122 @@
+ebtables-v2.0pre4 - 29 April
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/bridge/br_private.h Mon Apr 29 19:54:04 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre4/include/linux/if_bridge.h Mon Apr 29 19:54:04 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/core/dev.c Mon Apr 29 19:54:04 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/bridge/br_input.c Mon Apr 29 19:54:04 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/netsyms.c Mon Apr 29 19:54:04 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre4/include/linux/netfilter_bridge.h Mon Apr 29 19:54:04 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre4/net/Makefile Mon Apr 29 19:54:04 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Mon Apr 29 19:58:13 2002
++++ ebt2.0pre4/net/Config.in Mon Apr 29 19:54:04 2002
+@@ -60,6 +60,7 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++ source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/Makefile Mon Apr 29 19:54:04 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/Config.in Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/br_db.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_filter.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,93 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_nat.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,156 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtable_broute.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,80 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, {},
++ 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_redirect.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_arp.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_ip.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_log.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebt_nat.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,106 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/net/bridge/netfilter/ebtables.c Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,1180 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ if ( (point->bitmask & EBT_SOURCEMAC) &&
++ FWINV(!!memcmp(point->sourcemac,
++ ((**pskb).mac.ethernet)->h_source, ETH_ALEN),
++ EBT_ISOURCE) )
++ goto letscontinue;
++
++ if ( (point->bitmask & EBT_DESTMAC) &&
++ FWINV(!!memcmp(point->destmac,
++ ((**pskb).mac.ethernet)->h_dest, ETH_ALEN),
++ EBT_IDEST) )
++ goto letscontinue;
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++ "bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)
++ list_named_find(&ebt_targets, t->u.name))) {
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >=
++ NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data,
++ t->target_size) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry,
++ NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebtables.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,330 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ __u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ __u8 in[IFNAMSIZ];
++ // the logical in-dev
++ __u8 logical_in[IFNAMSIZ];
++ // the physical out-dev
++ __u8 out[IFNAMSIZ];
++ // the logical out-dev
++ __u8 logical_out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of it?
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of the counters bolonging to a chain
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_arp.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_ip.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_log.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_nat.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/netfilter_bridge/ebt_redirect.h Mon Apr 29 20:00:05 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre4/include/linux/br_db.h Mon Apr 29 19:54:05 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff
new file mode 100644
index 0000000..9537244
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre5_vs_2.4.18.diff
@@ -0,0 +1,3133 @@
+ebtables-v2.0pre5 - 01 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Fri May 3 21:15:24 2002
++++ ebt2.0pre5/net/bridge/br_private.h Wed May 1 15:58:13 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre5/include/linux/if_bridge.h Wed May 1 15:42:31 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/core/dev.c Wed May 1 14:51:46 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Fri May 3 21:15:24 2002
++++ ebt2.0pre5/net/bridge/br_input.c Wed May 1 14:51:46 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/netsyms.c Wed May 1 14:51:46 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre5/include/linux/netfilter_bridge.h Wed May 1 15:58:31 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre5/net/Makefile Wed May 1 14:51:46 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Fri May 3 21:15:24 2002
++++ ebt2.0pre5/net/Config.in Wed May 1 14:51:46 2002
+@@ -60,6 +60,7 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++ source net/bridge/netfilter/Config.in
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/Makefile Wed May 1 14:51:46 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/Config.in Wed May 1 14:51:46 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/br_db.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_filter.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,93 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_nat.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,156 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtable_broute.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,80 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, {},
++ 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_redirect.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_arp.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_ip.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_log.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebt_nat.c Wed May 1 14:51:46 2002
+@@ -0,0 +1,106 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/net/bridge/netfilter/ebtables.c Fri May 3 20:28:46 2002
+@@ -0,0 +1,1189 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ char hlpmac[6];
++ int j;
++
++ if (point->bitmask & EBT_SOURCEMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_source[j] & point->sourcemsk[j];
++ if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++ ETH_ALEN), EBT_ISOURCE) )
++ goto letscontinue;
++ }
++
++ if (point->bitmask & EBT_DESTMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_dest[j] & point->destmsk[j];
++ if (FWINV(!!memcmp(point->destmac, hlpmac,
++ ETH_ALEN), EBT_IDEST) )
++ goto letscontinue;
++ }
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++ "bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)
++ list_named_find(&ebt_targets, t->u.name))) {
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >=
++ NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data,
++ t->target_size) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry,
++ NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebtables.h Thu May 2 19:01:09 2002
+@@ -0,0 +1,332 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ __u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ __u8 in[IFNAMSIZ];
++ // the logical in-dev
++ __u8 logical_in[IFNAMSIZ];
++ // the physical out-dev
++ __u8 out[IFNAMSIZ];
++ // the logical out-dev
++ __u8 logical_out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 sourcemsk[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ __u8 destmsk[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of it?
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of the counters bolonging to a chain
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_arp.h Wed May 1 14:51:46 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_ip.h Wed May 1 14:51:46 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_log.h Wed May 1 14:51:46 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_nat.h Wed May 1 14:51:46 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/netfilter_bridge/ebt_redirect.h Wed May 1 14:51:46 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre5/include/linux/br_db.h Wed May 1 16:00:00 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff
new file mode 100644
index 0000000..7aa458c
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre6_vs_2.4.18.diff
@@ -0,0 +1,3135 @@
+ebtables-v2.0pre5 - 01 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/bridge/br_private.h Mon May 20 11:57:27 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre6/include/linux/if_bridge.h Mon May 20 11:57:27 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/core/dev.c Mon May 20 11:57:27 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/bridge/br_input.c Mon May 20 11:57:27 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/netsyms.c Mon May 20 11:57:27 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre6/include/linux/netfilter_bridge.h Mon May 20 11:57:27 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre6/net/Makefile Mon May 20 11:57:27 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -26,6 +27,12 @@
+ endif
+ endif
+
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
++endif
++endif
++
+ subdir-$(CONFIG_KHTTPD) += khttpd
+ subdir-$(CONFIG_PACKET) += packet
+ subdir-$(CONFIG_NET_SCHED) += sched
+--- linux/net/Config.in Mon May 20 12:28:17 2002
++++ ebt2.0pre6/net/Config.in Mon May 20 11:58:15 2002
+@@ -60,6 +60,9 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++ source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/Makefile Mon May 20 11:57:27 2002
+@@ -0,0 +1,24 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/Config.in Mon May 20 11:57:27 2002
+@@ -0,0 +1,14 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/br_db.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_filter.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,93 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_nat.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,156 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtable_broute.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,80 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, {},
++ 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_redirect.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_arp.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_ip.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_log.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebt_nat.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,106 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/net/bridge/netfilter/ebtables.c Mon May 20 11:57:27 2002
+@@ -0,0 +1,1189 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ char hlpmac[6];
++ int j;
++
++ if (point->bitmask & EBT_SOURCEMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_source[j] & point->sourcemsk[j];
++ if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++ ETH_ALEN), EBT_ISOURCE) )
++ goto letscontinue;
++ }
++
++ if (point->bitmask & EBT_DESTMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_dest[j] & point->destmsk[j];
++ if (FWINV(!!memcmp(point->destmac, hlpmac,
++ ETH_ALEN), EBT_IDEST) )
++ goto letscontinue;
++ }
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
++ BUGPRINT("entry offsets point too far\n");
++ return -EINVAL;
++ }
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
++ "bitmask for an entry\n");
++ return -EINVAL;
++ }
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(target = (struct ebt_target *)
++ list_named_find(&ebt_targets, t->u.name))) {
++ ret = -ENOENT;
++ up(&ebt_mutex);
++ goto cleanup_watchers;
++ }
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict >=
++ NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hook, e, t->data,
++ t->target_size) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k;
++ int ret;
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ newinfo->hook_entry[i] = NULL;
++ newinfo->counter_entry[i] = 0;
++ }
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))){
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ if (ret != 0) {
++ BUGPRINT("ebt_check_entry gave fault back\n");
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size, ebt_cleanup_entry, &i);
++ }
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ ret = down_interruptible(&ebt_mutex);
++
++ if (ret != 0)
++ goto free_cleanup;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ ret = -ENOENT;
++ // give some help to the poor user
++ print_string("The table is not present, try insmod\n");
++ goto free_unlock;
++ }
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++free_cleanup:
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_tmp;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, hlp.name))) {
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.counter_entry, info->counter_entry,
++ NF_BR_NUMHOOKS * sizeof(int));
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++
++ if (!(t = (struct ebt_table *)list_named_find(&ebt_tables, tmp.name))) {
++ print_string("Table not found, try insmod\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebtables.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,332 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++#define EBT_ACCEPT 0
++#define EBT_DROP 1
++#define EBT_CONTINUE 2
++#define NUM_STANDARD_TARGETS 3
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // one standard (accept or drop) per hook
++ __u8 policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ __u8 verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ __u8 in[IFNAMSIZ];
++ // the logical in-dev
++ __u8 logical_in[IFNAMSIZ];
++ // the physical out-dev
++ __u8 out[IFNAMSIZ];
++ // the logical out-dev
++ __u8 logical_out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 sourcemsk[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ __u8 destmsk[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of it?
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ __u8 (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // how many counters in front of the counters bolonging to a chain
++ unsigned int counter_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_arp.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_ip.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_log.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_nat.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/netfilter_bridge/ebt_redirect.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
++ __u8 target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null Sat May 18 12:04:21 2002
++++ ebt2.0pre6/include/linux/br_db.h Mon May 20 11:57:27 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff
new file mode 100644
index 0000000..963d6a9
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre7_vs_2.4.18.diff
@@ -0,0 +1,3285 @@
+ebtables-v2.0pre7 - 30 May
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/bridge/br_private.h Thu May 30 19:59:39 2002
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre7/include/linux/if_bridge.h Thu May 30 19:45:33 2002
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/core/dev.c Thu May 30 19:03:46 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/bridge/br_input.c Thu May 30 19:03:46 2002
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/netsyms.c Thu May 30 19:03:46 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre7/include/linux/netfilter_bridge.h Thu May 30 19:59:54 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre7/net/Makefile Thu May 30 19:03:46 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Fri May 31 18:16:26 2002
++++ ebt2.0pre7/net/Config.in Thu May 30 19:03:46 2002
+@@ -60,6 +60,9 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++ source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/Makefile Thu May 30 19:03:57 2002
+@@ -0,0 +1,25 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_NAT) += ebt_nat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/Config.in Thu May 30 19:03:57 2002
+@@ -0,0 +1,15 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat target support' CONFIG_BRIDGE_EBT_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/br_db.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_filter.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,93 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_nat.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,156 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0},
++ {0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
++ 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtable_broute.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,80 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, {},
++ 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_redirect.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_arp.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_ip.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_vlan.c Thu May 30 19:03:57 2002
+@@ -0,0 +1,124 @@
++/*
++ * ebt_vlan kernelspace
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Nick Fedchik <nick@fedchik.org.ua>
++ *
++ * May, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++ struct vlan_ethhdr *vlanethhdr =
++ (struct vlan_ethhdr *) skb->mac.raw;
++ unsigned short v_id;
++ unsigned short v_prio;
++
++ /*
++ * Calculate 802.1Q VLAN ID and Priority
++ * Reserved one bit (13) for CFI
++ */
++ v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
++ v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++
++ /*
++ * Checking VLANs
++ */
++ if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */
++ if (!((infostuff->id == v_id)
++ ^ !!(infostuff->invflags & EBT_VLAN_ID)))
++ return 1;
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched ID=%s%d (mask=%X)\n",
++ (infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
++ infostuff->id,
++ (unsigned char) infostuff->bitmask);
++ }
++ /*
++ * Checking Priority
++ */
++ if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */
++ if (!( (infostuff->prio == v_prio)
++ ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
++ return 1; /* missed */
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++ (infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
++ infostuff->prio,
++ (unsigned char) infostuff->bitmask);
++ }
++ /*
++ * rule matched
++ */
++ return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel,
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data,
++ unsigned int datalen)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++ if (datalen != sizeof (struct ebt_vlan_info))
++ return -EINVAL;
++
++ if (e->ethproto != __constant_htons (ETH_P_8021Q))
++ return -EINVAL;
++
++ if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static struct ebt_match filter_vlan = {
++ {NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++ NULL,
++ THIS_MODULE
++};
++
++static int __init init (void)
++{
++ printk (KERN_INFO
++ "ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: 802.1Q matching debug is on\n");
++ return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++ ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_LICENSE ("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_log.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebt_nat.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,106 @@
++/*
++ * ebt_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hooknr != NF_BR_POST_ROUTING)
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
++ (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target >= NUM_STANDARD_TARGETS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ int ret;
++ ret = ebt_register_target(&snat);
++ if (ret != 0)
++ return ret;
++ ret = ebt_register_target(&dnat);
++ if (ret == 0)
++ return 0;
++ ebt_unregister_target(&snat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre7/net/bridge/netfilter/ebtables.c Thu May 30 19:03:46 2002
+@@ -0,0 +1,1189 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ __u8 verdict;
++
++ read_lock_bh(&table->lock);
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ counter_base = table->private->counters +
++ cpu_number_map(smp_processor_id()) * table->private->nentries +
++ table->private->counter_entry[hook];
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ for (i = 0; i < nentries; i++) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ char hlpmac[6];
++ int j;
++
++ if (point->bitmask & EBT_SOURCEMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_source[j] & point->sourcemsk[j];
++ if (FWINV(!!memcmp(point->sourcemac, hlpmac,
++ ETH_ALEN), EBT_ISOURCE) )
++ goto letscontinue;
++ }
++
++ if (point->bitmask & EBT_DESTMAC) {
++ for (j = 0; j < 6; j++)
++ hlpmac[j] = ((**pskb).mac.ethernet)->
++ h_dest[j] & point->destmsk[j];
++ if (FWINV(!!memcmp(point->destmac, hlpmac,
++ ETH_ALEN), EBT_IDEST) )
++ goto letscontinue;
++ }
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict != EBT_CONTINUE) {
++ read_unlock_bh(&table->lock);
++ BUGPRINT("Illegal target while "
++ "firewalling!!\n");
++ // Try not to get oopsen
++ return NF_DROP;
++ }
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ }
++
++ if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ if (!(match = (struct ebt_match *)
++ list_named_find(&ebt_matches, m->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ m->u.match = match;
++ if (match->check &&
++ match->check(name, hook, e, m->data,
++ m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hook, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return -EFAULT;
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (!(watcher = (struct ebt_watcher *)
++ list_named_find(&ebt_watchers, w->u.name))) {
++ up(&ebt_mutex);
++ return -ENOENT;
++ }
++ w->u.watcher = watcher;
++ if (watcher->check &&
++ watcher->check(name, hook, e, w->data,
++ w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ up(&ebt_mutex);
++ return -EINVAL;
++ }
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ if (i != NF_BR_NUMHOOKS) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ newinfo->counter_entry[i] = *totalcnt;
+