summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff3637
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff1155
-rw-r--r--userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff2745
-rw-r--r--userspace/patches/zipped/ebtables-v2.0pre8.tar.gzbin0 -> 41432 bytes
4 files changed, 7537 insertions, 0 deletions
diff --git a/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff b/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff
new file mode 100644
index 0000000..ef6927b
--- /dev/null
+++ b/kernel/patches/base-patches/ebtables-v2.0pre9_vs_2.4.18.diff
@@ -0,0 +1,3637 @@
+ebtables-v2.0pre9 - 27 June
+
+*** modifications for brouter support ***
+
+--- linux/net/bridge/br_private.h Thu Jun 27 19:21:12 2002
++++ ebt2.0pre9/net/bridge/br_private.h Thu Jun 27 19:11:50 2002
+@@ -4,7 +4,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -170,7 +170,7 @@
+
+ /* br_input.c */
+ extern int br_handle_frame_finish(struct sk_buff *skb);
+-extern void br_handle_frame(struct sk_buff *skb);
++extern int br_handle_frame(struct sk_buff *skb);
+
+ /* br_ioctl.c */
+ extern void br_call_ioctl_atomic(void (*fn)(void));
+--- linux/include/linux/if_bridge.h Thu Nov 22 20:47:12 2001
++++ ebt2.0pre9/include/linux/if_bridge.h Thu Jun 27 19:11:50 2002
+@@ -4,7 +4,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -102,8 +102,13 @@
+ struct net_bridge_port;
+
+ extern int (*br_ioctl_hook)(unsigned long arg);
+-extern void (*br_handle_frame_hook)(struct sk_buff *skb);
+-
++extern int (*br_handle_frame_hook)(struct sk_buff *skb);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++extern unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *));
++#endif
+ #endif
+
+ #endif
+--- linux/net/core/dev.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/core/dev.c Thu Jun 27 19:11:50 2002
+@@ -1384,7 +1384,14 @@
+ }
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+-void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++unsigned int (*broute_decision) (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *)) = NULL;
++#endif
+ #endif
+
+ static __inline__ int handle_bridge(struct sk_buff *skb,
+@@ -1394,14 +1401,14 @@
+
+ if (pt_prev) {
+ if (!pt_prev->data)
+- ret = deliver_to_old_ones(pt_prev, skb, 0);
++ deliver_to_old_ones(pt_prev, skb, 0);
+ else {
+ atomic_inc(&skb->users);
+- ret = pt_prev->func(skb, skb->dev, pt_prev);
++ pt_prev->func(skb, skb->dev, pt_prev);
+ }
+ }
+
+- br_handle_frame_hook(skb);
++ ret = br_handle_frame_hook(skb);
+ return ret;
+ }
+
+@@ -1479,9 +1486,10 @@
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ if (skb->dev->br_port != NULL &&
+ br_handle_frame_hook != NULL) {
+- handle_bridge(skb, pt_prev);
+- dev_put(rx_dev);
+- continue;
++ if (handle_bridge(skb, pt_prev) == 0) {
++ dev_put(rx_dev);
++ continue;
++ }
+ }
+ #endif
+
+--- linux/net/bridge/br_input.c Thu Jun 27 19:21:12 2002
++++ ebt2.0pre9/net/bridge/br_input.c Thu Jun 27 19:11:50 2002
+@@ -5,7 +5,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -19,7 +19,10 @@
+ #include <linux/if_bridge.h>
+ #include <linux/netfilter_bridge.h>
+ #include "br_private.h"
+-
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++#include <linux/netfilter.h>
++#endif
+ unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+
+ static int br_pass_frame_up_finish(struct sk_buff *skb)
+@@ -112,7 +115,7 @@
+ return 0;
+ }
+
+-void br_handle_frame(struct sk_buff *skb)
++int br_handle_frame(struct sk_buff *skb)
+ {
+ struct net_bridge *br;
+ unsigned char *dest;
+@@ -146,23 +149,30 @@
+ goto handle_special_frame;
+
+ if (p->state == BR_STATE_FORWARDING) {
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++ if (broute_decision && broute_decision(NF_BR_BROUTING, &skb,
++ skb->dev, NULL, NULL) == NF_DROP)
++ return -1;
++#endif
+ NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ br_handle_frame_finish);
+ read_unlock(&br->lock);
+- return;
++ return 0;
+ }
+
+ err:
+ read_unlock(&br->lock);
+ err_nolock:
+ kfree_skb(skb);
+- return;
++ return 0;
+
+ handle_special_frame:
+ if (!dest[5]) {
+ br_stp_handle_bpdu(skb);
+- return;
++ return 0;
+ }
+
+ kfree_skb(skb);
++ return 0;
+ }
+--- linux/net/netsyms.c Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/netsyms.c Thu Jun 27 19:11:50 2002
+@@ -228,6 +228,10 @@
+
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ EXPORT_SYMBOL(br_handle_frame_hook);
++#if defined(CONFIG_BRIDGE_EBT_BROUTE) || \
++ defined(CONFIG_BRIDGE_EBT_BROUTE_MODULE)
++EXPORT_SYMBOL(broute_decision);
++#endif
+ #ifdef CONFIG_INET
+ EXPORT_SYMBOL(br_ioctl_hook);
+ #endif
+--- linux/include/linux/netfilter_bridge.h Tue Jun 12 04:15:27 2001
++++ ebt2.0pre9/include/linux/netfilter_bridge.h Thu Jun 27 19:11:50 2002
+@@ -18,7 +18,19 @@
+ #define NF_BR_LOCAL_OUT 3
+ /* Packets about to hit the wire. */
+ #define NF_BR_POST_ROUTING 4
+-#define NF_BR_NUMHOOKS 5
++/* Not really a hook, but used for the ebtables broute table */
++#define NF_BR_BROUTING 5
++#define NF_BR_NUMHOOKS 6
+
++enum nf_br_hook_priorities {
++ NF_BR_PRI_FIRST = INT_MIN,
++ NF_BR_PRI_FILTER_BRIDGED = -200,
++ NF_BR_PRI_FILTER_OTHER = 200,
++ NF_BR_PRI_NAT_DST_BRIDGED = -300,
++ NF_BR_PRI_NAT_DST_OTHER = 100,
++ NF_BR_PRI_NAT_SRC_BRIDGED = -100,
++ NF_BR_PRI_NAT_SRC_OTHER = 300,
++ NF_BR_PRI_LAST = INT_MAX,
++};
+
+ #endif
+
+*** modifications for ebtables compilation ***
+
+--- linux/net/Makefile Mon Feb 25 20:38:14 2002
++++ ebt2.0pre9/net/Makefile Thu Jun 27 19:11:50 2002
+@@ -7,7 +7,8 @@
+
+ O_TARGET := network.o
+
+-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched
++mod-subdirs := bridge/netfilter ipv4/netfilter ipv6/netfilter ipx irda \
++ bluetooth atm netlink sched
+ export-objs := netsyms.o
+
+ subdir-y := core ethernet
+@@ -23,6 +24,12 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER) += ipv6/netfilter
++endif
++endif
++
++ifneq ($(CONFIG_BRIDGE),n)
++ifneq ($CONFIG_BRIDGE),)
++subdir-$(CONFIG_BRIDGE) += bridge/netfilter
+ endif
+ endif
+
+--- linux/net/Config.in Thu Jun 27 19:21:12 2002
++++ ebt2.0pre9/net/Config.in Thu Jun 27 19:11:50 2002
+@@ -60,6 +60,9 @@
+ source net/decnet/Config.in
+ fi
+ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
++if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
++ source net/bridge/netfilter/Config.in
++fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_BRIDGE" != "n" -a "$CONFIG_NETFILTER" != "n" ]; then
+ bool ' netfilter (firewalling) support' CONFIG_BRIDGE_NF
+
+*** new ebtables files ***
+
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/Makefile Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,26 @@
++#
++# Makefile for the netfilter modules on top of bridging.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definition is now in the main makefile...
++
++O_TARGET := netfilter.o
++
++export-objs = ebtables.o
++
++obj-$(CONFIG_BRIDGE_EBT) += ebtables.o
++obj-$(CONFIG_BRIDGE_EBT_T_FILTER) += ebtable_filter.o
++obj-$(CONFIG_BRIDGE_EBT_T_NAT) += ebtable_nat.o
++obj-$(CONFIG_BRIDGE_EBT_BROUTE) += ebtable_broute.o
++obj-$(CONFIG_BRIDGE_DB) += br_db.o
++obj-$(CONFIG_BRIDGE_EBT_IPF) += ebt_ip.o
++obj-$(CONFIG_BRIDGE_EBT_ARPF) += ebt_arp.o
++obj-$(CONFIG_BRIDGE_EBT_VLANF) += ebt_vlan.o
++obj-$(CONFIG_BRIDGE_EBT_LOG) += ebt_log.o
++obj-$(CONFIG_BRIDGE_EBT_SNAT) += ebt_snat.o
++obj-$(CONFIG_BRIDGE_EBT_DNAT) += ebt_dnat.o
++obj-$(CONFIG_BRIDGE_EBT_REDIRECT) += ebt_redirect.o
++include $(TOPDIR)/Rules.make
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/Config.in Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,16 @@
++#
++# Bridge netfilter configuration
++#
++dep_tristate ' Bridge: ebtables' CONFIG_BRIDGE_EBT $CONFIG_BRIDGE
++dep_tristate ' ebt: filter table support' CONFIG_BRIDGE_EBT_T_FILTER $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: nat table support' CONFIG_BRIDGE_EBT_T_NAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: broute table support' CONFIG_BRIDGE_EBT_BROUTE $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: LOG support' CONFIG_BRIDGE_EBT_LOG $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: IP filter support' CONFIG_BRIDGE_EBT_IPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: ARP filter support' CONFIG_BRIDGE_EBT_ARPF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: 802.1Q VLAN filter support (EXPERIMENTAL)' CONFIG_BRIDGE_EBT_VLANF $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: snat target support' CONFIG_BRIDGE_EBT_SNAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: dnat target support' CONFIG_BRIDGE_EBT_DNAT $CONFIG_BRIDGE_EBT
++dep_tristate ' ebt: redirect target support' CONFIG_BRIDGE_EBT_REDIRECT $CONFIG_BRIDGE_EBT
++dep_tristate ' Bridge: ethernet database' CONFIG_BRIDGE_DB $CONFIG_BRIDGE
++
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/br_db.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,357 @@
++/*
++ * bridge ethernet protocol database
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.c, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/br_db.h>
++#include <linux/socket.h> /* PF_BRIDGE */
++#include <linux/spinlock.h> /* rwlock_t */
++#include <asm/errno.h>
++#include <asm/uaccess.h> /* copy_[to,from]_user */
++#include <linux/smp.h> /* multiprocessors */
++
++#define BUGPRINT(format, args...) printk("kernel msg: brdb bug: please report to author: "format, ## args)
++/*#define BUGPRINT(format, args...)*/
++#define MEMPRINT(format, args...) printk("kernel msg: brdb : out of memory: "format, ## args)
++/*#define MEMPRINT(format, args...)*/
++
++/* database variables */
++static __u16 allowdb = BRDB_NODB;
++static struct brdb_dbentry **flowdb = NULL;
++static unsigned int *dbsize;
++static unsigned int *dbnum;
++/* database lock */
++static rwlock_t brdb_dblock;
++
++static inline int brdb_dev_check(char *entry, const struct net_device *device){
++ if (*entry == '\0') return 0;
++ if (!device) return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++static inline int brdb_proto_check(unsigned int a, unsigned int b){
++ if (a == b || ( a == IDENTIFY802_3 && ntohs(b) < 1536 )) return 0;
++ return 1;
++}
++
++static unsigned int maintaindb (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ struct brdb_dbentry *hlp;
++ int i, cpunr;
++ unsigned short ethproto = ((**pskb).mac.ethernet)->h_proto;
++
++ cpunr = cpu_number_map(smp_processor_id());
++
++ read_lock_bh(&brdb_dblock);
++
++ if (allowdb == BRDB_NODB) {// must be after readlock
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp = flowdb[cpunr];
++ /* search for existing entry */
++ for (i = 0; i < dbnum[cpunr]; i++) {
++ if (hlp->hook == hook && !brdb_proto_check(hlp->ethproto, ethproto) &&
++ !brdb_dev_check(hlp->in, in) && !brdb_dev_check(hlp->out, out)) {
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ hlp++;
++ }
++ /* add new entry to database */
++ if (dbnum[cpunr] == dbsize[cpunr]) {
++ dbsize[cpunr] *= 2;
++ if ( !( hlp = (struct brdb_dbentry *) vmalloc(dbsize[cpunr] * sizeof(struct brdb_dbentry)) ) ) {
++ dbsize[cpunr] /= 2;
++ MEMPRINT("maintaindb && nomemory\n");
++ read_unlock_bh(&brdb_dblock);
++ return NF_ACCEPT;
++ }
++ memcpy(hlp, flowdb[cpunr], dbnum[cpunr] * sizeof(struct brdb_dbentry));
++ vfree(flowdb[cpunr]);
++ flowdb[cpunr] = hlp;
++ }
++
++ hlp = flowdb[cpunr] + dbnum[cpunr];
++ hlp->hook = hook;
++ if (in)
++ strncpy(hlp->in, in->name, IFNAMSIZ);
++ else
++ hlp->in[0] = '\0';
++ if (out)
++ strncpy(hlp->out, out->name, IFNAMSIZ);
++ else
++ hlp->out[0] = '\0';
++ if (ntohs(ethproto) < 1536)
++ hlp->ethproto = IDENTIFY802_3;
++ else
++ hlp->ethproto = ethproto;
++ dbnum[cpunr]++;
++
++ read_unlock_bh(&brdb_dblock);
++
++ return NF_ACCEPT;
++}
++
++static int copy_db(void *user, int *len)
++{
++ int i, j, nentries = 0, ret;
++ struct brdb_dbentry *begin, *end1, *end2, *point, *point2;
++
++ write_lock_bh(&brdb_dblock);
++ for (i = 0; i < smp_num_cpus; i++)
++ nentries += dbnum[i];
++ if (*len > nentries)
++ return -EINVAL;
++
++ if ( !(begin = (struct brdb_dbentry *) vmalloc((*len) * sizeof(struct brdb_dbentry))) )
++ return -ENOMEM;
++ memcpy(begin, flowdb[0], dbnum[0] * sizeof(struct brdb_dbentry));
++ end1 = begin + dbnum[0];
++ for (i = 1; i < smp_num_cpus; i++) {/* cycle databases per cpu */
++ point2 = flowdb[i];
++ end2 = end1;
++ for (j = 0; j < dbnum[i]; j++) {/* cycle entries of a cpu's database (point2) */
++ for (point = begin; point != end2; point++)/* cycle different entries we found so far */
++ if (point->hook == point2->hook && !strncmp(point->in, point2->in, IFNAMSIZ) &&
++ !strncmp(point->out, point2->out, IFNAMSIZ) && point->ethproto == point2->ethproto)
++ goto out;/* already exists in a database of another cpu */
++
++ memcpy(end1, point2, sizeof(struct brdb_dbentry));
++ end1++;
++out:
++ point2++;
++ }
++ }
++ write_unlock_bh(&brdb_dblock);
++ i = (int)( (char *)end1 - (char *)begin);
++ *len = i < *len ? i : *len;
++ if (copy_to_user(user, begin, *len * sizeof(struct brdb_dbentry)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ vfree(begin);
++ return ret;
++}
++
++static int switch_nodb(void){
++ int i;
++
++ if (!flowdb)
++ BUGPRINT("switch_nodb && !flowdb\n");
++ for (i = 0; i < smp_num_cpus; i++)
++ vfree(flowdb[i]);
++ vfree(flowdb);
++ if (!dbsize)
++ BUGPRINT("switch_nodb && !dbsize\n");
++ vfree(dbsize);
++ if (!dbnum)
++ BUGPRINT("switch_nodb && !dbnum\n");
++ vfree(dbnum);
++ flowdb = NULL;
++ allowdb = BRDB_NODB;
++ return 0;
++}
++
++static int switch_db(void)
++{
++ int i, j;
++
++ if (flowdb) BUGPRINT("switch_db && flowdb\n");
++ if ( !(flowdb = (struct brdb_dbentry **) vmalloc(smp_num_cpus * sizeof(struct brdb_dbentry *))) ) {
++ MEMPRINT("switch_db && nomemory\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < smp_num_cpus; i++)
++ if ( !(flowdb[i] = (struct brdb_dbentry *) vmalloc(INITIAL_DBSIZE * sizeof(struct brdb_dbentry))) )
++ goto sw_free1;
++ else
++ memset(flowdb[i], 0, INITIAL_DBSIZE * sizeof(struct brdb_dbentry));
++
++ if ( !(dbnum = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free2;
++
++ if ( !(dbsize = (int*) vmalloc(smp_num_cpus * sizeof(int))) )
++ goto sw_free3;
++
++ for (i = 0; i < smp_num_cpus; i++) {
++ dbnum[i] = 0;
++ dbsize[i] = INITIAL_DBSIZE;
++ }
++ allowdb = BRDB_DB;
++ return 0;
++
++sw_free3:
++ MEMPRINT("switch_db && nomemory2\n");
++ vfree(dbnum);
++ dbnum = NULL;
++sw_free2:
++ MEMPRINT("switch_db && nomemory3\n");
++sw_free1:
++ MEMPRINT("switch_db && nomemory4\n");
++ for (j = 0; j<i; j++)
++ vfree(flowdb[j]);
++ vfree(flowdb);
++ allowdb = BRDB_NODB;
++ return -ENOMEM;
++}
++
++static int
++do_brdb_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
++{
++ int ret;
++ __u16 adb;
++ switch(cmd) {
++ case BRDB_SO_SET_ALLOWDB:
++ if (len != sizeof(__u16)) {
++ ret = -EINVAL;
++ break;
++ }
++ if (copy_from_user(&adb, user, len) != 0) {
++ ret = -EFAULT;
++ break;
++ }
++ if (adb != BRDB_DB && adb != BRDB_NODB) {
++ ret = -EINVAL;
++ break;
++ }
++ write_lock_bh(&brdb_dblock);
++ if (adb == allowdb) {
++ ret = 0;
++ write_unlock_bh(&brdb_dblock);
++ break;
++ }
++ if (allowdb == BRDB_DB)
++ ret = switch_nodb();
++ else
++ ret = switch_db();
++ write_unlock_bh(&brdb_dblock);
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int
++do_brdb_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ struct brdb_dbinfo help2;
++ int i, ret;
++ switch(cmd) {
++ case BRDB_SO_GET_DBINFO:
++ if (sizeof(struct brdb_dbinfo) != *len)
++ return -EINVAL;
++ write_lock_bh(&brdb_dblock);
++ /* 0 == no database
++ * i-1 == number of entries (if database)
++ */
++ if (allowdb == BRDB_NODB)
++ help2.nentries = 0;
++ else {
++ help2.nentries = 1;
++ for (i = 0; i < smp_num_cpus; i++)
++ help2.nentries += dbnum[i];
++ }
++ write_unlock_bh(&brdb_dblock);
++ if (copy_to_user(user, &help2, sizeof(help2)) != 0)
++ ret = -EFAULT;
++ else
++ ret = 0;
++ break;
++
++ case BRDB_SO_GET_DB:
++ if (*len == 0 || allowdb == BRDB_NODB)
++ return -EINVAL;
++ ret = copy_db(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops brdb_sockopts
++= { { NULL, NULL }, PF_INET, BRDB_BASE_CTL, BRDB_SO_SET_MAX+1, do_brdb_set_ctl,
++ BRDB_BASE_CTL, BRDB_SO_GET_MAX+1, do_brdb_get_ctl, 0, NULL };
++
++
++static struct nf_hook_ops brdb_br_ops[] = {
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_PRE_ROUTING, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_IN, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_FORWARD, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_LOCAL_OUT, -250},
++ { { NULL, NULL }, maintaindb, PF_BRIDGE, NF_BR_POST_ROUTING, -250}
++};
++
++static int __init init(void)
++{
++ int ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[0])) < 0)
++ return ret;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[1])) < 0)
++ goto clean0;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[2])) < 0)
++ goto clean1;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[3])) < 0)
++ goto clean2;
++
++ if ((ret = nf_register_hook(&brdb_br_ops[4])) < 0)
++ goto clean3;
++
++ /* Register setsockopt */
++ if ((ret = nf_register_sockopt(&brdb_sockopts)) < 0)
++ goto clean4;
++
++ rwlock_init(&brdb_dblock);
++ printk("Bridge ethernet database registered\n");
++ return ret;
++
++clean4: nf_unregister_hook(&brdb_br_ops[4]);
++clean3: nf_unregister_hook(&brdb_br_ops[3]);
++clean2: nf_unregister_hook(&brdb_br_ops[2]);
++clean1: nf_unregister_hook(&brdb_br_ops[1]);
++clean0: nf_unregister_hook(&brdb_br_ops[0]);
++
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_hook(&brdb_br_ops[4]);
++ nf_unregister_hook(&brdb_br_ops[3]);
++ nf_unregister_hook(&brdb_br_ops[2]);
++ nf_unregister_hook(&brdb_br_ops[1]);
++ nf_unregister_hook(&brdb_br_ops[0]);
++ nf_unregister_sockopt(&brdb_sockopts);
++}
++
++module_init(init);
++module_exit(fini);
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_filter.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,92 @@
++/*
++ * ebtable_filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/module.h>
++
++#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
++ (1 << NF_BR_LOCAL_OUT))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, "INPUT", 0, EBT_ACCEPT, 0},
++ {0, "FORWARD", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
++ [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~FILTER_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_filter =
++{
++ {NULL, NULL}, "filter", &initial_table, FILTER_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int ebt_hook (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_filter);
++}
++
++static struct nf_hook_ops ebt_ops_filter[] = {
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_IN,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_BRIDGED},
++ { { NULL, NULL }, ebt_hook, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER}
++};
++
++static int __init init(void)
++{
++ int i, j, ret;
++
++ ret = ebt_register_table(&frame_filter);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_filter[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_filter[j]);
++ ebt_unregister_table(&frame_filter);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_filter) / sizeof(ebt_ops_filter[0]); i++)
++ nf_unregister_hook(&ebt_ops_filter[i]);
++ ebt_unregister_table(&frame_filter);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_nat.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,155 @@
++/*
++ * ebtable_nat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
++ (1 << NF_BR_POST_ROUTING))
++
++static struct ebt_entries initial_chains[] =
++{
++ {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++ {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
++};
++
++static struct ebt_replace initial_table =
++{
++ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
++ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~NAT_VALID_HOOKS)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table frame_nat =
++{
++ {NULL, NULL}, "nat", &initial_table, NAT_VALID_HOOKS,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++// used for snat to know if the frame comes from FORWARD or LOCAL_OUT.
++// needed because of the bridge-nf patch (that allows use of iptables
++// on bridged traffic)
++// if the packet is routed, we want the ebtables stuff on POSTROUTING
++// to be executed _after_ the iptables stuff. when it's bridged, it's
++// the way around
++static struct net_device __fake_net_device = {
++ hard_header_len: ETH_HLEN
++};
++
++static unsigned int
++ebt_nat_dst (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++// let snat know this frame is routed
++static unsigned int ebt_clear_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = NULL;
++ return NF_ACCEPT;
++}
++
++// let snat know this frame is bridged
++static unsigned int ebt_set_physin (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ (*pskb)->physindev = &__fake_net_device;
++ return NF_ACCEPT;
++}
++
++static unsigned int ebt_nat_src (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a routed packet
++ if ((*pskb)->physindev == NULL)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev != &__fake_net_device)
++ printk("ebtables (br_nat_src): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static unsigned int ebt_nat_src_route (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ // this is a bridged packet
++ if ((*pskb)->physindev == &__fake_net_device)
++ return NF_ACCEPT;
++ if ((*pskb)->physindev)
++ printk("ebtables (br_nat_src_route): physindev hack "
++ "doesn't work - BUG\n");
++
++ return ebt_do_table(hook, pskb, in, out, &frame_nat);
++}
++
++static struct nf_hook_ops ebt_ops_nat[] = {
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_NAT_DST_OTHER},
++ { { NULL, NULL }, ebt_nat_src, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_BRIDGED},
++ { { NULL, NULL }, ebt_nat_src_route, PF_BRIDGE, NF_BR_POST_ROUTING,
++ NF_BR_PRI_NAT_SRC_OTHER},
++ { { NULL, NULL }, ebt_nat_dst, PF_BRIDGE, NF_BR_PRE_ROUTING,
++ NF_BR_PRI_NAT_DST_BRIDGED},
++ { { NULL, NULL }, ebt_clear_physin, PF_BRIDGE, NF_BR_LOCAL_OUT,
++ NF_BR_PRI_FILTER_OTHER + 1},
++ { { NULL, NULL }, ebt_set_physin, PF_BRIDGE, NF_BR_FORWARD,
++ NF_BR_PRI_FILTER_OTHER + 1}
++};
++
++static int __init init(void)
++{
++ int i, ret, j;
++
++ ret = ebt_register_table(&frame_nat);
++ if (ret < 0)
++ return ret;
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ if ((ret = nf_register_hook(&ebt_ops_nat[i])) < 0)
++ goto cleanup;
++ return ret;
++cleanup:
++ for (j = 0; j < i; j++)
++ nf_unregister_hook(&ebt_ops_nat[j]);
++ ebt_unregister_table(&frame_nat);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ int i;
++
++ for (i = 0; i < sizeof(ebt_ops_nat) / sizeof(ebt_ops_nat[0]); i++)
++ nf_unregister_hook(&ebt_ops_nat[i]);
++ ebt_unregister_table(&frame_nat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtable_broute.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,79 @@
++/*
++ * ebtable_broute
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ * This table lets you choose between routing and bridging for frames
++ * entering on a bridge enslaved nic. This table is traversed before any
++ * other ebtables table. See net/bridge/br_input.c.
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netdevice.h>
++#include <linux/module.h>
++#include <linux/if_bridge.h>
++#include <linux/brlock.h>
++
++// EBT_ACCEPT means the frame will be bridged
++// EBT_DROP means the frame will be routed
++static struct ebt_entries initial_chain =
++ {0, "BROUTE", 0, EBT_ACCEPT, 0};
++
++static struct ebt_replace initial_table =
++{
++ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
++ { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
++};
++
++static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
++{
++ if (valid_hooks & ~(1 << NF_BR_BROUTING))
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_table broute_table =
++{
++ {NULL, NULL}, "broute", &initial_table, 1 << NF_BR_BROUTING,
++ RW_LOCK_UNLOCKED, check, NULL
++};
++
++static unsigned int
++ebt_broute (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in,
++ const struct net_device *out,
++ int (*okfn)(struct sk_buff *))
++{
++ return ebt_do_table(hook, pskb, in, out, &broute_table);
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ ret = ebt_register_table(&broute_table);
++ if (ret < 0)
++ return ret;
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ // in br_input.c, br_handle_frame() wants to call broute_decision()
++ broute_decision = ebt_broute;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ return ret;
++}
++
++static void __exit fini(void)
++{
++ br_write_lock_bh(BR_NETPROTO_LOCK);
++ broute_decision = NULL;
++ br_write_unlock_bh(BR_NETPROTO_LOCK);
++ ebt_unregister_table(&broute_table);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_redirect.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,65 @@
++/*
++ * ebt_redirect
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_redirect.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++#include "../br_private.h"
++
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ memcpy((**pskb).mac.ethernet->h_dest,
++ in->br_port->br->dev.dev_addr, ETH_ALEN);
++ (*pskb)->pkt_type = PACKET_HOST;
++ return infostuff->target;
++}
++
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
++
++ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_redirect_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target redirect_target =
++{
++ {NULL, NULL}, EBT_REDIRECT_TARGET, ebt_target_redirect,
++ ebt_target_redirect_check, NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&redirect_target);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&redirect_target);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_arp.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,107 @@
++/*
++ * ebt_arp
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Tim Gardner <timg@tpi.com>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_arp.h>
++#include <linux/if_arp.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_arp(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *)data;
++
++ if (infostuff->bitmask & EBT_ARP_OPCODE && FWINV2(infostuff->opcode !=
++ ((*skb).nh.arph)->ar_op, EBT_ARP_OPCODE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_HTYPE && FWINV2(infostuff->htype !=
++ ((*skb).nh.arph)->ar_hrd, EBT_ARP_HTYPE))
++ return 1;
++ if (infostuff->bitmask & EBT_ARP_PTYPE && FWINV2(infostuff->ptype !=
++ ((*skb).nh.arph)->ar_pro, EBT_ARP_PTYPE))
++ return 1;
++
++ if (infostuff->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP))
++ {
++ __u32 arp_len = sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (2*(((*skb).nh.arph)->ar_pln));
++ __u32 dst;
++ __u32 src;
++
++ // Make sure the packet is long enough.
++ if ((((*skb).nh.raw) + arp_len) > (*skb).tail)
++ return 1;
++ // IPV4 addresses are always 4 bytes.
++ if (((*skb).nh.arph)->ar_pln != sizeof(__u32))
++ return 1;
++
++ if (infostuff->bitmask & EBT_ARP_SRC_IP) {
++ memcpy(&src, ((*skb).nh.raw) + sizeof(struct arphdr) +
++ ((*skb).nh.arph)->ar_hln, sizeof(__u32));
++ if (FWINV2(infostuff->saddr != (src & infostuff->smsk),
++ EBT_ARP_SRC_IP))
++ return 1;
++ }
++
++ if (infostuff->bitmask & EBT_ARP_DST_IP) {
++ memcpy(&dst, ((*skb).nh.raw)+sizeof(struct arphdr) +
++ (2*(((*skb).nh.arph)->ar_hln)) +
++ (((*skb).nh.arph)->ar_pln), sizeof(__u32));
++ if (FWINV2(infostuff->daddr != (dst & infostuff->dmsk),
++ EBT_ARP_DST_IP))
++ return 1;
++ }
++ }
++ return 0;
++}
++
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
++
++ if (datalen != sizeof(struct ebt_arp_info))
++ return -EINVAL;
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ (e->ethproto != __constant_htons(ETH_P_ARP) &&
++ e->ethproto != __constant_htons(ETH_P_RARP)) ||
++ e->invflags & EBT_IPROTO)
++ return -EINVAL;
++ if (infostuff->bitmask & ~EBT_ARP_MASK)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_match filter_arp =
++{
++ {NULL, NULL}, EBT_ARP_MATCH, ebt_filter_arp, ebt_arp_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_arp);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_arp);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_ip.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,81 @@
++/*
++ * ebt_ip
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_ip.h>
++#include <linux/ip.h>
++#include <linux/module.h>
++
++#define FWINV2(bool,invflg) ((bool) ^ !!(infostuff->invflags & invflg))
++static int ebt_filter_ip(const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen, const struct ebt_counter *c)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (infostuff->bitmask & EBT_IP_TOS &&
++ FWINV2(infostuff->tos != ((*skb).nh.iph)->tos, EBT_IP_TOS))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_PROTO && FWINV2(infostuff->protocol !=
++ ((*skb).nh.iph)->protocol, EBT_IP_PROTO))
++ return 1;
++ if (infostuff->bitmask & EBT_IP_SOURCE &&
++ FWINV2((((*skb).nh.iph)->saddr & infostuff->smsk) !=
++ infostuff->saddr, EBT_IP_SOURCE))
++ return 1;
++ if ((infostuff->bitmask & EBT_IP_DEST) &&
++ FWINV2((((*skb).nh.iph)->daddr & infostuff->dmsk) !=
++ infostuff->daddr, EBT_IP_DEST))
++ return 1;
++ return 0;
++}
++
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
++
++ if (datalen != sizeof(struct ebt_ip_info)) {
++ return -EINVAL;
++ }
++ if (e->bitmask & (EBT_NOPROTO | EBT_802_3) ||
++ e->ethproto != __constant_htons(ETH_P_IP) ||
++ e->invflags & EBT_IPROTO)
++ {
++ return -EINVAL;
++ }
++ if (infostuff->bitmask & ~EBT_IP_MASK) {
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static struct ebt_match filter_ip =
++{
++ {NULL, NULL}, EBT_IP_MATCH, ebt_filter_ip, ebt_ip_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_match(&filter_ip);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_match(&filter_ip);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_vlan.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,152 @@
++/*
++ * ebt_vlan kernelspace
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ * Nick Fedchik <nick@fedchik.org.ua>
++ *
++ * June, 2002
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_vlan.h>
++#include <linux/if_vlan.h>
++#include <linux/if_ether.h>
++#include <linux/module.h>
++
++static unsigned char debug;
++MODULE_PARM (debug, "0-1b");
++MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
++
++#define MODULE_VERSION "0.2"
++
++static int ebt_filter_vlan (const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *data,
++ unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++ struct vlan_ethhdr *vlanethhdr =
++ (struct vlan_ethhdr *) skb->mac.raw;
++ unsigned short v_id;
++ unsigned short v_prio;
++ unsigned short v_TCI;
++
++ /*
++ * Calculate 802.1Q VLAN ID and user_priority from
++ * Tag Control Information (TCI) field.
++ * Reserved one bit (13) for CFI (Canonical Format Indicator)
++ */
++ v_TCI = ntohs (vlanethhdr->h_vlan_TCI);
++ v_id = v_TCI & 0xFFF;
++ v_prio = v_TCI >> 13;
++
++ /*
++ * Checking VLANs
++ */
++ if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */
++ if (!((infostuff->id == v_id)
++ ^ !!(infostuff->invflags & EBT_VLAN_ID)))
++ return 1;
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched ID=%s%d (mask=%X)\n",
++ (infostuff->
++ invflags & EBT_VLAN_ID) ? "!" : "",
++ infostuff->id, infostuff->bitmask);
++ }
++ /*
++ * Checking User Priority
++ */
++ if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */
++ if (!((infostuff->prio == v_prio)
++ ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
++ return 1; /* missed */
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched Prio=%s%d (mask=%X)\n",
++ (infostuff->
++ invflags & EBT_VLAN_PRIO) ? "!" : "",
++ infostuff->prio, infostuff->bitmask);
++ }
++ /*
++ * Checking for Encapsulated proto
++ */
++ if (infostuff->bitmask & EBT_VLAN_ENCAP) { /* Is VLAN Encap parsed? */
++ if (!
++ ((infostuff->encap ==
++ vlanethhdr->h_vlan_encapsulated_proto)
++ ^ !!(infostuff->invflags & EBT_VLAN_ENCAP)))
++ return 1; /* missed */
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched encap=%s%2.4X (mask=%X)\n",
++ (infostuff->
++ invflags & EBT_VLAN_ENCAP) ? "!" : "",
++ ntohs (infostuff->encap),
++ infostuff->bitmask);
++ }
++
++ /*
++ * rule matched
++ */
++ return 0;
++}
++
++/*
++ * ebt_vlan_check() is called when userspace delivers the table to the kernel,
++ * * it is called to check that userspace doesn't give a bad table.
++ */
++static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data,
++ unsigned int datalen)
++{
++ struct ebt_vlan_info *infostuff = (struct ebt_vlan_info *) data;
++
++ if (datalen != sizeof (struct ebt_vlan_info))
++ return -EINVAL;
++
++ if (e->ethproto != __constant_htons (ETH_P_8021Q))
++ return -EINVAL;
++
++ if (infostuff->bitmask & ~EBT_VLAN_MASK) {
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static struct ebt_match filter_vlan = {
++ {NULL, NULL},
++ EBT_VLAN_MATCH,
++ ebt_filter_vlan,
++ ebt_vlan_check,
++ NULL,
++ THIS_MODULE
++};
++
++static int __init init (void)
++{
++ printk (KERN_INFO
++ "ebt_vlan: 802.1Q VLAN matching module for EBTables "
++ MODULE_VERSION "\n");
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: 802.1Q rule matching debug is on\n");
++ return ebt_register_match (&filter_vlan);
++}
++
++static void __exit fini (void)
++{
++ ebt_unregister_match (&filter_vlan);
++}
++
++module_init (init);
++module_exit (fini);
++EXPORT_NO_SYMBOLS;
++MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v"
++ MODULE_VERSION);
++MODULE_LICENSE ("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_log.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,111 @@
++/*
++ * ebt_log
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * April, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_log.h>
++#include <linux/module.h>
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/spinlock.h>
++
++static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
++
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++
++ if (datalen != sizeof(struct ebt_log_info))
++ return -EINVAL;
++ if (loginfo->bitmask & ~EBT_LOG_MASK)
++ return -EINVAL;
++ if (loginfo->loglevel >= 8)
++ return -EINVAL;
++ loginfo->prefix[EBT_LOG_PREFIX_SIZE - 1] = '\0';
++ return 0;
++}
++
++static void ebt_log(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *data, unsigned int datalen,
++ const struct ebt_counter *c)
++{
++ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
++ char level_string[4] = "< >";
++ level_string[1] = '0' + loginfo->loglevel;
++
++ spin_lock_bh(&ebt_log_lock);
++ printk(level_string);
++ // max length: 29 + 10 + 2 * 16
++ printk("%s IN=%s OUT=%s ",
++ loginfo->prefix,
++ in ? in->name : "",
++ out ? out->name : "");
++
++ if (skb->dev->hard_header_len) {
++ int i;
++ unsigned char *p = (skb->mac.ethernet)->h_source;
++ printk("MAC source = ");
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 31
++ printk("MAC dest = ");
++ p = (skb->mac.ethernet)->h_dest;
++ for (i = 0; i < ETH_ALEN; i++,p++)
++ printk("%02x%c", *p,
++ i == ETH_ALEN - 1
++ ? ' ':':');// length: 29
++ }
++ // length: 14
++ printk("proto = 0x%04x", ntohs(((*skb).mac.ethernet)->h_proto));
++
++ if ((loginfo->bitmask & EBT_LOG_IP) && skb->mac.ethernet->h_proto ==
++ htons(ETH_P_IP)){
++ struct iphdr *iph = skb->nh.iph;
++ // max length: 46
++ printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u,",
++ NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
++ // max length: 26
++ printk(" IP tos=0x%02X, IP proto=%d", iph->tos, iph->protocol);
++ }
++
++ if ((loginfo->bitmask & EBT_LOG_ARP) &&
++ ((skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) ||
++ (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_RARP)))) {
++ struct arphdr * arph = skb->nh.arph;
++ // max length: 40
++ printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d",
++ ntohs(arph->ar_hrd), ntohs(arph->ar_pro),
++ ntohs(arph->ar_op));
++ }
++ printk("\n");
++ spin_unlock_bh(&ebt_log_lock);
++}
++
++struct ebt_watcher log =
++{
++ {NULL, NULL}, EBT_LOG_WATCHER, ebt_log, ebt_log_check, NULL,
++ THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_watcher(&log);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_watcher(&log);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_snat.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,64 @@
++/*
++ * ebt_snat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_source, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if (strcmp(tablename, "nat"))
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target snat =
++{
++ {NULL, NULL}, EBT_SNAT_TARGET, ebt_target_snat, ebt_target_snat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&snat);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&snat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebt_dnat.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,64 @@
++/*
++ * ebt_dnat
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * June, 2002
++ *
++ */
++
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/netfilter_bridge/ebt_nat.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/skbuff.h>
++#include <linux/module.h>
++#include <net/sock.h>
++
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++ const struct net_device *in, const struct net_device *out,
++ const void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ memcpy(((**pskb).mac.ethernet)->h_dest, infostuff->mac,
++ ETH_ALEN * sizeof(unsigned char));
++ return infostuff->target;
++}
++
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *data, unsigned int datalen)
++{
++ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
++
++ if ( (strcmp(tablename, "nat") ||
++ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
++ return -EINVAL;
++ if (datalen != sizeof(struct ebt_nat_info))
++ return -EINVAL;
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
++ return -EINVAL;
++ return 0;
++}
++
++static struct ebt_target dnat =
++{
++ {NULL, NULL}, EBT_DNAT_TARGET, ebt_target_dnat, ebt_target_dnat_check,
++ NULL, THIS_MODULE
++};
++
++static int __init init(void)
++{
++ return ebt_register_target(&dnat);
++}
++
++static void __exit fini(void)
++{
++ ebt_unregister_target(&dnat);
++}
++
++module_init(init);
++module_exit(fini);
++EXPORT_NO_SYMBOLS;
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/net/bridge/netfilter/ebtables.c Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,1453 @@
++/*
++ * ebtables
++ *
++ * Author:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++// used for print_string
++#include <linux/sched.h>
++#include <linux/tty.h>
++
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netfilter_bridge.h>
++#include <linux/netfilter_ipv4.h>
++#include <linux/netfilter_bridge/ebtables.h>
++#include <linux/spinlock.h>
++#include <asm/uaccess.h>
++#include <linux/smp.h>
++#include <net/sock.h>
++// needed for logical [in,out]-dev filtering
++#include "../br_private.h"
++
++// list_named_find
++#define ASSERT_READ_LOCK(x)
++#define ASSERT_WRITE_LOCK(x)
++#include <linux/netfilter_ipv4/listhelp.h>
++
++#if 0 // use this for remote debugging
++#define BUGPRINT(args) print_string(args);
++#else
++#define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
++ "report to author: "format, ## args)
++// #define BUGPRINT(format, args...)
++#endif
++#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
++ ": out of memory: "format, ## args)
++// #define MEMPRINT(format, args...)
++
++static void print_string(char *str);
++
++static DECLARE_MUTEX(ebt_mutex);
++static LIST_HEAD(ebt_tables);
++static LIST_HEAD(ebt_targets);
++static LIST_HEAD(ebt_matches);
++static LIST_HEAD(ebt_watchers);
++
++static struct ebt_target ebt_standard_target =
++{ {NULL, NULL}, EBT_STANDARD_TARGET, NULL, NULL, NULL, NULL};
++
++static inline int ebt_do_watcher (struct ebt_entry_watcher *w,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ w->u.watcher->watcher(skb, in, out, w->data,
++ w->watcher_size, c);
++ // watchers don't give a verdict
++ return 0;
++}
++
++static inline int ebt_do_match (struct ebt_entry_match *m,
++ const struct sk_buff *skb,
++ const struct net_device *in,
++ const struct net_device *out,
++ const struct ebt_counter *c)
++{
++ return m->u.match->match(skb, in, out, m->data,
++ m->match_size, c);
++}
++
++static inline int ebt_dev_check(char *entry, const struct net_device *device)
++{
++ if (*entry == '\0')
++ return 0;
++ if (!device)
++ return 1;
++ return strncmp(entry, device->name, IFNAMSIZ);
++}
++
++// Do some firewalling
++unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table)
++{
++ int i, j, nentries;
++ struct ebt_entry *point;
++ struct ebt_counter *counter_base;
++ struct ebt_entry_target *t;
++ int verdict, sp = 0;
++ struct ebt_chainstack *cs;
++ struct ebt_entries *chaininfo;
++ char *base;
++
++ read_lock_bh(&table->lock);
++ cs = table->private->chainstack;
++ chaininfo = table->private->hook_entry[hook];
++ nentries = table->private->hook_entry[hook]->nentries;
++ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
++ #define cb_base table->private->counters + \
++ cpu_number_map(smp_processor_id()) * table->private->nentries
++ counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
++ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
++ // base for chain jumps
++ base = (char *)chaininfo;
++ i = 0;
++ while (i < nentries) {
++ if ( ( point->bitmask & EBT_NOPROTO ||
++ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
++ EBT_IPROTO)
++ || FWINV(ntohs(((**pskb).mac.ethernet)->h_proto) < 1536 &&
++ (point->bitmask & EBT_802_3), EBT_IPROTO) )
++ && FWINV(!ebt_dev_check((char *)(point->in), in), EBT_IIN)
++ && FWINV(!ebt_dev_check((char *)(point->out), out), EBT_IOUT)
++ && ((!in || !in->br_port) ? 1 : FWINV(!ebt_dev_check((char *)
++ (point->logical_in), &in->br_port->br->dev), EBT_ILOGICALIN))
++ && ((!out || !out->br_port) ? 1 :
++ FWINV(!ebt_dev_check((char *)
++ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
++
++ ) {
++ if (point->bitmask & EBT_SOURCEMAC) {
++ verdict = 0;
++ for (j = 0; j < 6; j++)
++ verdict |= (((**pskb).mac.ethernet)->
++ h_source[j] ^ point->sourcemac[j]) &
++ point->sourcemsk[j];
++ if (FWINV(!!verdict, EBT_ISOURCE) )
++ goto letscontinue;
++ }
++
++ if (point->bitmask & EBT_DESTMAC) {
++ verdict = 0;
++ for (j = 0; j < 6; j++)
++ verdict |= (((**pskb).mac.ethernet)->
++ h_dest[j] ^ point->destmac[j]) &
++ point->destmsk[j];
++ if (FWINV(!!verdict, EBT_IDEST) )
++ goto letscontinue;
++ }
++
++ if (EBT_MATCH_ITERATE(point, ebt_do_match, *pskb, in,
++ out, counter_base + i) != 0)
++ goto letscontinue;
++
++ // increase counter
++ (*(counter_base + i)).pcnt++;
++
++ // these should only watch: not modify, nor tell us
++ // what to do with the packet
++ EBT_WATCHER_ITERATE(point, ebt_do_watcher, *pskb, in,
++ out, counter_base + i);
++
++ t = (struct ebt_entry_target *)
++ (((char *)point) + point->target_offset);
++ // standard target
++ if (!t->u.target->target)
++ verdict =
++ ((struct ebt_standard_target *)t)->verdict;
++ else
++ verdict = t->u.target->target(pskb, hook,
++ in, out, t->data, t->target_size);
++ if (verdict == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ if (verdict == EBT_DROP) {
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ if (verdict == EBT_RETURN) {
++letsreturn:
++ if (sp == 0)
++ // act like this is EBT_CONTINUE
++ goto letscontinue;
++ sp--;
++ // put all the local variables right
++ i = cs[sp].n;
++ chaininfo = cs[sp].chaininfo;
++ nentries = chaininfo->nentries;
++ point = cs[sp].e;
++ counter_base = cb_base +
++ chaininfo->counter_offset;
++ continue;
++ }
++ if (verdict == EBT_CONTINUE)
++ goto letscontinue;
++ if (verdict < 0) {
++ BUGPRINT("bogus standard verdict\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ // jump to a udc
++ cs[sp].n = i + 1;
++ cs[sp].chaininfo = chaininfo;
++ cs[sp].e = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i = 0;
++ chaininfo = (struct ebt_entries *) (base + verdict);
++ if (chaininfo->distinguisher) {
++ BUGPRINT("jump to non-chain\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ nentries = chaininfo->nentries;
++ point = (struct ebt_entry *)chaininfo->data;
++ counter_base = cb_base + chaininfo->counter_offset;
++ sp++;
++ continue;
++ }
++letscontinue:
++ point = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i++;
++ }
++
++ // I actually like this :)
++ if (chaininfo->policy == EBT_RETURN)
++ goto letsreturn;
++ if (chaininfo->policy == EBT_ACCEPT) {
++ read_unlock_bh(&table->lock);
++ return NF_ACCEPT;
++ }
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++}
++
++/* If it succeeds, returns element and locks mutex */
++static inline void *
++find_inlist_lock_noload(struct list_head *head,
++ const char *name,
++ int *error,
++ struct semaphore *mutex)
++{
++ void *ret;
++
++ *error = down_interruptible(mutex);
++ if (*error != 0)
++ return NULL;
++
++ ret = list_named_find(head, name);
++ if (!ret) {
++ *error = -ENOENT;
++ up(mutex);
++ }
++ return ret;
++}
++
++#ifndef CONFIG_KMOD
++#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
++#else
++static void *
++find_inlist_lock(struct list_head *head,
++ const char *name,
++ const char *prefix,
++ int *error,
++ struct semaphore *mutex)
++{
++ void *ret;
++
++ ret = find_inlist_lock_noload(head, name, error, mutex);
++ if (!ret) {
++ char modulename[EBT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
++ strcpy(modulename, prefix);
++ strcat(modulename, name);
++ request_module(modulename);
++ ret = find_inlist_lock_noload(head, name, error, mutex);
++ }
++
++ return ret;
++}
++#endif
++
++static inline struct ebt_table *
++find_table_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
++}
++
++static inline struct ebt_match *
++find_match_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_watcher *
++find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
++}
++
++static inline struct ebt_target *
++find_target_lock(const char *name, int *error, struct semaphore *mutex)
++{
++ return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
++}
++
++static inline int
++ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
++ const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++ struct ebt_match *match;
++ int ret;
++
++ m->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ match = find_match_lock(m->u.name, &ret, &ebt_mutex);
++ if (!match)
++ return ret;
++ m->u.match = match;
++ if (match->me)
++ __MOD_INC_USE_COUNT(match->me);
++ up(&ebt_mutex);
++ if (match->check &&
++ match->check(name, hookmask, e, m->data, m->match_size) != 0) {
++ BUGPRINT("match->check failed\n");
++ if (match->me)
++ __MOD_DEC_USE_COUNT(match->me);
++ return -EINVAL;
++ }
++ (*cnt)++;
++ return 0;
++}
++
++static inline int
++ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
++ const char *name, unsigned int hookmask, unsigned int *cnt)
++{
++ struct ebt_watcher *watcher;
++ int ret;
++
++ w->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
++ if (!watcher)
++ return ret;
++ w->u.watcher = watcher;
++ if (watcher->me)
++ __MOD_INC_USE_COUNT(watcher->me);
++ up(&ebt_mutex);
++ if (watcher->check &&
++ watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
++ BUGPRINT("watcher->check failed\n");
++ if (watcher->me)
++ __MOD_DEC_USE_COUNT(watcher->me);
++ return -EINVAL;
++ }
++ (*cnt)++;
++ return 0;
++}
++
++// this one is very careful, as it is the first function
++// to parse the userspace data
++static inline int
++ebt_check_entry_size_and_hooks(struct ebt_entry *e,
++ struct ebt_table_info *newinfo, char *base, char *limit,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
++ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
++{
++ int i;
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ( (char *)hook_entries[i] - base ==
++ (char *)e - newinfo->entries)
++ break;
++ }
++ // beginning of a new chain
++ // if i == NF_BR_NUMHOOKS it must be a user defined chain
++ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
++ // we make userspace set this right,
++ // so there is no misunderstanding
++ BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
++ "in distinguisher\n");
++ return -EINVAL;
++ }
++ // this checks if the previous chain has as many entries
++ // as it said it has
++ if (*n != *cnt) {
++ BUGPRINT("nentries does not equal the nr of entries "
++ "in the chain\n");
++ return -EINVAL;
++ }
++ // before we look at the struct, be sure it is not too big
++ if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
++ > limit) {
++ BUGPRINT("entries_size too small\n");
++ return -EINVAL;
++ }
++ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
++ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
++ // only RETURN from udc
++ if (i != NF_BR_NUMHOOKS ||
++ ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ }
++ if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++ (*udc_cnt)++;
++ else
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++ BUGPRINT("counter_offset != totalcnt");
++ return -EINVAL;
++ }
++ *n = ((struct ebt_entries *)e)->nentries;
++ *cnt = 0;
++ return 0;
++ }
++ // a plain old entry, heh
++ if (sizeof(struct ebt_entry) > e->watchers_offset ||
++ e->watchers_offset > e->target_offset ||
++ e->target_offset > e->next_offset) {
++ BUGPRINT("entry offsets not in right order\n");
++ return -EINVAL;
++ }
++ // this is not checked anywhere else
++ if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++ BUGPRINT("target size too small\n");
++ return -EINVAL;
++ }
++
++ (*cnt)++;
++ (*totalcnt)++;
++ return 0;
++}
++
++struct ebt_cl_stack
++{
++ struct ebt_chainstack cs;
++ int from;
++ unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++ struct ebt_cl_stack *udc)
++{
++ int i;
++
++ // we're only interested in chain starts
++ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++ return 0;
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++ break;
++ }
++ // only care about udc
++ if (i != NF_BR_NUMHOOKS)
++ return 0;
++
++ udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++ // these initialisations are depended on later in check_chainloops()
++ udc[*n].cs.n = 0;
++ udc[*n].hookmask = 0;
++
++ (*n)++;
++ return 0;
++}
++
++static inline int
++ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (m->u.match->destroy)
++ m->u.match->destroy(m->data, m->match_size);
++ if (m->u.match->me)
++ __MOD_DEC_USE_COUNT(m->u.match->me);
++
++ return 0;
++}
++
++static inline int
++ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
++{
++ if (i && (*i)-- == 0)
++ return 1;
++ if (w->u.watcher->destroy)
++ w->u.watcher->destroy(w->data, w->watcher_size);
++ if (w->u.watcher->me)
++ __MOD_DEC_USE_COUNT(w->u.watcher->me);
++
++ return 0;
++}
++
++static inline int
++ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ const char *name, unsigned int *cnt, unsigned int valid_hooks,
++ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
++{
++ struct ebt_entry_target *t;
++ struct ebt_target *target;
++ unsigned int i, j, hook = 0, hookmask = 0;
++ int ret;
++
++ // Don't mess with the struct ebt_entries
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ if (e->bitmask & ~EBT_F_MASK) {
++ BUGPRINT("Unknown flag for bitmask\n");
++ return -EINVAL;
++ }
++ if (e->invflags & ~EBT_INV_MASK) {
++ BUGPRINT("Unknown flag for inv bitmask\n");
++ return -EINVAL;
++ }
++ if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
++ BUGPRINT("NOPROTO & 802_3 not allowed\n");
++ return -EINVAL;
++ }
++ e->in[IFNAMSIZ - 1] = '\0';
++ e->out[IFNAMSIZ - 1] = '\0';
++ e->logical_in[IFNAMSIZ - 1] = '\0';
++ e->logical_out[IFNAMSIZ - 1] = '\0';
++ // what hook do we belong to?
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if ((char *)newinfo->hook_entry[i] < (char *)e)
++ hook = i;
++ else
++ break;
++ }
++ if (i < NF_BR_NUMHOOKS)
++ hookmask = (1 << hook);
++ else {
++ for (i = 0; i < udc_cnt; i++)
++ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++ break;
++ if (i == 0)
++ hookmask = (1 << hook);
++ else
++ hookmask = cl_s[i - 1].hookmask;
++ }
++ i = 0;
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
++ if (ret != 0)
++ goto cleanup_matches;
++ j = 0;
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
++ if (ret != 0)
++ goto cleanup_watchers;
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ target = find_target_lock(t->u.name, &ret, &ebt_mutex);
++ if (!target)
++ goto cleanup_watchers;
++ if (target->me)
++ __MOD_INC_USE_COUNT(target->me);
++ up(&ebt_mutex);
++
++ t->u.target = target;
++ if (t->u.target == &ebt_standard_target) {
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ if (((struct ebt_standard_target *)t)->verdict <
++ -NUM_STANDARD_TARGETS) {
++ BUGPRINT("Invalid standard target\n");
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ } else if (t->u.target->check &&
++ t->u.target->check(name, hookmask, e, t->data,
++ t->target_size) != 0) {
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++ ret = -EFAULT;
++ goto cleanup_watchers;
++ }
++ (*cnt)++;
++ return 0;
++cleanup_watchers:
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
++cleanup_matches:
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
++ return ret;
++}
++
++static inline int
++ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
++{
++ struct ebt_entry_target *t;
++
++ if (e->bitmask == 0)
++ return 0;
++ // we're done
++ if (cnt && (*cnt)-- == 0)
++ return 1;
++ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
++ EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++ if (t->u.target->destroy)
++ t->u.target->destroy(t->data, t->target_size);
++ if (t->u.target->me)
++ __MOD_DEC_USE_COUNT(t->u.target->me);
++
++ return 0;
++}
++
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++ unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++ int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++ struct ebt_entry *e = (struct ebt_entry *)chain->data;
++ struct ebt_entry_target *t;
++
++ while (pos < nentries || chain_nr != -1) {
++ // end of udc, go back one 'recursion' step
++ if (pos == nentries) {
++ // put back values of the time when this chain was called
++ e = cl_s[chain_nr].cs.e;
++ if (cl_s[chain_nr].from != -1)
++ nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++ else
++ nentries = chain->nentries;
++ pos = cl_s[chain_nr].cs.n;
++ // make sure we won't see a loop that isn't one
++ cl_s[chain_nr].cs.n = 0;
++ chain_nr = cl_s[chain_nr].from;
++ if (pos == nentries)
++ continue;
++ }
++ t = (struct ebt_entry_target *)
++ (((char *)e) + e->target_offset);
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++ goto letscontinue;
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ return -1;
++ }
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ if (verdict >= 0) { // jump to another chain
++ struct ebt_entries *hlp2 =
++ (struct ebt_entries *)(base + verdict);
++ for (i = 0; i < udc_cnt; i++)
++ if (hlp2 == cl_s[i].cs.chaininfo)
++ break;
++ // bad destination or loop
++ if (i == udc_cnt) {
++ BUGPRINT("bad destination\n");
++ return -1;
++ }
++ if (cl_s[i].cs.n) {
++ BUGPRINT("loop\n");
++ return -1;
++ }
++ cl_s[i].cs.n = pos + 1;
++ pos = 0;
++ cl_s[i].cs.e = ((void *)e + e->next_offset);
++ e = (struct ebt_entry *)(hlp2->data);
++ nentries = hlp2->nentries;
++ cl_s[i].from = chain_nr;
++ chain_nr = i;
++ // this udc is accessible from the base chain for hooknr
++ cl_s[i].hookmask |= (1 << hooknr);
++ continue;
++ }
++letscontinue:
++ e = (void *)e + e->next_offset;
++ pos++;
++ }
++ return 0;
++}
++
++// do the parsing of the table/chains/entries/matches/watchers/targets, heh
++static int translate_table(struct ebt_replace *repl,
++ struct ebt_table_info *newinfo)
++{
++ unsigned int i, j, k, udc_cnt;
++ int ret;
++ struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
++
++ i = 0;
++ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
++ i++;
++ if (i == NF_BR_NUMHOOKS) {
++ BUGPRINT("No valid hooks specified\n");
++ return -EINVAL;
++ }
++ if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
++ BUGPRINT("Chains don't start at beginning\n");
++ return -EINVAL;
++ }
++ // make sure chains are ordered after each other in same order
++ // as their corresponding hooks
++ for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
++ if (!(repl->valid_hooks & (1 << j)))
++ continue;
++ if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
++ BUGPRINT("Hook order must be followed\n");
++ return -EINVAL;
++ }
++ i = j;
++ }
++
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ newinfo->hook_entry[i] = NULL;
++
++ newinfo->entries_size = repl->entries_size;
++ newinfo->nentries = repl->nentries;
++
++ // do some early checkings and initialize some things
++ i = 0; // holds the expected nr. of entries for the chain
++ j = 0; // holds the up to now counted entries for the chain
++ k = 0; // holds the total nr. of entries, should equal
++ // newinfo->nentries afterwards
++ udc_cnt = 0; // will hold the nr. of user defined chains (udc)
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
++ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
++ &udc_cnt, repl->valid_hooks);
++
++ if (ret != 0)
++ return ret;
++
++ if (i != j) {
++ BUGPRINT("nentries does not equal the nr of entries in the "
++ "(last) chain\n");
++ return -EINVAL;
++ }
++ if (k != newinfo->nentries) {
++ BUGPRINT("Total nentries is wrong\n");
++ return -EINVAL;
++ }
++
++ // check if all valid hooks have a chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (newinfo->hook_entry[i] == NULL &&
++ (repl->valid_hooks & (1 << i))) {
++ BUGPRINT("Valid hook without chain\n");
++ return -EINVAL;
++ }
++ }
++
++ // Get the location of the udc, put them in an array
++ // While we're at it, allocate the chainstack
++ if (udc_cnt) {
++ // this will get free'd in do_replace()/ebt_register_table()
++ // if an error occurs
++ newinfo->chainstack = (struct ebt_chainstack *)
++ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++ if (!newinfo->chainstack)
++ return -ENOMEM;
++ cl_s = (struct ebt_cl_stack *)
++ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++ if (!cl_s)
++ return -ENOMEM;
++ i = 0; // the i'th udc
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++ repl->valid_hooks, cl_s);
++ // sanity check
++ if (i != udc_cnt) {
++ BUGPRINT("i != udc_cnt\n");
++ vfree(cl_s);
++ return -EFAULT;
++ }
++ }
++
++ // Check for loops
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ if (repl->valid_hooks & (1 << i))
++ if (check_chainloops(newinfo->hook_entry[i],
++ cl_s, udc_cnt, i, newinfo->entries)) {
++ if (cl_s)
++ vfree(cl_s);
++ return -EINVAL;
++ }
++
++ // we now know the following (along with E=mc²):
++ // - the nr of entries in each chain is right
++ // - the size of the allocated space is right
++ // - all valid hooks have a corresponding chain
++ // - there are no loops
++ // - wrong data can still be on the level of a single entry
++ // - could be there are jumps to places that are not the
++ // beginning of a chain. This can only occur in chains that
++ // are not accessible from any base chains, so we don't care.
++
++ // we just don't trust anything
++ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++ // used to know what we need to clean up if something goes wrong
++ i = 0;
++ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++ cl_s, udc_cnt);
++ if (ret != 0) {
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, &i);
++ }
++ if (cl_s)
++ vfree(cl_s);
++ return ret;
++}
++
++// called under write_lock
++static inline void get_counters(struct ebt_table_info *info,
++ struct ebt_counter *counters)
++{
++ int i, cpu, counter_base;
++
++ // counters of cpu 0
++ memcpy(counters, info->counters,
++ sizeof(struct ebt_counter) * info->nentries);
++ // add other counters to those of cpu 0
++ for (cpu = 1; cpu < smp_num_cpus; cpu++) {
++ counter_base = cpu * info->nentries;
++ for (i = 0; i < info->nentries; i++)
++ counters[i].pcnt +=
++ info->counters[counter_base + i].pcnt;
++ }
++}
++
++// replace the table
++static int do_replace(void *user, unsigned int len)
++{
++ int ret;
++ struct ebt_table_info *newinfo;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++ struct ebt_counter *counterstmp = NULL;
++ // used to be able to unlock earlier
++ struct ebt_table_info *table;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
++ return -EFAULT;
++
++ if (len != sizeof(tmp) + tmp.entries_size) {
++ BUGPRINT("Wrong len argument\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size == 0) {
++ BUGPRINT("Entries_size never zero\n");
++ return -EINVAL;
++ }
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ if (!newinfo)
++ return -ENOMEM;
++
++ if (tmp.nentries) {
++ newinfo->counters = (struct ebt_counter *)vmalloc(
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ if (!newinfo->counters) {
++ ret = -ENOMEM;
++ goto free_newinfo;
++ }
++ memset(newinfo->counters, 0,
++ sizeof(struct ebt_counter) * tmp.nentries * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ newinfo->entries = (char *)vmalloc(tmp.entries_size);
++ if (!newinfo->entries) {
++ ret = -ENOMEM;
++ goto free_counters;
++ }
++ if (copy_from_user(
++ newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
++ BUGPRINT("Couldn't copy entries from userspace\n");
++ ret = -EFAULT;
++ goto free_entries;
++ }
++
++ // the user wants counters back
++ // the check on the size is done later, when we have the lock
++ if (tmp.num_counters) {
++ counterstmp = (struct ebt_counter *)
++ vmalloc(tmp.num_counters * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ ret = -ENOMEM;
++ goto free_entries;
++ }
++ }
++ else
++ counterstmp = NULL;
++
++ // this can get initialized by translate_table()
++ newinfo->chainstack = NULL;
++ ret = translate_table(&tmp, newinfo);
++
++ if (ret != 0)
++ goto free_counterstmp;
++
++ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++ if (!t)
++ goto free_unlock;
++
++ // the table doesn't like it
++ if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
++ goto free_unlock;
++
++ if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr. of counters requested\n");
++ ret = -EINVAL;
++ goto free_unlock;
++ }
++
++ // we have the mutex lock, so no danger in reading this pointer
++ table = t->private;
++ // we need an atomic snapshot of the counters
++ write_lock_bh(&t->lock);
++ if (tmp.num_counters)
++ get_counters(t->private, counterstmp);
++
++ t->private = newinfo;
++ write_unlock_bh(&t->lock);
++ up(&ebt_mutex);
++ // So, a user can change the chains while having messed up his counter
++ // allocation. Only reason why I do this is because this way the lock
++ // is held only once, while this doesn't bring the kernel into a
++ // dangerous state.
++ if (tmp.num_counters &&
++ copy_to_user(tmp.counters, counterstmp,
++ tmp.num_counters * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ ret = -EFAULT;
++ }
++ else
++ ret = 0;
++
++ // decrease module count and free resources
++ EBT_ENTRY_ITERATE(table->entries, table->entries_size,
++ ebt_cleanup_entry, NULL);
++
++ vfree(table->entries);
++ if (table->counters)
++ vfree(table->counters);
++ if (table->chainstack)
++ vfree(table->chainstack);
++ vfree(table);
++
++ if (counterstmp)
++ vfree(counterstmp);
++ return ret;
++
++free_unlock:
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_cleanup_entry, NULL);
++free_counterstmp:
++ if (counterstmp)
++ vfree(counterstmp);
++ // can be initialized in translate_table()
++ if (newinfo->chainstack)
++ vfree(newinfo->chainstack);
++free_entries:
++ if (newinfo->entries)
++ vfree(newinfo->entries);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++free_newinfo:
++ if (newinfo)
++ vfree(newinfo);
++ return ret;
++}
++
++int ebt_register_target(struct ebt_target *target)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_targets, target)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_target(struct ebt_target *target)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_targets, target);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_match(struct ebt_match *match)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_matches, match)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_match(struct ebt_match *match)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_matches, match);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_watcher(struct ebt_watcher *watcher)
++{
++ int ret;
++
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ return ret;
++ if (!list_named_insert(&ebt_watchers, watcher)) {
++ up(&ebt_mutex);
++ return -EEXIST;
++ }
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++
++ return 0;
++}
++
++void ebt_unregister_watcher(struct ebt_watcher *watcher)
++{
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_watchers, watcher);
++ up(&ebt_mutex);
++ MOD_DEC_USE_COUNT;
++}
++
++int ebt_register_table(struct ebt_table *table)
++{
++ struct ebt_table_info *newinfo;
++ int ret;
++
++ if (!table || !table->table ||!table->table->entries ||
++ table->table->entries_size == 0 ||
++ table->table->counters || table->private) {
++ BUGPRINT("Bad table data for ebt_register_table!!!\n");
++ return -EINVAL;
++ }
++
++ newinfo = (struct ebt_table_info *)
++ vmalloc(sizeof(struct ebt_table_info));
++ ret = -ENOMEM;
++ if (!newinfo)
++ return -ENOMEM;
++
++ newinfo->entries = (char *)vmalloc(table->table->entries_size);
++ if (!(newinfo->entries))
++ goto free_newinfo;
++
++ memcpy(newinfo->entries, table->table->entries,
++ table->table->entries_size);
++
++ if (table->table->nentries) {
++ newinfo->counters = (struct ebt_counter *)
++ vmalloc(table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ if (!newinfo->counters)
++ goto free_entries;
++ memset(newinfo->counters, 0, table->table->nentries *
++ sizeof(struct ebt_counter) * smp_num_cpus);
++ }
++ else
++ newinfo->counters = NULL;
++
++ // fill in newinfo and parse the entries
++ newinfo->chainstack = NULL;
++ ret = translate_table(table->table, newinfo);
++ if (ret != 0) {
++ BUGPRINT("Translate_table failed\n");
++ goto free_counters;
++ }
++
++ if (table->check && table->check(newinfo, table->valid_hooks)) {
++ BUGPRINT("The table doesn't like its own initial data, lol\n");
++ return -EINVAL;
++ }
++
++ table->private = newinfo;
++ table->lock = RW_LOCK_UNLOCKED;
++ ret = down_interruptible(&ebt_mutex);
++ if (ret != 0)
++ goto free_counters;
++
++ if (list_named_find(&ebt_tables, table->name)) {
++ ret = -EEXIST;
++ BUGPRINT("Table name already exists\n");
++ goto free_unlock;
++ }
++
++ list_prepend(&ebt_tables, table);
++ up(&ebt_mutex);
++ MOD_INC_USE_COUNT;
++ return 0;
++free_unlock:
++ up(&ebt_mutex);
++free_counters:
++ if (newinfo->counters)
++ vfree(newinfo->counters);
++ if (newinfo->chainstack)
++ vfree(newinfo->chainstack);
++free_entries:
++ vfree(newinfo->entries);
++free_newinfo:
++ vfree(newinfo);
++ return ret;
++}
++
++void ebt_unregister_table(struct ebt_table *table)
++{
++ if (!table) {
++ BUGPRINT("Request to unregister NULL table!!!\n");
++ return;
++ }
++ down(&ebt_mutex);
++ LIST_DELETE(&ebt_tables, table);
++ up(&ebt_mutex);
++ EBT_ENTRY_ITERATE(table->private->entries,
++ table->private->entries_size, ebt_cleanup_entry, NULL);
++ if (table->private->counters)
++ vfree(table->private->counters);
++ if (table->private->entries)
++ vfree(table->private->entries);
++ if (table->private->chainstack)
++ vfree(table->private->chainstack);
++ vfree(table->private);
++ MOD_DEC_USE_COUNT;
++}
++
++// userspace just supplied us with counters
++static int update_counters(void *user, unsigned int len)
++{
++ int i, ret;
++ struct ebt_counter *tmp;
++ struct ebt_replace hlp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&hlp, user, sizeof(hlp)))
++ return -EFAULT;
++
++ if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
++ return -EINVAL;
++ if (hlp.num_counters == 0)
++ return -EINVAL;
++
++ if ( !(tmp = (struct ebt_counter *)
++ vmalloc(hlp.num_counters * sizeof(struct ebt_counter))) ){
++ MEMPRINT("Updata_counters && nomemory\n");
++ return -ENOMEM;
++ }
++
++ hlp.name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
++
++ t = find_table_lock(hlp.name, &ret, &ebt_mutex);
++ if (!t)
++ goto free_tmp;
++
++ if (hlp.num_counters != t->private->nentries) {
++ BUGPRINT("Wrong nr of counters\n");
++ ret = -EINVAL;
++ goto unlock_mutex;
++ }
++
++ if ( copy_from_user(tmp, hlp.counters,
++ hlp.num_counters * sizeof(struct ebt_counter)) ) {
++ BUGPRINT("Updata_counters && !cfu\n");
++ ret = -EFAULT;
++ goto unlock_mutex;
++ }
++
++ // we want an atomic add of the counters
++ write_lock_bh(&t->lock);
++
++ // we add to the counters of the first cpu
++ for (i = 0; i < hlp.num_counters; i++)
++ t->private->counters[i].pcnt += tmp[i].pcnt;
++
++ write_unlock_bh(&t->lock);
++ ret = 0;
++unlock_mutex:
++ up(&ebt_mutex);
++free_tmp:
++ vfree(tmp);
++ return ret;
++}
++
++static inline int ebt_make_matchname(struct ebt_entry_match *m,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)m;
++ if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
++ char *base, char *ubase)
++{
++ char *hlp = ubase - base + (char *)w;
++ if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
++{
++ int ret;
++ char *hlp = ubase - base + (char *)e + e->target_offset;
++ struct ebt_entry_target *t;
++
++ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
++ return 0;
++
++ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
++
++ ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
++ if (ret != 0)
++ return ret;
++ ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
++ if (ret != 0)
++ return ret;
++ if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
++ return -EFAULT;
++ return 0;
++}
++
++// called with ebt_mutex down
++static int copy_everything_to_user(struct ebt_table *t, void *user, int *len)
++{
++ struct ebt_replace tmp;
++ struct ebt_table_info *info = t->private;
++ struct ebt_counter *counterstmp;
++ int i;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp))) {
++ BUGPRINT("Cfu didn't work\n");
++ return -EFAULT;
++ }
++
++ if (*len != sizeof(struct ebt_replace) + info->entries_size +
++ (tmp.num_counters? info->nentries * sizeof(struct ebt_counter): 0)) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ if (tmp.nentries != info->nentries) {
++ BUGPRINT("Nentries wrong\n");
++ return -EINVAL;
++ }
++
++ if (tmp.entries_size != info->entries_size) {
++ BUGPRINT("Wrong size\n");
++ return -EINVAL;
++ }
++
++ // userspace might not need the counters
++ if (tmp.num_counters) {
++ if (tmp.num_counters != info->nentries) {
++ BUGPRINT("Num_counters wrong\n");
++ return -EINVAL;
++ }
++ counterstmp = (struct ebt_counter *)
++ vmalloc(info->nentries * sizeof(struct ebt_counter));
++ if (!counterstmp) {
++ BUGPRINT("Couldn't copy counters, out of memory\n");
++ return -ENOMEM;
++ }
++ write_lock_bh(&t->lock);
++ get_counters(info, counterstmp);
++ write_unlock_bh(&t->lock);
++
++ if (copy_to_user(tmp.counters, counterstmp,
++ info->nentries * sizeof(struct ebt_counter))) {
++ BUGPRINT("Couldn't copy counters to userspace\n");
++ vfree(counterstmp);
++ return -EFAULT;
++ }
++ vfree(counterstmp);
++ }
++
++ if (copy_to_user(tmp.entries, info->entries, info->entries_size)) {
++ BUGPRINT("Couldn't copy entries to userspace\n");
++ return -EFAULT;
++ }
++ // make userspace's life easier
++ memcpy(tmp.hook_entry, info->hook_entry,
++ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ tmp.hook_entry[i] = (struct ebt_entries *)(((char *)
++ (info->hook_entry[i])) - info->entries + tmp.entries);
++ if (copy_to_user(user, &tmp, sizeof(struct ebt_replace))) {
++ BUGPRINT("Couldn't copy ebt_replace to userspace\n");
++ return -EFAULT;
++ }
++ // set the match/watcher/target names right
++ return EBT_ENTRY_ITERATE(info->entries, info->entries_size,
++ ebt_make_names, info->entries, tmp.entries);
++}
++
++static int do_ebt_set_ctl(struct sock *sk,
++ int cmd, void *user, unsigned int len)
++{
++ int ret;
++
++ switch(cmd) {
++ case EBT_SO_SET_ENTRIES:
++ ret = do_replace(user, len);
++ break;
++ case EBT_SO_SET_COUNTERS:
++ ret = update_counters(user, len);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int do_ebt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
++{
++ int ret;
++ struct ebt_replace tmp;
++ struct ebt_table *t;
++
++ if (copy_from_user(&tmp, user, sizeof(tmp)))
++ return -EFAULT;
++
++ t = find_table_lock(tmp.name, &ret, &ebt_mutex);
++ if (!t)
++ return ret;
++
++ switch(cmd) {
++ case EBT_SO_GET_INFO:
++ if (*len != sizeof(struct ebt_replace)){
++ ret = -EINVAL;
++ up(&ebt_mutex);
++ break;
++ }
++ tmp.nentries = t->private->nentries;
++ tmp.entries_size = t->private->entries_size;
++ // userspace needs this to check the chain names
++ tmp.valid_hooks = t->valid_hooks;
++ up(&ebt_mutex);
++ if (copy_to_user(user, &tmp, *len) != 0){
++ BUGPRINT("c2u Didn't work\n");
++ ret = -EFAULT;
++ break;
++ }
++ ret = 0;
++ break;
++
++ case EBT_SO_GET_ENTRIES:
++ ret = copy_everything_to_user(t, user, len);
++ up(&ebt_mutex);
++ break;
++
++ default:
++ up(&ebt_mutex);
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++static struct nf_sockopt_ops ebt_sockopts =
++{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
++ EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
++};
++
++// Copyright (C) 1998 by Ori Pomerantz
++// Print the string to the appropriate tty, the one
++// the current task uses
++static void print_string(char *str)
++{
++ struct tty_struct *my_tty;
++
++ /* The tty for the current task */
++ my_tty = current->tty;
++ if (my_tty != NULL) {
++ (*(my_tty->driver).write)(my_tty, 0, str, strlen(str));
++ (*(my_tty->driver).write)(my_tty, 0, "\015\012", 2);
++ }
++}
++
++static int __init init(void)
++{
++ int ret;
++
++ down(&ebt_mutex);
++ list_named_insert(&ebt_targets, &ebt_standard_target);
++ up(&ebt_mutex);
++ if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
++ return ret;
++
++ print_string("Ebtables v2.0 registered");
++ return 0;
++}
++
++static void __exit fini(void)
++{
++ nf_unregister_sockopt(&ebt_sockopts);
++ print_string("Ebtables v2.0 unregistered");
++}
++
++EXPORT_SYMBOL(ebt_register_table);
++EXPORT_SYMBOL(ebt_unregister_table);
++EXPORT_SYMBOL(ebt_register_match);
++EXPORT_SYMBOL(ebt_unregister_match);
++EXPORT_SYMBOL(ebt_register_watcher);
++EXPORT_SYMBOL(ebt_unregister_watcher);
++EXPORT_SYMBOL(ebt_register_target);
++EXPORT_SYMBOL(ebt_unregister_target);
++EXPORT_SYMBOL(ebt_do_table);
++module_init(init);
++module_exit(fini);
++MODULE_LICENSE("GPL");
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebtables.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,345 @@
++/*
++ * ebtables
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * ebtables.c,v 2.0, April, 2002
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ */
++
++#ifndef __LINUX_BRIDGE_EFF_H
++#define __LINUX_BRIDGE_EFF_H
++#include <linux/if.h> // IFNAMSIZ
++#include <linux/netfilter_bridge.h>
++#include <linux/if_ether.h> // ETH_ALEN
++
++#define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
++
++// [gs]etsockopt numbers
++#define EBT_BASE_CTL 128
++
++#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL)
++#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1)
++#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1)
++
++#define EBT_SO_GET_INFO (EBT_BASE_CTL)
++#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
++#define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
++
++// verdicts >0 are "branches"
++#define EBT_ACCEPT -1
++#define EBT_DROP -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN -4
++#define NUM_STANDARD_TARGETS 4
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
++
++struct ebt_entries {
++ // this field is always set to zero (including userspace).
++ // See EBT_ENTRY_OR_ENTRIES.
++ // Must be same size as ebt_entry.bitmask
++ __u32 distinguisher;
++ // the chain name
++ char name[EBT_CHAIN_MAXNAMELEN];
++ // counter offset for this chain
++ unsigned int counter_offset;
++ // one standard (accept, drop, return) per hook
++ int policy;
++ // nr. of entries
++ __u32 nentries;
++ // entry list
++ __u8 data[0];
++};
++
++// used for the bitmask of struct ebt_entry
++
++// This is a hack to make a difference between an ebt_entry struct and an
++// ebt_entries struct when traversing the entries from start to end.
++// Using this simplifies the code alot, while still being able to use
++// ebt_entries.
++// Contrary, iptables doesn't use something like ebt_entries and therefore uses
++// different techniques for naming the policy and such. So, iptables doesn't
++// need a hack like this.
++#define EBT_ENTRY_OR_ENTRIES 0x01
++// these are the normal masks
++#define EBT_NOPROTO 0x02
++#define EBT_802_3 0x04
++#define EBT_SOURCEMAC 0x08
++#define EBT_DESTMAC 0x10
++#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
++ | EBT_ENTRY_OR_ENTRIES)
++
++#define EBT_IPROTO 0x01
++#define EBT_IIN 0x02
++#define EBT_IOUT 0x04
++#define EBT_ISOURCE 0x8
++#define EBT_IDEST 0x10
++#define EBT_ILOGICALIN 0x20
++#define EBT_ILOGICALOUT 0x40
++#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
++ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
++
++struct ebt_entry_match
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_match *match;
++ } u;
++ // size of data
++ unsigned int match_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_watcher
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_watcher *watcher;
++ } u;
++ // size of data
++ unsigned int watcher_size;
++ unsigned char data[0];
++};
++
++struct ebt_entry_target
++{
++ union {
++ char name[EBT_FUNCTION_MAXNAMELEN];
++ struct ebt_target *target;
++ } u;
++ // size of data
++ unsigned int target_size;
++ unsigned char data[0];
++};
++
++#define EBT_STANDARD_TARGET "standard"
++struct ebt_standard_target
++{
++ struct ebt_entry_target target;
++ int verdict;
++};
++
++// one entry
++struct ebt_entry {
++ // this needs to be the first field
++ __u32 bitmask;
++ __u32 invflags;
++ __u16 ethproto;
++ // the physical in-dev
++ __u8 in[IFNAMSIZ];
++ // the logical in-dev
++ __u8 logical_in[IFNAMSIZ];
++ // the physical out-dev
++ __u8 out[IFNAMSIZ];
++ // the logical out-dev
++ __u8 logical_out[IFNAMSIZ];
++ __u8 sourcemac[ETH_ALEN];
++ __u8 sourcemsk[ETH_ALEN];
++ __u8 destmac[ETH_ALEN];
++ __u8 destmsk[ETH_ALEN];
++ // sizeof ebt_entry + matches
++ __u16 watchers_offset;
++ // sizeof ebt_entry + matches + watchers
++ __u16 target_offset;
++ // sizeof ebt_entry + matches + watchers + target
++ __u16 next_offset;
++ unsigned char elems[0];
++};
++
++struct ebt_replace
++{
++ char name[EBT_TABLE_MAXNAMELEN];
++ unsigned int valid_hooks;
++ // nr of rules in the table
++ unsigned int nentries;
++ // total size of the entries
++ unsigned int entries_size;
++ // start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ // nr of counters userspace expects back
++ unsigned int num_counters;
++ // where the kernel will put the old counters
++ struct ebt_counter *counters;
++ char *entries;
++};
++
++#ifdef __KERNEL__
++
++struct ebt_match
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // 0 == it matches
++ int (*match)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *matchdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
++ void (*destroy)(void *matchdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_watcher
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ void (*watcher)(const struct sk_buff *skb, const struct net_device *in,
++ const struct net_device *out, const void *watcherdata,
++ unsigned int datalen, const struct ebt_counter *c);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
++ void (*destroy)(void *watcherdata, unsigned int datalen);
++ struct module *me;
++};
++
++struct ebt_target
++{
++ struct list_head list;
++ const char name[EBT_FUNCTION_MAXNAMELEN];
++ // returns one of the standard verdicts
++ int (*target)(struct sk_buff **pskb,
++ unsigned int hooknr,
++ const struct net_device *in,
++ const struct net_device *out,
++ const void *targetdata,
++ unsigned int datalen);
++ // 0 == let it in
++ int (*check)(const char *tablename, unsigned int hookmask,
++ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
++ void (*destroy)(void *targetdata, unsigned int datalen);
++ struct module *me;
++};
++
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++ struct ebt_entries *chaininfo; // pointer to chain data
++ struct ebt_entry *e; // pointer to entry data
++ unsigned int n; // n'th entry
++};
++
++struct ebt_table_info
++{
++ // total size of the entries
++ unsigned int entries_size;
++ unsigned int nentries;
++ // pointers to the start of the chains
++ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
++ struct ebt_counter *counters;
++ // room to maintain the stack used for jumping from and into udc
++ struct ebt_chainstack *chainstack;
++ char *entries;
++};
++
++struct ebt_table
++{
++ struct list_head list;
++ char name[EBT_TABLE_MAXNAMELEN];
++ struct ebt_replace *table;
++ unsigned int valid_hooks;
++ rwlock_t lock;
++ // e.g. could be the table explicitly only allows certain
++ // matches, targets, ... 0 == let it in
++ int (*check)(const struct ebt_table_info *info,
++ unsigned int valid_hooks);
++ // the data used by the kernel
++ struct ebt_table_info *private;
++};
++
++extern int ebt_register_table(struct ebt_table *table);
++extern void ebt_unregister_table(struct ebt_table *table);
++extern int ebt_register_match(struct ebt_match *match);
++extern void ebt_unregister_match(struct ebt_match *match);
++extern int ebt_register_watcher(struct ebt_watcher *watcher);
++extern void ebt_unregister_watcher(struct ebt_watcher *watcher);
++extern int ebt_register_target(struct ebt_target *target);
++extern void ebt_unregister_target(struct ebt_target *target);
++extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff **pskb,
++ const struct net_device *in, const struct net_device *out,
++ struct ebt_table *table);
++
++#endif /* __KERNEL__ */
++
++// blatently stolen from ip_tables.h
++// fn returns 0 to continue iteration
++#define EBT_MATCH_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_match *__match; \
++ \
++ for (__i = sizeof(struct ebt_entry); \
++ __i < (e)->watchers_offset; \
++ __i += __match->match_size + \
++ sizeof(struct ebt_entry_match)) { \
++ __match = (void *)(e) + __i; \
++ \
++ __ret = fn(__match , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->watchers_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_WATCHER_ITERATE(e, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry_watcher *__watcher; \
++ \
++ for (__i = e->watchers_offset; \
++ __i < (e)->target_offset; \
++ __i += __watcher->watcher_size + \
++ sizeof(struct ebt_entry_watcher)) { \
++ __watcher = (void *)(e) + __i; \
++ \
++ __ret = fn(__watcher , ## args); \
++ if (__ret != 0) \
++ break; \
++ } \
++ if (__ret == 0) { \
++ if (__i != (e)->target_offset) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \
++({ \
++ unsigned int __i; \
++ int __ret = 0; \
++ struct ebt_entry *__entry; \
++ \
++ for (__i = 0; __i < (size);) { \
++ __entry = (void *)(entries) + __i; \
++ __ret = fn(__entry , ## args); \
++ if (__ret != 0) \
++ break; \
++ if (__entry->bitmask != 0) \
++ __i += __entry->next_offset; \
++ else \
++ __i += sizeof(struct ebt_entries); \
++ } \
++ if (__ret == 0) { \
++ if (__i != (size)) \
++ __ret = -EINVAL; \
++ } \
++ __ret; \
++})
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_arp.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,26 @@
++#ifndef __LINUX_BRIDGE_EBT_ARP_H
++#define __LINUX_BRIDGE_EBT_ARP_H
++
++#define EBT_ARP_OPCODE 0x01
++#define EBT_ARP_HTYPE 0x02
++#define EBT_ARP_PTYPE 0x04
++#define EBT_ARP_SRC_IP 0x08
++#define EBT_ARP_DST_IP 0x10
++#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
++ EBT_ARP_SRC_IP | EBT_ARP_DST_IP)
++#define EBT_ARP_MATCH "arp"
++
++struct ebt_arp_info
++{
++ __u16 htype;
++ __u16 ptype;
++ __u16 opcode;
++ __u32 saddr;
++ __u32 smsk;
++ __u32 daddr;
++ __u32 dmsk;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_ip.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,24 @@
++#ifndef __LINUX_BRIDGE_EBT_IP_H
++#define __LINUX_BRIDGE_EBT_IP_H
++
++#define EBT_IP_SOURCE 0x01
++#define EBT_IP_DEST 0x02
++#define EBT_IP_TOS 0x04
++#define EBT_IP_PROTO 0x08
++#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO)
++#define EBT_IP_MATCH "ip"
++
++// the same values are used for the invflags
++struct ebt_ip_info
++{
++ __u32 saddr;
++ __u32 daddr;
++ __u32 smsk;
++ __u32 dmsk;
++ __u8 tos;
++ __u8 protocol;
++ __u8 bitmask;
++ __u8 invflags;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_vlan.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,20 @@
++#ifndef __LINUX_BRIDGE_EBT_VLAN_H
++#define __LINUX_BRIDGE_EBT_VLAN_H
++
++#define EBT_VLAN_ID 0x01
++#define EBT_VLAN_PRIO 0x02
++#define EBT_VLAN_ENCAP 0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
++#define EBT_VLAN_MATCH "vlan"
++
++struct ebt_vlan_info {
++ __u16 id; /* VLAN ID {1-4095} */
++ __u8 prio; /* VLAN User Priority {0-7} */
++ __u16 encap; /* VLAN Encapsulated frame code {0-65535} */
++ __u8 bitmask; /* Args bitmask bit 1=1 - ID arg,
++ bit 2=1 User-Priority arg, bit 3=1 encap*/
++ __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg,
++ bit 2=1 - inversed Pirority arg */
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_log.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,17 @@
++#ifndef __LINUX_BRIDGE_EBT_LOG_H
++#define __LINUX_BRIDGE_EBT_LOG_H
++
++#define EBT_LOG_IP 0x01 // if the frame is made by ip, log the ip information
++#define EBT_LOG_ARP 0x02
++#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP)
++#define EBT_LOG_PREFIX_SIZE 30
++#define EBT_LOG_WATCHER "log"
++
++struct ebt_log_info
++{
++ __u8 loglevel;
++ __u8 prefix[EBT_LOG_PREFIX_SIZE];
++ __u32 bitmask;
++};
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_nat.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,13 @@
++#ifndef __LINUX_BRIDGE_EBT_NAT_H
++#define __LINUX_BRIDGE_EBT_NAT_H
++
++struct ebt_nat_info
++{
++ unsigned char mac[ETH_ALEN];
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_SNAT_TARGET "snat"
++#define EBT_DNAT_TARGET "dnat"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/netfilter_bridge/ebt_redirect.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,11 @@
++#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
++#define __LINUX_BRIDGE_EBT_REDIRECT_H
++
++struct ebt_redirect_info
++{
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
++};
++#define EBT_REDIRECT_TARGET "redirect"
++
++#endif
+--- /dev/null Thu Aug 24 11:00:32 2000
++++ ebt2.0pre9/include/linux/br_db.h Thu Jun 27 19:11:50 2002
+@@ -0,0 +1,53 @@
++/*
++ * bridge ethernet protocol filter
++ *
++ * Authors:
++ * Bart De Schuymer <bart.de.schuymer@pandora.be>
++ *
++ * br_db.h,v 1.1 2001/04/16
++ *
++ * This code is stongly inspired on the iptables code which is
++ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef __LINUX_BRIDGE_DB_H
++#define __LINUX_BRIDGE_DB_H
++#include <linux/if.h> /* IFNAMSIZ */
++#ifdef __KERNEL__
++#include <linux/if_bridge.h>
++#include <linux/netfilter_bridge.h>
++#else
++#include <linux/netfilter_bridge.h>
++#endif
++#define BRDB_BASE_CTL 135
++
++#define BRDB_SO_SET_ALLOWDB (BRDB_BASE_CTL)
++#define BRDB_SO_SET_MAX (BRDB_SO_SET_ALLOWDB+1)
++
++#define BRDB_SO_GET_DBINFO (BRDB_BASE_CTL)
++#define BRDB_SO_GET_DB (BRDB_SO_GET_DBINFO+1)
++#define BRDB_SO_GET_MAX (BRDB_SO_GET_DB+1)
++
++#define BRDB_NODB 0
++#define BRDB_DB 1
++
++#define INITIAL_DBSIZE 10
++#define IDENTIFY802_3 46
++
++struct brdb_dbinfo {
++ __u32 nentries;
++};
++
++struct brdb_dbentry {
++ __u8 in[IFNAMSIZ];
++ __u8 out[IFNAMSIZ];
++ __u16 ethproto;
++ __u32 hook;
++};
++
++#endif
diff --git a/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff
new file mode 100644
index 0000000..cb7c8c0
--- /dev/null
+++ b/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff
@@ -0,0 +1,1155 @@
+--- linux/net/bridge/br_private.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/br_private.h Thu Jun 27 19:11:50 2002
+@@ -4,7 +4,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+--- linux/include/linux/if_bridge.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/include/linux/if_bridge.h Thu Jun 27 19:11:50 2002
+@@ -4,7 +4,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+--- linux/net/bridge/br_input.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/br_input.c Thu Jun 27 19:11:50 2002
+@@ -5,7 +5,7 @@
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+--- linux/net/bridge/netfilter/ebtable_filter.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_filter.c Thu Jun 27 19:11:50 2002
+@@ -17,17 +17,16 @@
+
+ static struct ebt_entries initial_chains[] =
+ {
+- {0, EBT_ACCEPT, 0},
+- {0, EBT_ACCEPT, 0},
+- {0, EBT_ACCEPT, 0}
++ {0, "INPUT", 0, EBT_ACCEPT, 0},
++ {0, "FORWARD", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0}
+ };
+
+-static struct ebt_replace initial_table =
+-{
++static struct ebt_replace initial_table =
++{
+ "filter", FILTER_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+ { [NF_BR_LOCAL_IN]&initial_chains[0], [NF_BR_FORWARD]&initial_chains[1],
+- [NF_BR_LOCAL_OUT]&initial_chains[2] },{},
+- 0, NULL, (char *)initial_chains
++ [NF_BR_LOCAL_OUT]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+ };
+
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebtable_nat.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_nat.c Thu Jun 27 19:11:50 2002
+@@ -17,17 +17,16 @@
+
+ static struct ebt_entries initial_chains[] =
+ {
+- {0, EBT_ACCEPT, 0},
+- {0, EBT_ACCEPT, 0},
+- {0, EBT_ACCEPT, 0}
++ {0, "PREROUTING", 0, EBT_ACCEPT, 0},
++ {0, "OUTPUT", 0, EBT_ACCEPT, 0},
++ {0, "POSTROUTING", 0, EBT_ACCEPT, 0}
+ };
+
+ static struct ebt_replace initial_table =
+ {
+ "nat", NAT_VALID_HOOKS, 0, 3 * sizeof(struct ebt_entries),
+ { [NF_BR_PRE_ROUTING]&initial_chains[0], [NF_BR_LOCAL_OUT]&initial_chains[1],
+- [NF_BR_POST_ROUTING]&initial_chains[2] }, {},
+- 0, NULL, (char *)initial_chains
++ [NF_BR_POST_ROUTING]&initial_chains[2] }, 0, NULL, (char *)initial_chains
+ };
+
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebtable_broute.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtable_broute.c Thu Jun 27 19:11:50 2002
+@@ -21,13 +21,12 @@
+ // EBT_ACCEPT means the frame will be bridged
+ // EBT_DROP means the frame will be routed
+ static struct ebt_entries initial_chain =
+- {0, EBT_ACCEPT, 0};
++ {0, "BROUTE", 0, EBT_ACCEPT, 0};
+
+ static struct ebt_replace initial_table =
+ {
+ "broute", 1 << NF_BR_BROUTING, 0, sizeof(struct ebt_entries),
+- { [NF_BR_BROUTING]&initial_chain}, {},
+- 0, NULL, (char *)&initial_chain
++ { [NF_BR_BROUTING]&initial_chain}, 0, NULL, (char *)&initial_chain
+ };
+
+ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
+--- linux/net/bridge/netfilter/ebt_redirect.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_redirect.c Thu Jun 27 19:11:50 2002
+@@ -16,7 +16,7 @@
+ #include <net/sock.h>
+ #include "../br_private.h"
+
+-static __u8 ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+ {
+@@ -28,17 +28,17 @@
+ return infostuff->target;
+ }
+
+-static int ebt_target_redirect_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_redirect_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_redirect_info *infostuff = (struct ebt_redirect_info *) data;
+
+- if ( (strcmp(tablename, "nat") || hooknr != NF_BR_PRE_ROUTING) &&
+- (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ if ( (strcmp(tablename, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (datalen != sizeof(struct ebt_redirect_info))
+ return -EINVAL;
+- if (infostuff->target >= NUM_STANDARD_TARGETS)
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ return -EINVAL;
+ return 0;
+ }
+--- linux/net/bridge/netfilter/ebt_arp.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_arp.c Thu Jun 27 19:11:50 2002
+@@ -68,7 +68,7 @@
+ return 0;
+ }
+
+-static int ebt_arp_check(const char *tablename, unsigned int hooknr,
++static int ebt_arp_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_arp_info *infostuff = (struct ebt_arp_info *) data;
+--- linux/net/bridge/netfilter/ebt_ip.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_ip.c Thu Jun 27 19:11:50 2002
+@@ -39,7 +39,7 @@
+ return 0;
+ }
+
+-static int ebt_ip_check(const char *tablename, unsigned int hooknr,
++static int ebt_ip_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_ip_info *infostuff = (struct ebt_ip_info *) data;
+--- linux/net/bridge/netfilter/ebt_vlan.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_vlan.c Thu Jun 27 19:11:50 2002
+@@ -5,7 +5,7 @@
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ * Nick Fedchik <nick@fedchik.org.ua>
+ *
+- * May, 2002
++ * June, 2002
+ */
+
+ #include <linux/netfilter_bridge/ebtables.h>
+@@ -18,6 +18,8 @@
+ MODULE_PARM (debug, "0-1b");
+ MODULE_PARM_DESC (debug, "debug=1 is turn on debug messages");
+
++#define MODULE_VERSION "0.2"
++
+ static int ebt_filter_vlan (const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+@@ -30,43 +32,64 @@
+ (struct vlan_ethhdr *) skb->mac.raw;
+ unsigned short v_id;
+ unsigned short v_prio;
++ unsigned short v_TCI;
+
+ /*
+- * Calculate 802.1Q VLAN ID and Priority
+- * Reserved one bit (13) for CFI
++ * Calculate 802.1Q VLAN ID and user_priority from
++ * Tag Control Information (TCI) field.
++ * Reserved one bit (13) for CFI (Canonical Format Indicator)
+ */
+- v_id = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) & 0xFFF;
+- v_prio = ntohs ((unsigned short) vlanethhdr->h_vlan_TCI) >> 13;
++ v_TCI = ntohs (vlanethhdr->h_vlan_TCI);
++ v_id = v_TCI & 0xFFF;
++ v_prio = v_TCI >> 13;
+
+ /*
+ * Checking VLANs
+ */
+ if (infostuff->bitmask & EBT_VLAN_ID) { /* Is VLAN ID parsed? */
+ if (!((infostuff->id == v_id)
+- ^ !!(infostuff->invflags & EBT_VLAN_ID)))
+- return 1;
++ ^ !!(infostuff->invflags & EBT_VLAN_ID)))
++ return 1;
+ if (debug)
+ printk (KERN_DEBUG
+ "ebt_vlan: matched ID=%s%d (mask=%X)\n",
+- (infostuff->invflags & EBT_VLAN_ID) ? "!" : "",
+- infostuff->id,
+- (unsigned char) infostuff->bitmask);
++ (infostuff->
++ invflags & EBT_VLAN_ID) ? "!" : "",
++ infostuff->id, infostuff->bitmask);
+ }
+ /*
+- * Checking Priority
++ * Checking User Priority
+ */
+ if (infostuff->bitmask & EBT_VLAN_PRIO) { /* Is VLAN Prio parsed? */
+- if (!( (infostuff->prio == v_prio)
+- ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
+- return 1; /* missed */
++ if (!((infostuff->prio == v_prio)
++ ^ !!(infostuff->invflags & EBT_VLAN_PRIO)))
++ return 1; /* missed */
+ if (debug)
+ printk (KERN_DEBUG
+ "ebt_vlan: matched Prio=%s%d (mask=%X)\n",
+- (infostuff->invflags & EBT_VLAN_PRIO) ? "!" : "",
+- infostuff->prio,
+- (unsigned char) infostuff->bitmask);
++ (infostuff->
++ invflags & EBT_VLAN_PRIO) ? "!" : "",
++ infostuff->prio, infostuff->bitmask);
+ }
+ /*
++ * Checking for Encapsulated proto
++ */
++ if (infostuff->bitmask & EBT_VLAN_ENCAP) { /* Is VLAN Encap parsed? */
++ if (!
++ ((infostuff->encap ==
++ vlanethhdr->h_vlan_encapsulated_proto)
++ ^ !!(infostuff->invflags & EBT_VLAN_ENCAP)))
++ return 1; /* missed */
++ if (debug)
++ printk (KERN_DEBUG
++ "ebt_vlan: matched encap=%s%2.4X (mask=%X)\n",
++ (infostuff->
++ invflags & EBT_VLAN_ENCAP) ? "!" : "",
++ ntohs (infostuff->encap),
++ infostuff->bitmask);
++ }
++
++ /*
+ * rule matched
+ */
+ return 0;
+@@ -76,7 +99,7 @@
+ * ebt_vlan_check() is called when userspace delivers the table to the kernel,
+ * * it is called to check that userspace doesn't give a bad table.
+ */
+-static int ebt_vlan_check (const char *tablename, unsigned int hooknr,
++static int ebt_vlan_check (const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data,
+ unsigned int datalen)
+ {
+@@ -96,7 +119,10 @@
+ }
+
+ static struct ebt_match filter_vlan = {
+- {NULL, NULL}, EBT_VLAN_MATCH, ebt_filter_vlan, ebt_vlan_check,
++ {NULL, NULL},
++ EBT_VLAN_MATCH,
++ ebt_filter_vlan,
++ ebt_vlan_check,
+ NULL,
+ THIS_MODULE
+ };
+@@ -104,10 +130,11 @@
+ static int __init init (void)
+ {
+ printk (KERN_INFO
+- "ebt_vlan: 802.1Q VLAN matching module for EBTables\n");
++ "ebt_vlan: 802.1Q VLAN matching module for EBTables "
++ MODULE_VERSION "\n");
+ if (debug)
+ printk (KERN_DEBUG
+- "ebt_vlan: 802.1Q matching debug is on\n");
++ "ebt_vlan: 802.1Q rule matching debug is on\n");
+ return ebt_register_match (&filter_vlan);
+ }
+
+@@ -120,5 +147,6 @@
+ module_exit (fini);
+ EXPORT_NO_SYMBOLS;
+ MODULE_AUTHOR ("Nick Fedchik <nick@fedchik.org.ua>");
+-MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v0.1");
++MODULE_DESCRIPTION ("802.1Q VLAN matching module for ebtables, v"
++ MODULE_VERSION);
+ MODULE_LICENSE ("GPL");
+--- linux/net/bridge/netfilter/ebt_log.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_log.c Thu Jun 27 19:11:50 2002
+@@ -17,7 +17,7 @@
+
+ static spinlock_t ebt_log_lock = SPIN_LOCK_UNLOCKED;
+
+-static int ebt_log_check(const char *tablename, unsigned int hooknr,
++static int ebt_log_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_log_info *loginfo = (struct ebt_log_info *)data;
+--- linux/net/bridge/netfilter/ebt_snat.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_snat.c Thu Jun 27 19:11:50 2002
+@@ -15,7 +15,7 @@
+ #include <linux/module.h>
+ #include <net/sock.h>
+
+-static __u8 ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_snat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+ {
+@@ -26,7 +26,7 @@
+ return infostuff->target;
+ }
+
+-static int ebt_target_snat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_snat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+@@ -35,9 +35,9 @@
+ return -EINVAL;
+ if (datalen != sizeof(struct ebt_nat_info))
+ return -EINVAL;
+- if (hooknr != NF_BR_POST_ROUTING)
++ if (hookmask & ~(1 << NF_BR_POST_ROUTING))
+ return -EINVAL;
+- if (infostuff->target >= NUM_STANDARD_TARGETS)
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ return -EINVAL;
+ return 0;
+ }
+--- linux/net/bridge/netfilter/ebt_dnat.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebt_dnat.c Thu Jun 27 19:11:50 2002
+@@ -15,7 +15,7 @@
+ #include <linux/module.h>
+ #include <net/sock.h>
+
+-static __u8 ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
++static int ebt_target_dnat(struct sk_buff **pskb, unsigned int hooknr,
+ const struct net_device *in, const struct net_device *out,
+ const void *data, unsigned int datalen)
+ {
+@@ -26,18 +26,18 @@
+ return infostuff->target;
+ }
+
+-static int ebt_target_dnat_check(const char *tablename, unsigned int hooknr,
++static int ebt_target_dnat_check(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *data, unsigned int datalen)
+ {
+ struct ebt_nat_info *infostuff = (struct ebt_nat_info *) data;
+
+- if ( (strcmp(tablename, "nat") ||
+- (hooknr != NF_BR_PRE_ROUTING && hooknr != NF_BR_LOCAL_OUT)) &&
+- (strcmp(tablename, "broute") || hooknr != NF_BR_BROUTING) )
++ if ( (strcmp(tablename, "nat") ||
++ (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) &&
++ (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) )
+ return -EINVAL;
+ if (datalen != sizeof(struct ebt_nat_info))
+ return -EINVAL;
+- if (infostuff->target >= NUM_STANDARD_TARGETS)
++ if (infostuff->target < -NUM_STANDARD_TARGETS || infostuff->target >= 0)
+ return -EINVAL;
+ return 0;
+ }
+--- linux/net/bridge/netfilter/ebtables.c Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/net/bridge/netfilter/ebtables.c Thu Jun 27 19:11:50 2002
+@@ -90,27 +90,35 @@
+ if (!device)
+ return 1;
+ return strncmp(entry, device->name, IFNAMSIZ);
+-}
++}
+
+ // Do some firewalling
+ unsigned int ebt_do_table (unsigned int hook, struct sk_buff **pskb,
+ const struct net_device *in, const struct net_device *out,
+ struct ebt_table *table)
+ {
+- int i, nentries;
++ int i, j, nentries;
+ struct ebt_entry *point;
+ struct ebt_counter *counter_base;
+ struct ebt_entry_target *t;
+- __u8 verdict;
++ int verdict, sp = 0;
++ struct ebt_chainstack *cs;
++ struct ebt_entries *chaininfo;
++ char *base;
+
+ read_lock_bh(&table->lock);
++ cs = table->private->chainstack;
++ chaininfo = table->private->hook_entry[hook];
+ nentries = table->private->hook_entry[hook]->nentries;
+ point = (struct ebt_entry *)(table->private->hook_entry[hook]->data);
+- counter_base = table->private->counters +
+- cpu_number_map(smp_processor_id()) * table->private->nentries +
+- table->private->counter_entry[hook];
++ #define cb_base table->private->counters + \
++ cpu_number_map(smp_processor_id()) * table->private->nentries
++ counter_base = cb_base + table->private->hook_entry[hook]->counter_offset;
+ #define FWINV(bool,invflg) ((bool) ^ !!(point->invflags & invflg))
+- for (i = 0; i < nentries; i++) {
++ // base for chain jumps
++ base = (char *)chaininfo;
++ i = 0;
++ while (i < nentries) {
+ if ( ( point->bitmask & EBT_NOPROTO ||
+ FWINV(point->ethproto == ((**pskb).mac.ethernet)->h_proto,
+ EBT_IPROTO)
+@@ -125,24 +133,23 @@
+ (point->logical_out), &out->br_port->br->dev), EBT_ILOGICALOUT))
+
+ ) {
+- char hlpmac[6];
+- int j;
+-
+ if (point->bitmask & EBT_SOURCEMAC) {
++ verdict = 0;
+ for (j = 0; j < 6; j++)
+- hlpmac[j] = ((**pskb).mac.ethernet)->
+- h_source[j] & point->sourcemsk[j];
+- if (FWINV(!!memcmp(point->sourcemac, hlpmac,
+- ETH_ALEN), EBT_ISOURCE) )
++ verdict |= (((**pskb).mac.ethernet)->
++ h_source[j] ^ point->sourcemac[j]) &
++ point->sourcemsk[j];
++ if (FWINV(!!verdict, EBT_ISOURCE) )
+ goto letscontinue;
+ }
+
+ if (point->bitmask & EBT_DESTMAC) {
++ verdict = 0;
+ for (j = 0; j < 6; j++)
+- hlpmac[j] = ((**pskb).mac.ethernet)->
+- h_dest[j] & point->destmsk[j];
+- if (FWINV(!!memcmp(point->destmac, hlpmac,
+- ETH_ALEN), EBT_IDEST) )
++ verdict |= (((**pskb).mac.ethernet)->
++ h_dest[j] ^ point->destmac[j]) &
++ point->destmsk[j];
++ if (FWINV(!!verdict, EBT_IDEST) )
+ goto letscontinue;
+ }
+
+@@ -175,20 +182,56 @@
+ read_unlock_bh(&table->lock);
+ return NF_DROP;
+ }
+- if (verdict != EBT_CONTINUE) {
++ if (verdict == EBT_RETURN) {
++letsreturn:
++ if (sp == 0)
++ // act like this is EBT_CONTINUE
++ goto letscontinue;
++ sp--;
++ // put all the local variables right
++ i = cs[sp].n;
++ chaininfo = cs[sp].chaininfo;
++ nentries = chaininfo->nentries;
++ point = cs[sp].e;
++ counter_base = cb_base +
++ chaininfo->counter_offset;
++ continue;
++ }
++ if (verdict == EBT_CONTINUE)
++ goto letscontinue;
++ if (verdict < 0) {
++ BUGPRINT("bogus standard verdict\n");
++ read_unlock_bh(&table->lock);
++ return NF_DROP;
++ }
++ // jump to a udc
++ cs[sp].n = i + 1;
++ cs[sp].chaininfo = chaininfo;
++ cs[sp].e = (struct ebt_entry *)
++ (((char *)point) + point->next_offset);
++ i = 0;
++ chaininfo = (struct ebt_entries *) (base + verdict);
++ if (chaininfo->distinguisher) {
++ BUGPRINT("jump to non-chain\n");
+ read_unlock_bh(&table->lock);
+- BUGPRINT("Illegal target while "
+- "firewalling!!\n");
+- // Try not to get oopsen
+ return NF_DROP;
+ }
++ nentries = chaininfo->nentries;
++ point = (struct ebt_entry *)chaininfo->data;
++ counter_base = cb_base + chaininfo->counter_offset;
++ sp++;
++ continue;
+ }
+ letscontinue:
+ point = (struct ebt_entry *)
+ (((char *)point) + point->next_offset);
++ i++;
+ }
+
+- if ( table->private->hook_entry[hook]->policy == EBT_ACCEPT ) {
++ // I actually like this :)
++ if (chaininfo->policy == EBT_RETURN)
++ goto letsreturn;
++ if (chaininfo->policy == EBT_ACCEPT) {
+ read_unlock_bh(&table->lock);
+ return NF_ACCEPT;
+ }
+@@ -268,7 +311,7 @@
+
+ static inline int
+ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
+- const char *name, unsigned int hook, unsigned int *cnt)
++ const char *name, unsigned int hookmask, unsigned int *cnt)
+ {
+ struct ebt_match *match;
+ int ret;
+@@ -282,7 +325,7 @@
+ __MOD_INC_USE_COUNT(match->me);
+ up(&ebt_mutex);
+ if (match->check &&
+- match->check(name, hook, e, m->data, m->match_size) != 0) {
++ match->check(name, hookmask, e, m->data, m->match_size) != 0) {
+ BUGPRINT("match->check failed\n");
+ if (match->me)
+ __MOD_DEC_USE_COUNT(match->me);
+@@ -294,7 +337,7 @@
+
+ static inline int
+ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
+- const char *name, unsigned int hook, unsigned int *cnt)
++ const char *name, unsigned int hookmask, unsigned int *cnt)
+ {
+ struct ebt_watcher *watcher;
+ int ret;
+@@ -308,7 +351,7 @@
+ __MOD_INC_USE_COUNT(watcher->me);
+ up(&ebt_mutex);
+ if (watcher->check &&
+- watcher->check(name, hook, e, w->data, w->watcher_size) != 0) {
++ watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
+ BUGPRINT("watcher->check failed\n");
+ if (watcher->me)
+ __MOD_DEC_USE_COUNT(watcher->me);
+@@ -324,7 +367,7 @@
+ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
+ struct ebt_table_info *newinfo, char *base, char *limit,
+ struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
+- unsigned int *totalcnt, unsigned int valid_hooks)
++ unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+ {
+ int i;
+
+@@ -336,7 +379,8 @@
+ break;
+ }
+ // beginning of a new chain
+- if (i != NF_BR_NUMHOOKS) {
++ // if i == NF_BR_NUMHOOKS it must be a user defined chain
++ if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+ if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
+ // we make userspace set this right,
+ // so there is no misunderstanding
+@@ -359,13 +403,23 @@
+ }
+ if (((struct ebt_entries *)e)->policy != EBT_DROP &&
+ ((struct ebt_entries *)e)->policy != EBT_ACCEPT) {
+- BUGPRINT("bad policy\n");
++ // only RETURN from udc
++ if (i != NF_BR_NUMHOOKS ||
++ ((struct ebt_entries *)e)->policy != EBT_RETURN) {
++ BUGPRINT("bad policy\n");
++ return -EINVAL;
++ }
++ }
++ if (i == NF_BR_NUMHOOKS) // it's a user defined chain
++ (*udc_cnt)++;
++ else
++ newinfo->hook_entry[i] = (struct ebt_entries *)e;
++ if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
++ BUGPRINT("counter_offset != totalcnt");
+ return -EINVAL;
+ }
+ *n = ((struct ebt_entries *)e)->nentries;
+ *cnt = 0;
+- newinfo->hook_entry[i] = (struct ebt_entries *)e;
+- newinfo->counter_entry[i] = *totalcnt;
+ return 0;
+ }
+ // a plain old entry, heh
+@@ -375,21 +429,55 @@
+ BUGPRINT("entry offsets not in right order\n");
+ return -EINVAL;
+ }
+- if (((char *)e) + e->next_offset - newinfo->entries > limit - base) {
+- BUGPRINT("entry offsets point too far\n");
++ // this is not checked anywhere else
++ if (e->next_offset - e->target_offset < sizeof(struct ebt_entry_target)) {
++ BUGPRINT("target size too small\n");
+ return -EINVAL;
+ }
+
+- if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0) {
+- BUGPRINT("EBT_ENTRY_OR_ENTRIES should be set in "
+- "bitmask for an entry\n");
+- return -EINVAL;
+- }
+ (*cnt)++;
+ (*totalcnt)++;
+ return 0;
+ }
+
++struct ebt_cl_stack
++{
++ struct ebt_chainstack cs;
++ int from;
++ unsigned int hookmask;
++};
++
++// we need these positions to check that the jumps to a different part of the
++// entries is a jump to the beginning of a new chain.
++static inline int
++ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
++ struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
++ struct ebt_cl_stack *udc)
++{
++ int i;
++
++ // we're only interested in chain starts
++ if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
++ return 0;
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if ((valid_hooks & (1 << i)) == 0)
++ continue;
++ if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
++ break;
++ }
++ // only care about udc
++ if (i != NF_BR_NUMHOOKS)
++ return 0;
++
++ udc[*n].cs.chaininfo = (struct ebt_entries *)e;
++ // these initialisations are depended on later in check_chainloops()
++ udc[*n].cs.n = 0;
++ udc[*n].hookmask = 0;
++
++ (*n)++;
++ return 0;
++}
++
+ static inline int
+ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ {
+@@ -418,11 +506,12 @@
+
+ static inline int
+ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+- const char *name, unsigned int *cnt, unsigned int valid_hooks)
++ const char *name, unsigned int *cnt, unsigned int valid_hooks,
++ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+ {
+ struct ebt_entry_target *t;
+ struct ebt_target *target;
+- unsigned int i, j, hook = 0;
++ unsigned int i, j, hook = 0, hookmask = 0;
+ int ret;
+
+ // Don't mess with the struct ebt_entries
+@@ -454,18 +543,29 @@
+ else
+ break;
+ }
++ if (i < NF_BR_NUMHOOKS)
++ hookmask = (1 << hook);
++ else {
++ for (i = 0; i < udc_cnt; i++)
++ if ((char *)(cl_s[i].cs.chaininfo) > (char *)e)
++ break;
++ if (i == 0)
++ hookmask = (1 << hook);
++ else
++ hookmask = cl_s[i - 1].hookmask;
++ }
+ i = 0;
+- ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hook, &i);
++ ret = EBT_MATCH_ITERATE(e, ebt_check_match, e, name, hookmask, &i);
+ if (ret != 0)
+ goto cleanup_matches;
+ j = 0;
+- ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hook, &j);
++ ret = EBT_WATCHER_ITERATE(e, ebt_check_watcher, e, name, hookmask, &j);
+ if (ret != 0)
+ goto cleanup_watchers;
+ t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
+ target = find_target_lock(t->u.name, &ret, &ebt_mutex);
+- if (!target)
++ if (!target)
+ goto cleanup_watchers;
+ if (target->me)
+ __MOD_INC_USE_COUNT(target->me);
+@@ -479,14 +579,14 @@
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+- if (((struct ebt_standard_target *)t)->verdict >=
+- NUM_STANDARD_TARGETS) {
++ if (((struct ebt_standard_target *)t)->verdict <
++ -NUM_STANDARD_TARGETS) {
+ BUGPRINT("Invalid standard target\n");
+ ret = -EFAULT;
+ goto cleanup_watchers;
+ }
+ } else if (t->u.target->check &&
+- t->u.target->check(name, hook, e, t->data,
++ t->u.target->check(name, hookmask, e, t->data,
+ t->target_size) != 0) {
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+@@ -523,12 +623,83 @@
+ return 0;
+ }
+
++// checks for loops and sets the hook mask for udc
++// the hook mask for udc tells us from which base chains the udc can be
++// accessed. This mask is a parameter to the check() functions of the extensions
++int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
++ unsigned int udc_cnt, unsigned int hooknr, char *base)
++{
++ int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
++ struct ebt_entry *e = (struct ebt_entry *)chain->data;
++ struct ebt_entry_target *t;
++
++ while (pos < nentries || chain_nr != -1) {
++ // end of udc, go back one 'recursion' step
++ if (pos == nentries) {
++ // put back values of the time when this chain was called
++ e = cl_s[chain_nr].cs.e;
++ if (cl_s[chain_nr].from != -1)
++ nentries = cl_s[cl_s[chain_nr].from].cs.chaininfo->nentries;
++ else
++ nentries = chain->nentries;
++ pos = cl_s[chain_nr].cs.n;
++ // make sure we won't see a loop that isn't one
++ cl_s[chain_nr].cs.n = 0;
++ chain_nr = cl_s[chain_nr].from;
++ if (pos == nentries)
++ continue;
++ }
++ t = (struct ebt_entry_target *)
++ (((char *)e) + e->target_offset);
++ t->u.name[EBT_FUNCTION_MAXNAMELEN - 1] = '\0';
++ if (strcmp(t->u.name, EBT_STANDARD_TARGET))
++ goto letscontinue;
++ if (e->target_offset + sizeof(struct ebt_standard_target) >
++ e->next_offset) {
++ BUGPRINT("Standard target size too big\n");
++ return -1;
++ }
++ verdict = ((struct ebt_standard_target *)t)->verdict;
++ if (verdict >= 0) { // jump to another chain
++ struct ebt_entries *hlp2 =
++ (struct ebt_entries *)(base + verdict);
++ for (i = 0; i < udc_cnt; i++)
++ if (hlp2 == cl_s[i].cs.chaininfo)
++ break;
++ // bad destination or loop
++ if (i == udc_cnt) {
++ BUGPRINT("bad destination\n");
++ return -1;
++ }
++ if (cl_s[i].cs.n) {
++ BUGPRINT("loop\n");
++ return -1;
++ }
++ cl_s[i].cs.n = pos + 1;
++ pos = 0;
++ cl_s[i].cs.e = ((void *)e + e->next_offset);
++ e = (struct ebt_entry *)(hlp2->data);
++ nentries = hlp2->nentries;
++ cl_s[i].from = chain_nr;
++ chain_nr = i;
++ // this udc is accessible from the base chain for hooknr
++ cl_s[i].hookmask |= (1 << hooknr);
++ continue;
++ }
++letscontinue:
++ e = (void *)e + e->next_offset;
++ pos++;
++ }
++ return 0;
++}
++
+ // do the parsing of the table/chains/entries/matches/watchers/targets, heh
+ static int translate_table(struct ebt_replace *repl,
+ struct ebt_table_info *newinfo)
+ {
+- unsigned int i, j, k;
++ unsigned int i, j, k, udc_cnt;
+ int ret;
++ struct ebt_cl_stack *cl_s = NULL; // used in the checking for chain loops
+
+ i = 0;
+ while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+@@ -553,10 +724,8 @@
+ i = j;
+ }
+
+- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+ newinfo->hook_entry[i] = NULL;
+- newinfo->counter_entry[i] = 0;
+- }
+
+ newinfo->entries_size = repl->entries_size;
+ newinfo->nentries = repl->nentries;
+@@ -566,10 +735,11 @@
+ j = 0; // holds the up to now counted entries for the chain
+ k = 0; // holds the total nr. of entries, should equal
+ // newinfo->nentries afterwards
++ udc_cnt = 0; // will hold the nr. of user defined chains (udc)
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_check_entry_size_and_hooks, newinfo, repl->entries,
+ repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
+- repl->valid_hooks);
++ &udc_cnt, repl->valid_hooks);
+
+ if (ret != 0)
+ return ret;
+@@ -587,22 +757,70 @@
+ // check if all valid hooks have a chain
+ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+ if (newinfo->hook_entry[i] == NULL &&
+- (repl->valid_hooks & (1 << i))){
++ (repl->valid_hooks & (1 << i))) {
+ BUGPRINT("Valid hook without chain\n");
+ return -EINVAL;
+ }
+ }
+
++ // Get the location of the udc, put them in an array
++ // While we're at it, allocate the chainstack
++ if (udc_cnt) {
++ // this will get free'd in do_replace()/ebt_register_table()
++ // if an error occurs
++ newinfo->chainstack = (struct ebt_chainstack *)
++ vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
++ if (!newinfo->chainstack)
++ return -ENOMEM;
++ cl_s = (struct ebt_cl_stack *)
++ vmalloc(udc_cnt * sizeof(struct ebt_cl_stack));
++ if (!cl_s)
++ return -ENOMEM;
++ i = 0; // the i'th udc
++ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
++ ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
++ repl->valid_hooks, cl_s);
++ // sanity check
++ if (i != udc_cnt) {
++ BUGPRINT("i != udc_cnt\n");
++ vfree(cl_s);
++ return -EFAULT;
++ }
++ }
++
++ // Check for loops
++ for (i = 0; i < NF_BR_NUMHOOKS; i++)
++ if (repl->valid_hooks & (1 << i))
++ if (check_chainloops(newinfo->hook_entry[i],
++ cl_s, udc_cnt, i, newinfo->entries)) {
++ if (cl_s)
++ vfree(cl_s);
++ return -EINVAL;
++ }
++
++ // we now know the following (along with E=mc²):
++ // - the nr of entries in each chain is right
++ // - the size of the allocated space is right
++ // - all valid hooks have a corresponding chain
++ // - there are no loops
++ // - wrong data can still be on the level of a single entry
++ // - could be there are jumps to places that are not the
++ // beginning of a chain. This can only occur in chains that
++ // are not accessible from any base chains, so we don't care.
++
+ // we just don't trust anything
+ repl->name[EBT_TABLE_MAXNAMELEN - 1] = '\0';
+ // used to know what we need to clean up if something goes wrong
+ i = 0;
+ ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+- ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks);
++ ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
++ cl_s, udc_cnt);
+ if (ret != 0) {
+ EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
+ ebt_cleanup_entry, &i);
+ }
++ if (cl_s)
++ vfree(cl_s);
+ return ret;
+ }
+
+@@ -690,6 +908,8 @@
+ else
+ counterstmp = NULL;
+
++ // this can get initialized by translate_table()
++ newinfo->chainstack = NULL;
+ ret = translate_table(&tmp, newinfo);
+
+ if (ret != 0)
+@@ -739,6 +959,8 @@
+ vfree(table->entries);
+ if (table->counters)
+ vfree(table->counters);
++ if (table->chainstack)
++ vfree(table->chainstack);
+ vfree(table);
+
+ if (counterstmp)
+@@ -752,6 +974,9 @@
+ free_counterstmp:
+ if (counterstmp)
+ vfree(counterstmp);
++ // can be initialized in translate_table()
++ if (newinfo->chainstack)
++ vfree(newinfo->chainstack);
+ free_entries:
+ if (newinfo->entries)
+ vfree(newinfo->entries);
+@@ -877,6 +1102,7 @@
+ newinfo->counters = NULL;
+
+ // fill in newinfo and parse the entries
++ newinfo->chainstack = NULL;
+ ret = translate_table(table->table, newinfo);
+ if (ret != 0) {
+ BUGPRINT("Translate_table failed\n");
+@@ -909,6 +1135,8 @@
+ free_counters:
+ if (newinfo->counters)
+ vfree(newinfo->counters);
++ if (newinfo->chainstack)
++ vfree(newinfo->chainstack);
+ free_entries:
+ vfree(newinfo->entries);
+ free_newinfo:
+@@ -931,6 +1159,8 @@
+ vfree(table->private->counters);
+ if (table->private->entries)
+ vfree(table->private->entries);
++ if (table->private->chainstack)
++ vfree(table->private->chainstack);
+ vfree(table->private);
+ MOD_DEC_USE_COUNT;
+ }
+@@ -1091,8 +1321,6 @@
+ return -EFAULT;
+ }
+ // make userspace's life easier
+- memcpy(tmp.counter_entry, info->counter_entry,
+- NF_BR_NUMHOOKS * sizeof(int));
+ memcpy(tmp.hook_entry, info->hook_entry,
+ NF_BR_NUMHOOKS * sizeof(struct ebt_entries *));
+ for (i = 0; i < NF_BR_NUMHOOKS; i++)
+--- linux/include/linux/netfilter_bridge/ebtables.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebtables.h Thu Jun 27 19:11:50 2002
+@@ -17,6 +17,7 @@
+ #include <linux/if_ether.h> // ETH_ALEN
+
+ #define EBT_TABLE_MAXNAMELEN 32
++#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+ // [gs]etsockopt numbers
+@@ -30,18 +31,29 @@
+ #define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1)
+ #define EBT_SO_GET_MAX (EBT_SO_GET_ENTRIES+1)
+
+-#define EBT_ACCEPT 0
+-#define EBT_DROP 1
+-#define EBT_CONTINUE 2
+-#define NUM_STANDARD_TARGETS 3
++// verdicts >0 are "branches"
++#define EBT_ACCEPT -1
++#define EBT_DROP -2
++#define EBT_CONTINUE -3
++#define EBT_RETURN -4
++#define NUM_STANDARD_TARGETS 4
++
++struct ebt_counter
++{
++ __u64 pcnt;
++};
+
+ struct ebt_entries {
+ // this field is always set to zero (including userspace).
+ // See EBT_ENTRY_OR_ENTRIES.
+ // Must be same size as ebt_entry.bitmask
+ __u32 distinguisher;
+- // one standard (accept or drop) per hook
+- __u8 policy;
++ // the chain name
++ char name[EBT_CHAIN_MAXNAMELEN];
++ // counter offset for this chain
++ unsigned int counter_offset;
++ // one standard (accept, drop, return) per hook
++ int policy;
+ // nr. of entries
+ __u32 nentries;
+ // entry list
+@@ -76,11 +88,6 @@
+ #define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+ | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+-struct ebt_counter
+-{
+- __u64 pcnt;
+-};
+-
+ struct ebt_entry_match
+ {
+ union {
+@@ -118,7 +125,7 @@
+ struct ebt_standard_target
+ {
+ struct ebt_entry_target target;
+- __u8 verdict;
++ int verdict;
+ };
+
+ // one entry
+@@ -158,8 +165,6 @@
+ unsigned int entries_size;
+ // start of the chains
+ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+- // how many counters in front of it?
+- unsigned int counter_entry[NF_BR_NUMHOOKS];
+ // nr of counters userspace expects back
+ unsigned int num_counters;
+ // where the kernel will put the old counters
+@@ -178,7 +183,7 @@
+ const struct net_device *out, const void *matchdata,
+ unsigned int datalen, const struct ebt_counter *c);
+ // 0 == let it in
+- int (*check)(const char *tablename, unsigned int hooknr,
++ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *matchdata, unsigned int datalen);
+ void (*destroy)(void *matchdata, unsigned int datalen);
+ struct module *me;
+@@ -192,7 +197,7 @@
+ const struct net_device *out, const void *watcherdata,
+ unsigned int datalen, const struct ebt_counter *c);
+ // 0 == let it in
+- int (*check)(const char *tablename, unsigned int hooknr,
++ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *watcherdata, unsigned int datalen);
+ void (*destroy)(void *watcherdata, unsigned int datalen);
+ struct module *me;
+@@ -203,19 +208,27 @@
+ struct list_head list;
+ const char name[EBT_FUNCTION_MAXNAMELEN];
+ // returns one of the standard verdicts
+- __u8 (*target)(struct sk_buff **pskb,
++ int (*target)(struct sk_buff **pskb,
+ unsigned int hooknr,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targetdata,
+ unsigned int datalen);
+ // 0 == let it in
+- int (*check)(const char *tablename, unsigned int hooknr,
++ int (*check)(const char *tablename, unsigned int hookmask,
+ const struct ebt_entry *e, void *targetdata, unsigned int datalen);
+ void (*destroy)(void *targetdata, unsigned int datalen);
+ struct module *me;
+ };
+
++// used for jumping from and into user defined chains (udc)
++struct ebt_chainstack
++{
++ struct ebt_entries *chaininfo; // pointer to chain data
++ struct ebt_entry *e; // pointer to entry data
++ unsigned int n; // n'th entry
++};
++
+ struct ebt_table_info
+ {
+ // total size of the entries
+@@ -223,9 +236,9 @@
+ unsigned int nentries;
+ // pointers to the start of the chains
+ struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+- // how many counters in front of the counters bolonging to a chain
+- unsigned int counter_entry[NF_BR_NUMHOOKS];
+ struct ebt_counter *counters;
++ // room to maintain the stack used for jumping from and into udc
++ struct ebt_chainstack *chainstack;
+ char *entries;
+ };
+
+--- linux/include/linux/netfilter_bridge/ebt_vlan.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_vlan.h Thu Jun 27 19:11:50 2002
+@@ -3,14 +3,16 @@
+
+ #define EBT_VLAN_ID 0x01
+ #define EBT_VLAN_PRIO 0x02
+-#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO)
++#define EBT_VLAN_ENCAP 0x04
++#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+ #define EBT_VLAN_MATCH "vlan"
+
+ struct ebt_vlan_info {
+ __u16 id; /* VLAN ID {1-4095} */
+- __u16 prio; /* VLAN Priority {0-7} */
++ __u8 prio; /* VLAN User Priority {0-7} */
++ __u16 encap; /* VLAN Encapsulated frame code {0-65535} */
+ __u8 bitmask; /* Args bitmask bit 1=1 - ID arg,
+- bit 2=1 - Pirority arg */
++ bit 2=1 User-Priority arg, bit 3=1 encap*/
+ __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg,
+ bit 2=1 - inversed Pirority arg */
+ };
+--- linux/include/linux/netfilter_bridge/ebt_nat.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_nat.h Thu Jun 27 19:11:50 2002
+@@ -4,8 +4,8 @@
+ struct ebt_nat_info
+ {
+ unsigned char mac[ETH_ALEN];
+- // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+- __u8 target;
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
+ };
+ #define EBT_SNAT_TARGET "snat"
+ #define EBT_DNAT_TARGET "dnat"
+--- linux/include/linux/netfilter_bridge/ebt_redirect.h Thu Jun 27 19:15:44 2002
++++ ebt2.0pre9.001/include/linux/netfilter_bridge/ebt_redirect.h Thu Jun 27 19:11:50 2002
+@@ -3,8 +3,8 @@
+
+ struct ebt_redirect_info
+ {
+- // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE
+- __u8 target;
++ // EBT_ACCEPT, EBT_DROP or EBT_CONTINUE or EBT_RETURN
++ int target;
+ };
+ #define EBT_REDIRECT_TARGET "redirect"
+
diff --git a/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff b/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff
new file mode 100644
index 0000000..851b2c6
--- /dev/null
+++ b/userspace/patches/incremental-patches/ebtables-v2.0pre8.001.diff
@@ -0,0 +1,2745 @@
+--- ebtables-v2.0pre7/Makefile Thu Jun 6 19:18:29 2002
++++ ebtables-v2.0pre8.001/Makefile Thu Jun 27 18:53:55 2002
+@@ -2,7 +2,7 @@
+
+ KERNEL_DIR?=/usr/src/linux
+ PROGNAME:=ebtables
+-PROGVERSION:="2.0pre7 (June 2002)"
++PROGVERSION:="2.0pre8 (June 2002)"
+
+ MANDIR?=/usr/local/man
+ CFLAGS:=-Wall -Wunused
+--- ebtables-v2.0pre7/ebtables.c Wed Jun 5 21:42:17 2002
++++ ebtables-v2.0pre8.001/ebtables.c Thu Jun 27 18:53:55 2002
+@@ -34,17 +34,25 @@
+ #include <netinet/ether.h>
+ #include <asm/types.h>
+ #include "include/ebtables_u.h"
++#include <unistd.h>
++#include <fcntl.h>
++#include <sys/wait.h>
+
+ // here are the number-name correspondences kept for the ethernet
+ // frame type field
+ #define PROTOCOLFILE "/etc/ethertypes"
+
+-#define DATABASEHOOKNR NF_BR_NUMHOOKS
++#ifndef PROC_SYS_MODPROBE
++#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
++#endif
++
++#define DATABASEHOOKNR -2
+ #define DATABASEHOOKNAME "DB"
+
+ static char *prog_name = PROGNAME;
+ static char *prog_version = PROGVERSION;
+-char* hooknames[NF_BR_NUMHOOKS] = {
++char *hooknames[NF_BR_NUMHOOKS] =
++{
+ [NF_BR_PRE_ROUTING]"PREROUTING",
+ [NF_BR_LOCAL_IN]"INPUT",
+ [NF_BR_FORWARD]"FORWARD",
+@@ -79,6 +87,10 @@
+ { "destination" , required_argument, 0, 'd' },
+ { "dst" , required_argument, 0, 'd' },
+ { "table" , required_argument, 0, 't' },
++ { "modprobe" , required_argument, 0, 'M' },
++ { "new-chain" , required_argument, 0, 'N' },
++ { "rename-chain" , required_argument, 0, 'E' },
++ { "delete-chain" , required_argument, 0, 'X' },
+ { 0 }
+ };
+
+@@ -89,10 +101,11 @@
+ "ACCEPT",
+ "DROP",
+ "CONTINUE",
++ "RETURN",
+ };
+
+-unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+-unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
++unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
++unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+ unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+ unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+ unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+@@ -326,10 +339,74 @@
+ tables = t;
+ }
+
+-// used to parse /etc/etherproto
++// blatently stolen (again) from iptables.c userspace program
++// find out where the modprobe utility is located
++static char *get_modprobe(void)
++{
++ int procfile;
++ char *ret;
++
++ procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
++ if (procfile < 0)
++ return NULL;
++
++ ret = malloc(1024);
++ if (ret) {
++ switch (read(procfile, ret, 1024)) {
++ case -1: goto fail;
++ case 1024: goto fail; /* Partial read. Wierd */
++ }
++ if (ret[strlen(ret)-1]=='\n')
++ ret[strlen(ret)-1]=0;
++ close(procfile);
++ return ret;
++ }
++ fail:
++ free(ret);
++ close(procfile);
++ return NULL;
++}
++
++// I hate stealing, really... Lets call it a tribute.
++int ebtables_insmod(const char *modname, const char *modprobe)
++{
++ char *buf = NULL;
++ char *argv[3];
++
++ // If they don't explicitly set it, read out of kernel
++ if (!modprobe) {
++ buf = get_modprobe();
++ if (!buf)
++ return -1;
++ modprobe = buf;
++ }
++
++ switch (fork()) {
++ case 0:
++ argv[0] = (char *)modprobe;
++ argv[1] = (char *)modname;
++ argv[2] = NULL;
++ execv(argv[0], argv);
++
++ /* not usually reached */
++ exit(0);
++ case -1:
++ return -1;
++
++ default: /* parent */
++ wait(NULL);
++ }
++
++ free(buf);
++ return 0;
++}
++
++
++// used to parse /etc/ethertypes
+ int disregard_whitespace(char *buffer, FILE *ifp)
+ {
+ int hlp;
++
+ buffer[0] = '\t';
+ while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+ hlp = fscanf(ifp, "%c", buffer);
+@@ -338,10 +415,11 @@
+ return 0;
+ }
+
+-// used to parse /etc/etherproto
++// used to parse /etc/ethertypes
+ int disregard_tabspace(char *buffer, FILE *ifp)
+ {
+ int hlp;
++
+ buffer[0] = '\t';
+ while (buffer[0] == '\t' || buffer[0] == ' ') {
+ hlp = fscanf(ifp, "%c", buffer);
+@@ -356,9 +434,10 @@
+ int i, hlp;
+ char anotherhlp;
+
+- /* discard comment lines && whitespace*/
++ // discard comment lines and whitespace
+ while (1) {
+- if (disregard_whitespace(buffer, ifp)) return -1;
++ if (disregard_whitespace(buffer, ifp))
++ return -1;
+ if (buffer[0] == '#')
+ while (1) {
+ hlp = fscanf(ifp, "%c", &anotherhlp);
+@@ -367,17 +446,20 @@
+ if (anotherhlp == '\n')
+ break;
+ }
+- else break;
++ else
++ break;
+ }
+
+ // buffer[0] already contains the first letter
+ for (i = 1; i < 21; i++) {
+ hlp = fscanf(ifp, "%c", buffer + i);
+- if (hlp == EOF || hlp == 0) return -1;
++ if (hlp == EOF || hlp == 0)
++ return -1;
+ if (buffer[i] == '\t' || buffer[i] == ' ')
+ break;
+ }
+- if (i == 21) return -1;
++ if (i == 21)
++ return -1;
+ buffer[i] = '\0';
+ if (disregard_tabspace(value, ifp))
+ return -1;
+@@ -401,7 +483,8 @@
+ return 0;
+ }
+
+-// helper function for list_em()
++// translate a hexadecimal number to a protocol name, parsing /etc/ethertypes
++// returns 0 on success
+ int number_to_name(unsigned short proto, char *name)
+ {
+ FILE *ifp;
+@@ -425,7 +508,7 @@
+ }
+
+ // helper function for list_rules()
+-static void list_em(int hooknr)
++static void list_em(struct ebt_u_entries *entries)
+ {
+ int i, j, space = 0, digits;
+ struct ebt_u_entry *hlp;
+@@ -436,20 +519,21 @@
+ struct ebt_u_target *t;
+ char name[21];
+
+- hlp = replace.hook_entry[hooknr]->entries;
+- printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
+- standard_targets[replace.hook_entry[hooknr]->policy]);
+- printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
++ hlp = entries->entries;
++ printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
++ standard_targets[-entries->policy - 1]);
++ printf("nr. of entries: %d \n", entries->nentries);
+
+- i = replace.hook_entry[hooknr]->nentries;
+- while (i >9) {
++ i = entries->nentries;
++ while (i > 9) {
+ space++;
+ i /= 10;
+ }
+
+- for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
++ for (i = 0; i < entries->nentries; i++) {
+ digits = 0;
+ // A little work to get nice rule numbers.
++ j = i + 1;
+ while (j > 9) {
+ digits++;
+ j /= 10;
+@@ -461,22 +545,22 @@
+ // Don't print anything about the protocol if no protocol was
+ // specified, obviously this means any protocol will do.
+ if (!(hlp->bitmask & EBT_NOPROTO)) {
+- printf("eth proto: ");
++ printf("-p ");
+ if (hlp->invflags & EBT_IPROTO)
+ printf("! ");
+ if (hlp->bitmask & EBT_802_3)
+- printf("Length, ");
++ printf("Length ");
+ else {
+ if (number_to_name(ntohs(hlp->ethproto), name))
+- printf("0x%x, ", ntohs(hlp->ethproto));
++ printf("0x%x ", ntohs(hlp->ethproto));
+ else
+- printf("%s, ", name);
++ printf("%s ", name);
+ }
+ }
+ if (hlp->bitmask & EBT_SOURCEMAC) {
+ char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+- printf("source mac: ");
++ printf("-s ");
+ if (hlp->invflags & EBT_ISOURCE)
+ printf("! ");
+ if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
+@@ -502,12 +586,12 @@
+ hlp->sourcemsk));
+ }
+ endsrc:
+- printf(", ");
++ printf(" ");
+ }
+ if (hlp->bitmask & EBT_DESTMAC) {
+ char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+- printf("dest mac: ");
++ printf("-d ");
+ if (hlp->invflags & EBT_IDEST)
+ printf("! ");
+ if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
+@@ -533,27 +617,31 @@
+ hlp->destmsk));
+ }
+ enddst:
+- printf(", ");
++ printf(" ");
+ }
+ if (hlp->in[0] != '\0') {
++ printf("-i ");
+ if (hlp->invflags & EBT_IIN)
+ printf("! ");
+- printf("in-if: %s, ", hlp->in);
++ printf("%s ", hlp->in);
+ }
+ if (hlp->logical_in[0] != '\0') {
++ printf("--logical-in ");
+ if (hlp->invflags & EBT_ILOGICALIN)
+ printf("! ");
+- printf("logical in-if: %s, ", hlp->logical_in);
++ printf("%s ", hlp->logical_in);
+ }
+ if (hlp->logical_out[0] != '\0') {
++ printf("--logical-out ");
+ if (hlp->invflags & EBT_ILOGICALOUT)
+ printf("! ");
+- printf("logical out-if: %s, ", hlp->logical_out);
++ printf("%s, ", hlp->logical_out);
+ }
+ if (hlp->out[0] != '\0') {
++ printf("-o ");
+ if (hlp->invflags & EBT_IOUT)
+ printf("! ");
+- printf("out-if: %s, ", hlp->out);
++ printf("%s, ", hlp->out);
+ }
+
+ m_l = hlp->m_list;
+@@ -573,30 +661,154 @@
+ w_l = w_l->next;
+ }
+
+- printf("target: ");
++ printf("-j ");
++ if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
++ printf("%s ", hlp->t->u.name);
+ t = find_target(hlp->t->u.name);
+ if (!t)
+ print_bug("Target not found");
+ t->print(hlp, hlp->t);
+ printf(", count = %llu",
+- replace.counters[replace.counter_entry[hooknr] + i].pcnt);
++ replace.counters[entries->counter_offset + i].pcnt);
+ printf("\n");
+ hlp = hlp->next;
+ }
+ }
+
++struct ebt_u_entries *nr_to_chain(int nr)
++{
++ if (nr == -1)
++ return NULL;
++ if (nr < NF_BR_NUMHOOKS)
++ return replace.hook_entry[nr];
++ else {
++ int i;
++ struct ebt_u_chain_list *cl = replace.udc;
++
++ i = nr - NF_BR_NUMHOOKS;
++ while (i > 0 && cl) {
++ cl = cl->next;
++ i--;
++ }
++ if (cl)
++ return cl->udc;
++ else
++ return NULL;
++ }
++}
++
++static struct ebt_u_entries *to_chain()
++{
++ return nr_to_chain(replace.selected_hook);
++}
++
++struct ebt_u_stack
++{
++ int chain_nr;
++ int n;
++ struct ebt_u_entry *e;
++ struct ebt_u_entries *entries;
++};
++
++void check_for_loops()
++{
++ int chain_nr , i, j , k, sp = 0, verdict;
++ struct ebt_u_entries *entries, *entries2;
++ struct ebt_u_stack *stack = NULL;
++ struct ebt_u_entry *e;
++
++ i = -1;
++ // initialize hook_mask to 0
++ while (1) {
++ i++;
++ if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
++ continue;
++ entries = nr_to_chain(i);
++ if (!entries)
++ break;
++ entries->hook_mask = 0;
++ }
++ if (i > NF_BR_NUMHOOKS) {
++ stack = (struct ebt_u_stack *)malloc((i - NF_BR_NUMHOOKS) *
++ sizeof(struct ebt_u_stack));
++ if (!stack)
++ print_memory();
++ }
++
++ // check for loops, starting from every base chain
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (!(replace.valid_hooks & (1 << i)))
++ continue;
++ entries = nr_to_chain(i);
++ entries->hook_mask = (1 << i);
++ chain_nr = i;
++
++ e = entries->entries;
++ for (j = 0; j < entries->nentries; j++) {
++ if (strcmp(e->t->u.name, EBT_STANDARD_TARGET))
++ goto letscontinue;
++ verdict = ((struct ebt_standard_target *)(e->t))->verdict;
++ if (verdict < 0)
++ goto letscontinue;
++ entries2 = nr_to_chain(verdict + NF_BR_NUMHOOKS);
++ entries2->hook_mask |= entries->hook_mask;
++ // now see if we've been here before
++ for (k = 0; k < sp; k++)
++ if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS)
++ print_error("Loop from chain %s to chain %s",
++ nr_to_chain(chain_nr)->name, nr_to_chain(stack[k].chain_nr)->name);
++ // jump to the chain, make sure we know how to get back
++ stack[sp].chain_nr = chain_nr;
++ stack[sp].n = j;
++ stack[sp].entries = entries;
++ stack[sp].e = e;
++ sp++;
++ j = -1;
++ e = entries2->entries;
++ chain_nr = verdict + NF_BR_NUMHOOKS;
++ entries = entries2;
++ continue;
++letscontinue:
++ e = e->next;
++ }
++ // we are at the end of a standard chain
++ if (sp == 0)
++ continue;
++ // go back to the chain one level higher
++ sp--;
++ j = stack[sp].n;
++ chain_nr = stack[sp].chain_nr;
++ e = stack[sp].e;
++ entries = stack[sp].entries;
++ goto letscontinue;
++ }
++ free(stack);
++ return;
++}
++
+ // parse the chain name and return the corresponding nr
++// returns -1 on failure
+ int get_hooknr(char* arg)
+ {
+ int i;
++ struct ebt_u_chain_list *cl = replace.udc;
+
+ // database is special case (not really a chain)
+ if (!strcmp(arg, DATABASEHOOKNAME))
+ return DATABASEHOOKNR;
+
+- for (i = 0; i < NF_BR_NUMHOOKS; i++)
+- if (!strcmp(arg, hooknames[i]))
++ for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ if (!(replace.valid_hooks & (1 << i)))
++ continue;
++ if (!strcmp(arg, replace.hook_entry[i]->name))
++ return i;
++ }
++ while(cl) {
++ if (!strcmp(arg, cl->udc->name))
+ return i;
++ i++;
++ cl = cl->next;
++ }
+ return -1;
+ }
+
+@@ -623,6 +835,9 @@
+ "--flush -F [chain] : Delete all rules in chain or in all chains\n"
+ "--zero -Z [chain] : Put counters on zero in chain or in all chains\n"
+ "--policy -P chain target : Change policy on chain to target\n"
++"--new-chain -N chain : Create a user defined chain\n"
++"--rename-chain -E old new : Rename a chain\n"
++"--delete-chain -X chain : Delete a user defined chain\n"
+ "Options:\n"
+ "--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n"
+ "--src -s [!] address[/mask]: source mac address\n"
+@@ -631,6 +846,7 @@
+ "--out-if -o [!] name : network output interface name\n"
+ "--logical-in [!] name : logical bridge input interface name\n"
+ "--logical-out [!] name : logical bridge output interface name\n"
++"--modprobe -M : try to insert modules using this command\n"
+ "--version -V : print package version\n"
+ "\n" ,
+ prog_name,
+@@ -661,22 +877,37 @@
+ int i;
+
+ printf("Bridge table: %s\n", table->name);
+- if (replace.selected_hook != -1) list_em(replace.selected_hook);
+- else
+- for (i = 0; i < NF_BR_NUMHOOKS; i++)
+- if (replace.valid_hooks & (1 << i))
+- list_em(i);
+- return;
++ if (replace.selected_hook != -1) {
++ list_em(to_chain());
++ } else {
++ struct ebt_u_chain_list *cl = replace.udc;
++
++ i = 0;
++ while (1) {
++ if (i < NF_BR_NUMHOOKS) {
++ if (replace.valid_hooks & (1 << i))
++ list_em(replace.hook_entry[i]);
++ i++;
++ continue;
++ } else {
++ if (!cl)
++ break;
++ list_em(cl->udc);
++ cl = cl->next;
++ }
++ }
++ }
+ }
+
+ // execute command P
+ static void change_policy(int policy)
+ {
+ int i;
++ struct ebt_u_entries *entries = to_chain();
+
+ // don't do anything if the policy is the same
+- if (replace.hook_entry[replace.selected_hook]->policy != policy) {
+- replace.hook_entry[replace.selected_hook]->policy = policy;
++ if (entries->policy != policy) {
++ entries->policy = policy;
+ replace.num_counters = replace.nentries;
+ if (replace.nentries) {
+ // '+ 1' for the CNT_END
+@@ -696,76 +927,105 @@
+ }
+
+ // flush one chain or the complete table
+-static void flush_chains()
++// -1 == nothing to do
++// 0 == give back to kernel
++static int flush_chains()
+ {
+- int i, j, oldnentries;
++ int i, j, oldnentries, numdel;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *tmp;
++ struct ebt_u_entries *entries = to_chain();
+
+ // flush whole table
+- if (replace.selected_hook == -1) {
++ if (!entries) {
+ if (replace.nentries == 0)
+- exit(0);
++ return -1;
+ replace.nentries = 0;
+ // no need for the kernel to give us counters back
+ replace.num_counters = 0;
++
+ // free everything and zero (n)entries
+- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+- if (!(replace.valid_hooks & (1 << i)))
+- continue;
+- replace.hook_entry[i]->nentries = 0;
+- u_e = replace.hook_entry[i]->entries;
++ i = -1;
++ while (1) {
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
++ continue;
++ else
++ break;
++ }
++ entries->nentries = 0;
++ entries->counter_offset = 0;
++ u_e = entries->entries;
++ entries->entries = NULL;
+ while (u_e) {
+ free_u_entry(u_e);
+ tmp = u_e->next;
+ free(u_e);
+ u_e = tmp;
+ }
+- replace.hook_entry[i]->entries = NULL;
+ }
+- return;
++ return 0;
+ }
+
+- if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+- exit(0);
++ if (entries->nentries == 0)
++ return -1;
+ oldnentries = replace.nentries;
+- replace.nentries = replace.nentries -
+- replace.hook_entry[replace.selected_hook]->nentries;
++ replace.nentries -= entries->nentries;
++ numdel = entries->nentries;
+
+- // delete the counters belonging to the specified chain
+ if (replace.nentries) {
+ // +1 for CNT_END
+ if ( !(counterchanges = (unsigned short *)
+ malloc((oldnentries + 1) * sizeof(unsigned short))) )
+ print_memory();
+- cnt = counterchanges;
+- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+- if (!(replace.valid_hooks & (1 << i)))
++ }
++ // delete the counters belonging to the specified chain,
++ // update counter_offset
++ i = -1;
++ cnt = counterchanges;
++ while (1) {
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
+ continue;
+- for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+- if (i != replace.selected_hook)
+- *cnt = CNT_NORM;
+- else
++ else
++ break;
++ }
++ if (i > replace.selected_hook)
++ entries->counter_offset -= numdel;
++ if (replace.nentries) {
++ for (j = 0; j < entries->nentries; j++) {
++ if (i == replace.selected_hook)
+ *cnt = CNT_DEL;
++ else
++ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
++ }
++
++ if (replace.nentries) {
+ *cnt = CNT_END;
+ replace.num_counters = oldnentries;
+ }
+ else
+ replace.num_counters = 0;
+
+- replace.hook_entry[replace.selected_hook]->nentries = 0;
+- u_e = replace.hook_entry[replace.selected_hook]->entries;
++ entries = to_chain();
++ entries->nentries = 0;
++ u_e = entries->entries;
+ while (u_e) {
+ free_u_entry(u_e);
+ tmp = u_e->next;
+ free(u_e);
+ u_e = tmp;
+ }
+- replace.hook_entry[replace.selected_hook]->entries = NULL;
+-}
++ entries->entries = NULL;
++ return 0;
++}
+
+ // -1 == no match
+ static int check_rule_exists(int rule_nr)
+@@ -776,32 +1036,33 @@
+ struct ebt_u_watcher_list *w_l, *w_l2;
+ struct ebt_u_watcher *w;
+ struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
++ struct ebt_u_entries *entries = to_chain();
+ int i, j, k;
+
+ // handle '-D chain rulenr' command
+ if (rule_nr != -1) {
+- if (rule_nr >
+- replace.hook_entry[replace.selected_hook]->nentries)
+- return 0;
++ if (rule_nr > entries->nentries)
++ return -1;
+ // user starts counting from 1
+ return rule_nr - 1;
+ }
+- u_e = replace.hook_entry[replace.selected_hook]->entries;
++ u_e = entries->entries;
+ // check for an existing rule (if there are duplicate rules,
+ // take the first occurance)
+- for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
+- i++, u_e = u_e->next) {
++ for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+ if (!u_e)
+ print_bug("Hmm, trouble");
+ if ( u_e->ethproto == new_entry->ethproto
+ && !strcmp(u_e->in, new_entry->in)
+- && !strcmp(u_e->out, new_entry->out)
+- && u_e->bitmask == new_entry->bitmask) {
++ && !strcmp(u_e->out, new_entry->out)) {
++ if (strcmp(u_e->logical_in, new_entry->logical_in) ||
++ strcmp(u_e->logical_out, new_entry->logical_out))
++ continue;
+ if (new_entry->bitmask & EBT_SOURCEMAC &&
+- strcmp(u_e->sourcemac, new_entry->sourcemac))
++ memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+ continue;
+ if (new_entry->bitmask & EBT_DESTMAC &&
+- strcmp(u_e->destmac, new_entry->destmac))
++ memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+ continue;
+ if (new_entry->bitmask != u_e->bitmask ||
+ new_entry->invflags != u_e->invflags)
+@@ -863,7 +1124,7 @@
+ return -1;
+ }
+
+-// execute command A
++// execute command A or I
+ static void add_rule(int rule_nr)
+ {
+ int i, j;
+@@ -871,18 +1132,18 @@
+ unsigned short *cnt;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
++ struct ebt_u_entries *entries = to_chain(), *entries2;
+
+ if (rule_nr != -1) { // command -I
+- if (--rule_nr >
+- replace.hook_entry[replace.selected_hook]->nentries)
+- print_error("rule nr too high: %d > %d", rule_nr,
+- replace.hook_entry[replace.selected_hook]->nentries);
++ if (--rule_nr > entries->nentries)
++ print_error("rule nr too high: %d > %d", rule_nr + 1,
++ entries->nentries + 1);
+ } else
+- rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
++ rule_nr = entries->nentries;
+ // we're adding one rule
+ replace.num_counters = replace.nentries;
+ replace.nentries++;
+- replace.hook_entry[replace.selected_hook]->nentries++;
++ entries->nentries++;
+
+ // handle counter stuff
+ // +1 for CNT_END
+@@ -891,9 +1152,10 @@
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < replace.selected_hook; i++) {
+- if (!(replace.valid_hooks & (1 << i)))
++ if (i < NF_BR_NUMHOOKS && !(replace.valid_hooks & (1 << i)))
+ continue;
+- for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
++ entries2 = nr_to_chain(i);
++ for (j = 0; j < entries2->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+@@ -912,7 +1174,7 @@
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+- u_e = replace.hook_entry[replace.selected_hook]->entries;
++ u_e = entries->entries;
+ for (i = 0; i < rule_nr; i++) {
+ u_e2 = u_e;
+ u_e = u_e->next;
+@@ -921,7 +1183,7 @@
+ if (u_e2)
+ u_e2->next = new_entry;
+ else
+- replace.hook_entry[replace.selected_hook]->entries = new_entry;
++ entries->entries = new_entry;
+ new_entry->next = u_e;
+
+ // put the ebt_[match, watcher, target] pointers in place
+@@ -936,6 +1198,20 @@
+ w_l = w_l->next;
+ }
+ new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
++
++ // update the counter_offset of chains behind this one
++ i = replace.selected_hook;
++ while (1) {
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
++ continue;
++ else
++ break;
++ } else
++ entries->counter_offset++;
++ }
+ }
+
+ // execute command D
+@@ -944,9 +1220,10 @@
+ int i, j, lentmp = 0;
+ unsigned short *cnt;
+ struct ebt_u_entry *u_e, *u_e2;
++ struct ebt_u_entries *entries = to_chain(), *entries2;
+
+ if ( (i = check_rule_exists(rule_nr)) == -1 )
+- print_error("Sorry, rule does not exists");
++ print_error("Sorry, rule does not exist");
+
+ // we're deleting a rule
+ replace.num_counters = replace.nentries;
+@@ -954,9 +1231,11 @@
+
+ if (replace.nentries) {
+ for (j = 0; j < replace.selected_hook; j++) {
+- if (!(replace.valid_hooks & (1 << j)))
++ if (j < NF_BR_NUMHOOKS &&
++ !(replace.valid_hooks & (1 << j)))
+ continue;
+- lentmp += replace.hook_entry[j]->nentries;
++ entries2 = nr_to_chain(j);
++ lentmp += entries2->nentries;
+ }
+ lentmp += i;
+ // +1 for CNT_END
+@@ -981,7 +1260,7 @@
+
+ // go to the right position in the chain
+ u_e2 = NULL;
+- u_e = replace.hook_entry[replace.selected_hook]->entries;
++ u_e = entries->entries;
+ for (j = 0; j < i; j++) {
+ u_e2 = u_e;
+ u_e = u_e->next;
+@@ -991,12 +1270,25 @@
+ if (u_e2)
+ u_e2->next = u_e->next;
+ else
+- replace.hook_entry[replace.selected_hook]->entries = u_e->next;
++ entries->entries = u_e->next;
+
+- replace.hook_entry[replace.selected_hook]->nentries--;
++ entries->nentries--;
+ // free everything
+ free_u_entry(u_e);
+ free(u_e);
++ // update the counter_offset of chains behind this one
++ i = replace.selected_hook;
++ while (1) {
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
++ continue;
++ else
++ break;
++ } else
++ entries->counter_offset--;
++ }
+ }
+
+ // execute command Z
+@@ -1005,15 +1297,16 @@
+
+ if (zerochain == -1) {
+ // tell main() we don't update the counters
+- // this results in tricking the kernel to zero his counters,
++ // this results in tricking the kernel to zero its counters,
+ // naively expecting userspace to update its counters. Muahahaha
+ counterchanges = NULL;
+ replace.num_counters = 0;
+ } else {
+ int i, j;
+ unsigned short *cnt;
++ struct ebt_u_entries *entries = nr_to_chain(zerochain), *e2;
+
+- if (replace.hook_entry[zerochain]->nentries == 0)
++ if (entries->nentries == 0)
+ exit(0);
+ counterchanges = (unsigned short *)
+ malloc((replace.nentries + 1) * sizeof(unsigned short));
+@@ -1021,14 +1314,16 @@
+ print_memory();
+ cnt = counterchanges;
+ for (i = 0; i < zerochain; i++) {
+- if (!(replace.valid_hooks & (1 << i)))
++ if (i < NF_BR_NUMHOOKS &&
++ !(replace.valid_hooks & (1 << i)))
+ continue;
+- for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
++ e2 = nr_to_chain(i);
++ for (j = 0; j < e2->nentries; j++) {
+ *cnt = CNT_NORM;
+ cnt++;
+ }
+ }
+- for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
++ for (i = 0; i < entries->nentries; i++) {
+ *cnt = CNT_ZERO;
+ cnt++;
+ }
+@@ -1100,15 +1395,17 @@
+ exit(0);
+ }
+
+-// set ethproto
+-int name_to_protocol(char *name)
++// 0 == success
++// 1 == success, but for the special 'protocol' LENGTH
++// -1 == failure
++int name_to_number(char *name, __u16 *proto)
+ {
+ FILE *ifp;
+ char buffer[21], value[5], *bfr;
+ unsigned short i;
+
+ if (!strcasecmp("LENGTH", name)) {
+- new_entry->ethproto = 0;
++ *proto = 0;
+ new_entry->bitmask |= EBT_802_3;
+ return 1;
+ }
+@@ -1121,7 +1418,7 @@
+ i = (unsigned short) strtol(value, &bfr, 16);
+ if (*bfr != '\0')
+ return -1;
+- new_entry->ethproto = i;
++ *proto = i;
+ fclose(ifp);
+ return 0;
+ }
+@@ -1165,6 +1462,66 @@
+ return 0;
+ }
+
++// executes the final_check() function for all extensions used by the rule
++void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
++{
++ struct ebt_u_match_list *m_l;
++ struct ebt_u_watcher_list *w_l;
++ struct ebt_u_target *t;
++ struct ebt_u_match *m;
++ struct ebt_u_watcher *w;
++
++ m_l = e->m_list;
++ w_l = e->w_list;
++ while (m_l) {
++ m = find_match(m_l->m->u.name);
++ m->final_check(e, m_l->m, replace.name,
++ entries->hook_mask, 1);
++ m_l = m_l->next;
++ }
++ while (w_l) {
++ w = find_watcher(w_l->w->u.name);
++ w->final_check(e, w_l->w, replace.name,
++ entries->hook_mask, 1);
++ w_l = w_l->next;
++ }
++ t = find_target(e->t->u.name);
++ t->final_check(e, e->t, replace.name,
++ entries->hook_mask, 1);
++}
++
++// used for the -X command
++void check_for_references(int chain_nr)
++{
++ int i = -1, j;
++ struct ebt_u_entries *entries;
++ struct ebt_u_entry *e;
++
++ while (1) {
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
++ continue;
++ else
++ break;
++ }
++ e = entries->entries;
++ j = 0;
++ while (e) {
++ j++;
++ if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
++ e = e->next;
++ continue;
++ }
++ if (((struct ebt_standard_target *)e->t)->verdict == chain_nr)
++ print_error("Can't delete the chain, it's referenced "
++ "in chain %s, rule %d", entries->name, j);
++ e = e->next;
++ }
++ }
++}
++
+ int check_inverse(const char option[])
+ {
+ if (strcmp(option, "!") == 0) {
+@@ -1199,13 +1556,15 @@
+ int c, i;
+ // this special one for the -Z option (we can have -Z <this> -L <that>)
+ int zerochain = -1;
+- int policy = -1;
++ int policy = 0;
+ int rule_nr = -1;// used for -D chain number
+ struct ebt_u_target *t;
+ struct ebt_u_match *m;
+ struct ebt_u_watcher *w;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
++ struct ebt_u_entries *entries;
++ const char *modprobe = NULL;
+
+ // initialize the table name, OPT_ flags, selected hook and command
+ strcpy(replace.name, "filter");
+@@ -1219,21 +1578,113 @@
+ // put some sane values in our new entry
+ initialize_entry(new_entry);
+
++ // The scenario induced by this loop makes that:
++ // '-t' and '-M' (if specified) have to come before '-A' and the like
++
+ // getopt saves the day
+ while ((c = getopt_long(argc, argv,
+- "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
++ "-A:D:I:N:E:X:L::Z::F::P:Vhi:o:j:p:b:s:d:t:M:", ebt_options, NULL)) != -1) {
+ switch (c) {
+
+ case 'A': // add a rule
+ case 'D': // delete a rule
+ case 'P': // define policy
+ case 'I': // insert a rule
++ case 'N': // make a user defined chain
++ case 'E': // rename chain
++ case 'X': // delete chain
+ replace.command = c;
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+ replace.flags |= OPT_COMMAND;
++ if ( !(table = find_table(replace.name)) )
++ print_error("Bad table name");
++ // get the kernel's information
++ if (get_table(&replace)) {
++ ebtables_insmod("ebtables", modprobe);
++ if (get_table(&replace))
++ print_error("can't initialize ebtables "
++ "table %s", replace.name);
++ }
++ if (optarg[0] == '-')
++ print_error("No chain name specified");
++ if (c == 'N') {
++ struct ebt_u_chain_list *cl, **cl2;
++
++ if (get_hooknr(optarg) != -1)
++ print_error("Chain %s already exists",
++ optarg);
++ if (find_target(optarg))
++ print_error("Target with name %s exists"
++ , optarg);
++ if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
++ print_error("Chain name length can't exceed %d",
++ EBT_CHAIN_MAXNAMELEN - 1);
++ cl = (struct ebt_u_chain_list *)
++ malloc(sizeof(struct ebt_u_chain_list));
++ if (!cl)
++ print_memory();
++ cl->next = NULL;
++ cl->udc = (struct ebt_u_entries *)
++ malloc(sizeof(struct ebt_u_entries));
++ if (!cl->udc)
++ print_memory();
++ cl->udc->nentries = 0;
++ cl->udc->policy = EBT_ACCEPT;
++ cl->udc->counter_offset = replace.nentries;
++ cl->udc->hook_mask = 0;
++ strcpy(cl->udc->name, optarg);
++ cl->udc->entries = NULL;
++ cl->kernel_start = NULL;
++ // put the new chain at the end
++ cl2 = &replace.udc;
++ while (*cl2)
++ cl2 = &((*cl2)->next);
++ *cl2 = cl;
++ break;
++ }
+ if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+- print_error("Bad chain");
++ print_error("Chain %s doesn't exist", optarg);
++ if (c == 'E') {
++ if (optind >= argc || argv[optind][0] == '-')
++ print_error("No new chain name specified");
++ if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
++ print_error("Chain name len can't exceed %d",
++ EBT_CHAIN_MAXNAMELEN - 1);
++ if (get_hooknr(argv[optind]) != -1)
++ print_error("Chain %s already exists",
++ argv[optind]);
++ entries = to_chain();
++ strcpy(entries->name, argv[optind]);
++ optind++;
++ break;
++ }
++ if (c == 'X') {
++ struct ebt_u_chain_list *cl, **cl2;
++
++ if (replace.selected_hook < NF_BR_NUMHOOKS)
++ print_error("You can't remove a standard chain");
++ // if the chain is referenced, don't delete it
++ check_for_references(replace.selected_hook - NF_BR_NUMHOOKS);
++ flush_chains();
++ entries = to_chain();
++ if (replace.udc->udc == entries) {
++ cl = replace.udc;
++ replace.udc = replace.udc->next;
++ free(cl->udc);
++ free(cl);
++ break;
++ }
++ cl2 = &(replace.udc);
++ while ((*cl2)->next->udc != entries)
++ cl2 = &((*cl2)->next);
++ cl = (*cl2)->next;
++ (*cl2)->next = (*cl2)->next->next;
++ free(cl->udc);
++ free(cl);
++ break;
++ }
++
+ if (c == 'D' && optind < argc &&
+ argv[optind][0] != '-') {
+ rule_nr = strtol(argv[optind], &buffer, 10);
+@@ -1245,13 +1696,16 @@
+ if (c == 'P') {
+ if (optind >= argc)
+ print_error("No policy specified");
+- for (i = 0; i < 2; i++)
++ policy = 0;
++ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(argv[optind],
+ standard_targets[i])) {
+- policy = i;
++ policy = -i -1;
++ if (policy == EBT_CONTINUE)
++ policy = 0;
+ break;
+ }
+- if (policy == -1)
++ if (policy == 0)
+ print_error("Wrong policy");
+ optind++;
+ }
+@@ -1286,6 +1740,15 @@
+ " not allowed");
+ replace.flags |= OPT_COMMAND;
+ }
++ if ( !(table = find_table(replace.name)) )
++ print_error("Bad table name");
++ // get the kernel's information
++ if (get_table(&replace)) {
++ ebtables_insmod("ebtables", modprobe);
++ if (get_table(&replace))
++ print_error("can't initialize ebtables "
++ "table %s", replace.name);
++ }
+ i = -1;
+ if (optarg) {
+ if ( (i = get_hooknr(optarg)) == -1 )
+@@ -1312,6 +1775,12 @@
+ printf("%s, %s\n", prog_name, prog_version);
+ exit(0);
+
++ case 'M': // modprobe
++ if (replace.command != 'h')
++ print_error("Please put the -M option earlier");
++ modprobe = optarg;
++ break;
++
+ case 'h': // help
+ if (replace.flags & OPT_COMMAND)
+ print_error("Multiple commands not allowed");
+@@ -1342,8 +1811,10 @@
+ break;
+
+ case 't': // table
++ if (replace.command != 'h')
++ print_error("Please put the -t option first");
+ check_option(&replace.flags, OPT_TABLE);
+- if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
++ if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+ print_error("Table name too long");
+ strcpy(replace.name, optarg);
+ break;
+@@ -1375,7 +1846,7 @@
+ print_error("No in-interface "
+ "specified");
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+- print_error("Illegal interfacelength");
++ print_error("Illegal interface length");
+ strcpy(new_entry->in, argv[optind - 1]);
+ break;
+ }
+@@ -1393,7 +1864,7 @@
+ print_error("No logical in-interface "
+ "specified");
+ if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+- print_error("Illegal interfacelength");
++ print_error("Illegal interface length");
+ strcpy(new_entry->logical_in, argv[optind - 1]);
+ break;
+ }
+@@ -1437,7 +1908,6 @@
+ break;
+ }
+ if (c == 'j') {
+-
+ check_option(&replace.flags, OPT_JUMP);
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(optarg,
+@@ -1445,12 +1915,30 @@
+ t = find_target(
+ EBT_STANDARD_TARGET);
+ ((struct ebt_standard_target *)
+- t->t)->verdict = i;
++ t->t)->verdict = -i - 1;
+ break;
+ }
+- // must be an extension then
+- if (i == NUM_STANDARD_TARGETS) {
++ if (-i - 1 == EBT_RETURN) {
++ if (replace.selected_hook < NF_BR_NUMHOOKS)
++ print_error("Return target"
++ " only for user defined chains");
++ }
++ if (i != NUM_STANDARD_TARGETS)
++ break;
++ if ((i = get_hooknr(optarg)) != -1) {
++ if (i < NF_BR_NUMHOOKS)
++ print_error("don't jump"
++ " to a standard chain");
++ t = find_target(
++ EBT_STANDARD_TARGET);
++ ((struct ebt_standard_target *)
++ t->t)->verdict = i - NF_BR_NUMHOOKS;
++ break;
++ }
++ else {
++ // must be an extension then
+ struct ebt_u_target *t;
++
+ t = find_target(optarg);
+ // -j standard not allowed either
+ if (!t || t ==
+@@ -1504,10 +1992,14 @@
+ print_error("Problem with the specified "
+ "protocol");
+ new_entry->ethproto = i;
+- if (*buffer != '\0')
+- if (name_to_protocol(argv[optind - 1]) == -1)
++ if (*buffer != '\0') {
++ if ((i = name_to_number(argv[optind - 1],
++ &new_entry->ethproto)) == -1)
+ print_error("Problem with the specified"
+ " protocol");
++ if (i == 1)
++ new_entry->bitmask |= EBT_802_3;
++ }
+ if (new_entry->ethproto < 1536 &&
+ !(new_entry->bitmask & EBT_802_3))
+ print_error("Sorry, protocols have values above"
+@@ -1527,7 +2019,7 @@
+ t = (struct ebt_u_target *)new_entry->t;
+ if ((t->parse(c - t->option_offset, argv, argc,
+ new_entry, &t->flags, &t->t)))
+- continue;
++ goto check_extension;
+
+ // is it a match_option?
+ for (m = matches; m; m = m->next)
+@@ -1538,7 +2030,7 @@
+ if (m != NULL) {
+ if (m->used == 0)
+ add_match(m);
+- continue;
++ goto check_extension;
+ }
+
+ // is it a watcher option?
+@@ -1551,9 +2043,15 @@
+ print_error("Unknown argument");
+ if (w->used == 0)
+ add_watcher(w);
++check_extension:
++ if (replace.command != 'A' && replace.command != 'I' &&
++ replace.command != 'D')
++ print_error("extensions only for -A, -I and -D");
+ }
+ }
+
++ if ( !table && !(table = find_table(replace.name)) )
++ print_error("Bad table name");
+ // database stuff before ebtables stuff
+ if (replace.command == 'b')
+ allowdb(allowbc);
+@@ -1570,41 +2068,38 @@
+ print_error("Not enough information");
+ }
+
+- if ( !(table = find_table(replace.name)) )
+- print_error("Bad table name");
+-
+ // do this after parsing everything, so we can print specific info
+ if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+ print_help();
+
+ // do the final checks
+- m_l = new_entry->m_list;
+- w_l = new_entry->w_list;
+- t = (struct ebt_u_target *)new_entry->t;
+- while (m_l) {
+- m = (struct ebt_u_match *)(m_l->m);
+- m->final_check(new_entry, m->m, replace.name,
+- replace.selected_hook);
+- m_l = m_l->next;
+- }
+- while (w_l) {
+- w = (struct ebt_u_watcher *)(w_l->w);
+- w->final_check(new_entry, w->w, replace.name,
+- replace.selected_hook);
+- w_l = w_l->next;
++ if (replace.command == 'A' || replace.command == 'I' ||
++ replace.command == 'D') {
++ // this will put the hook_mask right for the chains
++ check_for_loops();
++ entries = to_chain();
++ m_l = new_entry->m_list;
++ w_l = new_entry->w_list;
++ t = (struct ebt_u_target *)new_entry->t;
++ while (m_l) {
++ m = (struct ebt_u_match *)(m_l->m);
++ m->final_check(new_entry, m->m, replace.name,
++ entries->hook_mask, 0);
++ m_l = m_l->next;
++ }
++ while (w_l) {
++ w = (struct ebt_u_watcher *)(w_l->w);
++ w->final_check(new_entry, w->w, replace.name,
++ entries->hook_mask, 0);
++ w_l = w_l->next;
++ }
++ t->final_check(new_entry, t->t, replace.name,
++ entries->hook_mask, 0);
+ }
+- t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+-
+ // so, the extensions can work with the host endian
+ // the kernel does not have to do this ofcourse
+ new_entry->ethproto = htons(new_entry->ethproto);
+
+- // get the kernel's information
+- get_table(&replace);
+- // check if selected_hook is a valid_hook
+- if (replace.selected_hook >= 0 &&
+- !(replace.valid_hooks & (1 << replace.selected_hook)))
+- print_error("Bad chain name");
+ if (replace.command == 'P')
+ change_policy(policy);
+ else if (replace.command == 'L') {
+@@ -1616,12 +2111,38 @@
+ }
+ if (replace.flags & OPT_ZERO)
+ zero_counters(zerochain);
+- else if (replace.command == 'F')
+- flush_chains();
+- else if (replace.command == 'A' || replace.command == 'I')
++ else if (replace.command == 'F') {
++ if (flush_chains() == -1)
++ exit(0);
++ } else if (replace.command == 'A' || replace.command == 'I') {
+ add_rule(rule_nr);
+- else if (replace.command == 'D')
++ check_for_loops();
++ // do the final_check(), for all entries
++ // needed when adding a rule that has a chain target
++ i = -1;
++ while (1) {
++ struct ebt_u_entry *e;
++
++ i++;
++ entries = nr_to_chain(i);
++ if (!entries) {
++ if (i < NF_BR_NUMHOOKS)
++ continue;
++ else
++ break;
++ }
++ e = entries->entries;
++ while (e) {
++ // userspace extensions use host endian
++ e->ethproto = ntohs(e->ethproto);
++ do_final_checks(e, entries);
++ e->ethproto = htons(e->ethproto);
++ e = e->next;
++ }
++ }
++ } else if (replace.command == 'D')
+ delete_rule(rule_nr);
++ // commands -N, -E, -X fall through
+
+ if (table->check)
+ table->check(&replace);
+--- ebtables-v2.0pre7/communication.c Wed Jun 5 20:17:25 2002
++++ ebtables-v2.0pre8.001/communication.c Thu Jun 27 18:53:55 2002
+@@ -42,9 +42,11 @@
+ struct ebt_u_entry *e;
+ struct ebt_u_match_list *m_l;
+ struct ebt_u_watcher_list *w_l;
++ struct ebt_u_chain_list *cl;
++ struct ebt_u_entries *entries;
+ char *p, *base;
+ int i, j;
+- unsigned int entries_size = 0;
++ unsigned int entries_size = 0, *chain_offsets;
+
+ new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+ if (!new)
+@@ -54,15 +56,34 @@
+ new->nentries = u_repl->nentries;
+ new->num_counters = u_repl->num_counters;
+ new->counters = u_repl->counters;
+- memcpy(new->counter_entry, u_repl->counter_entry,
+- sizeof(new->counter_entry));
++ // determine nr of udc
++ i = 0;
++ cl = u_repl->udc;
++ while (cl) {
++ i++;
++ cl = cl->next;
++ }
++ i += NF_BR_NUMHOOKS;
++ chain_offsets = (unsigned int *)malloc(i * sizeof(unsigned int));
+ // determine size
+- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+- if (!(new->valid_hooks & (1 << i)))
+- continue;
++ i = 0;
++ cl = u_repl->udc;
++ while (1) {
++ if (i < NF_BR_NUMHOOKS) {
++ if (!(new->valid_hooks & (1 << i))) {
++ i++;
++ continue;
++ }
++ entries = u_repl->hook_entry[i];
++ } else {
++ if (!cl)
++ break;
++ entries = cl->udc;
++ }
++ chain_offsets[i] = entries_size;
+ entries_size += sizeof(struct ebt_entries);
+ j = 0;
+- e = u_repl->hook_entry[i]->entries;
++ e = entries->entries;
+ while (e) {
+ j++;
+ entries_size += sizeof(struct ebt_entry);
+@@ -83,9 +104,12 @@
+ e = e->next;
+ }
+ // a little sanity check
+- if (j != u_repl->hook_entry[i]->nentries)
++ if (j != entries->nentries)
+ print_bug("Wrong nentries: %d != %d, hook = %s", j,
+- u_repl->hook_entry[i]->nentries, hooknames[i]);
++ entries->nentries, entries->name);
++ if (i >= NF_BR_NUMHOOKS)
++ cl = cl->next;
++ i++;
+ }
+
+ new->entries_size = entries_size;
+@@ -95,18 +119,31 @@
+
+ // put everything in one block
+ p = new->entries;
+- for (i = 0; i < NF_BR_NUMHOOKS; i++) {
++ i = 0;
++ cl = u_repl->udc;
++ while (1) {
+ struct ebt_entries *hlp;
+
+- if (!(new->valid_hooks & (1 << i)))
+- continue;
+ hlp = (struct ebt_entries *)p;
+- new->hook_entry[i] = hlp;
+- hlp->nentries = u_repl->hook_entry[i]->nentries;
+- hlp->policy = u_repl->hook_entry[i]->policy;
++ if (i < NF_BR_NUMHOOKS) {
++ if (!(new->valid_hooks & (1 << i))) {
++ i++;
++ continue;
++ }
++ entries = u_repl->hook_entry[i];
++ new->hook_entry[i] = hlp;
++ } else {
++ if (!cl)
++ break;
++ entries = cl->udc;
++ }
++ hlp->nentries = entries->nentries;
++ hlp->policy = entries->policy;
++ strcpy(hlp->name, entries->name);
++ hlp->counter_offset = entries->counter_offset;
+ hlp->distinguisher = 0; // make the kernel see the light
+ p += sizeof(struct ebt_entries);
+- e = u_repl->hook_entry[i]->entries;
++ e = entries->entries;
+ while (e) {
+ struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+@@ -148,16 +185,27 @@
+ tmp->target_offset = p - base;
+ memcpy(p, e->t, e->t->target_size +
+ sizeof(struct ebt_entry_target));
++ if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
++ struct ebt_standard_target *st =
++ (struct ebt_standard_target *)p;
++ // translate the jump to a udc
++ if (st->verdict >= 0)
++ st->verdict = chain_offsets[st->verdict + NF_BR_NUMHOOKS];
++ }
+ p += e->t->target_size +
+ sizeof(struct ebt_entry_target);
+ tmp->next_offset = p - base;
+ e = e->next;
+ }
++ if (i >= NF_BR_NUMHOOKS)
++ cl = cl->next;
++ i++;
+ }
+
+ // sanity check
+ if (p - new->entries != new->entries_size)
+ print_bug("Entries_size bug");
++ free(chain_offsets);
+ return new;
+ }
+
+@@ -287,7 +335,7 @@
+ static int
+ ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
+ int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
+- unsigned int valid_hooks)
++ unsigned int valid_hooks, char *base)
+ {
+ // an entry
+ if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+@@ -332,6 +380,26 @@
+ "userspace tool", t->u.name);
+ memcpy(new->t, t, t->target_size +
+ sizeof(struct ebt_entry_target));
++ // deal with jumps to udc
++ if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
++ char *tmp = base;
++ int verdict = ((struct ebt_standard_target *)t)->verdict;
++ int i;
++ struct ebt_u_chain_list *cl;
++
++ if (verdict >= 0) {
++ tmp += verdict;
++ cl = u_repl->udc;
++ i = 0;
++ while (cl && cl->kernel_start != tmp) {
++ i++;
++ cl = cl->next;
++ }
++ if (!cl)
++ print_bug("can't find udc for jump");
++ ((struct ebt_standard_target *)new->t)->verdict = i;
++ }
++ }
+
+ // I love pointers
+ **u_e = new;
+@@ -342,33 +410,82 @@
+ } else { // a new chain
+ int i;
+ struct ebt_entries *entries = (struct ebt_entries *)e;
+- struct ebt_u_entries *new;
++ struct ebt_u_chain_list *cl;
+
+- for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+- if (valid_hooks & (1 << i))
+- break;
+- if (i >= NF_BR_NUMHOOKS)
+- print_bug("Not enough valid hooks");
+- *hook = i;
+ if (*n != *cnt)
+ print_bug("Nr of entries in the chain is wrong");
+ *n = entries->nentries;
+ *cnt = 0;
+- new = (struct ebt_u_entries *)
+- malloc(sizeof(struct ebt_u_entries));
+- if (!new)
+- print_memory();
++ for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
++ if (valid_hooks & (1 << i))
++ break;
++ *hook = i;
++ // makes use of fact that standard chains come before udc
++ if (i >= NF_BR_NUMHOOKS) { // udc
++ i -= NF_BR_NUMHOOKS;
++ cl = u_repl->udc;
++ while (i-- > 0)
++ cl = cl->next;
++ *u_e = &(cl->udc->entries);
++ } else {
++ *u_e = &(u_repl->hook_entry[*hook]->entries);
++ }
++ return 0;
++ }
++}
++
++// initialize all chain headers
++static int
++ebt_translate_chains(struct ebt_entry *e, unsigned int *hook,
++ struct ebt_u_replace *u_repl, unsigned int valid_hooks)
++{
++ int i;
++ struct ebt_entries *entries = (struct ebt_entries *)e;
++ struct ebt_u_entries *new;
++ struct ebt_u_chain_list **chain_list;
++
++ if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
++ for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
++ if (valid_hooks & (1 << i))
++ break;
++ // makes use of fact that standard chains come before udc
++ if (i >= NF_BR_NUMHOOKS) { // udc
++ chain_list = &u_repl->udc;
++ // add in the back
++ while (*chain_list)
++ chain_list = &((*chain_list)->next);
++ *chain_list = (struct ebt_u_chain_list *)
++ malloc(sizeof(struct ebt_u_chain_list));
++ if (!(*chain_list))
++ print_memory();
++ (*chain_list)->next = NULL;
++ (*chain_list)->udc = (struct ebt_u_entries *)
++ malloc(sizeof(struct ebt_u_entries));
++ if (!((*chain_list)->udc))
++ print_memory();
++ new = (*chain_list)->udc;
++ // ebt_translate_entry depends on this for knowing
++ // to which chain is being jumped
++ (*chain_list)->kernel_start = (char *)e;
++ } else {
++ *hook = i;
++ new = (struct ebt_u_entries *)
++ malloc(sizeof(struct ebt_u_entries));
++ if (!new)
++ print_memory();
++ u_repl->hook_entry[*hook] = new;
++ }
+ new->nentries = entries->nentries;
+ new->policy = entries->policy;
+ new->entries = NULL;
+- u_repl->hook_entry[*hook] = new;
+- *u_e = &new->entries;
+- return 0;
++ new->counter_offset = entries->counter_offset;
++ strcpy(new->name, entries->name);
+ }
++ return 0;
+ }
+
+ // talk with kernel to receive the kernel's table
+-void get_table(struct ebt_u_replace *u_repl)
++int get_table(struct ebt_u_replace *u_repl)
+ {
+ int i, j, k, hook;
+ socklen_t optlen;
+@@ -380,9 +497,7 @@
+ optlen = sizeof(struct ebt_replace);
+ strcpy(repl.name, u_repl->name);
+ if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+- print_error("The %s table is not supported by the kernel,"
+- " consider recompiling your kernel or try insmod ebt_%s",
+- repl.name, repl.name);
++ return -1;
+
+ if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+ print_memory();
+@@ -407,17 +522,20 @@
+ u_repl->nentries = repl.nentries;
+ u_repl->num_counters = repl.num_counters;
+ u_repl->counters = repl.counters;
+- memcpy(u_repl->counter_entry, repl.counter_entry,
+- sizeof(repl.counter_entry));
++ u_repl->udc = NULL;
+ hook = -1;
++ EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
++ &hook, u_repl, u_repl->valid_hooks);
+ i = 0; // holds the expected nr. of entries for the chain
+ j = 0; // holds the up to now counted entries for the chain
+ k = 0; // holds the total nr. of entries,
+ // should equal u_repl->nentries afterwards
++ hook = -1;
+ EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
+- &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
++ &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks, repl.entries);
+ if (k != u_repl->nentries)
+ print_bug("Wrong total nentries");
++ return 0;
+ }
+
+ void get_dbinfo(struct brdb_dbinfo *nr)
+@@ -425,7 +543,7 @@
+ socklen_t optlen = sizeof(struct brdb_dbinfo);
+
+ get_sockfd();
+-
++
+ if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
+ print_error("Sorry, br_db code probably not in kernel, "
+ "try insmod br_db");
+--- ebtables-v2.0pre7/extensions/ebt_redirect.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_redirect.c Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <string.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_redirect.h>
+@@ -33,7 +32,6 @@
+ return;
+ }
+
+-
+ #define OPT_REDIRECT_TARGET 0x01
+ static int parse(int c, char **argv, int argc,
+ const struct ebt_u_entry *entry, unsigned int *flags,
+@@ -48,7 +46,7 @@
+ check_option(flags, OPT_REDIRECT_TARGET);
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(optarg, standard_targets[i])) {
+- redirectinfo->target = i;
++ redirectinfo->target = -i - 1;
+ break;
+ }
+ if (i == NUM_STANDARD_TARGETS)
+@@ -61,10 +59,11 @@
+ }
+
+ static void final_check(const struct ebt_u_entry *entry,
+- const struct ebt_entry_target *target, const char *name, unsigned int hook)
++ const struct ebt_entry_target *target, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+- if ( (hook != NF_BR_PRE_ROUTING || strcmp(name, "nat")) &&
+- (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++ if ( ((hook_mask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
++ ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+ print_error("Wrong chain for redirect");
+ }
+
+@@ -74,8 +73,10 @@
+ struct ebt_redirect_info *redirectinfo =
+ (struct ebt_redirect_info *)target->data;
+
+- printf("redirect");
+- printf(" --redirect-target %s", standard_targets[redirectinfo->target]);
++ if (redirectinfo->target == EBT_ACCEPT)
++ return;
++ printf(" --redirect-target %s",
++ standard_targets[-redirectinfo->target - 1]);
+ }
+
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/extensions/ebt_nat.c Wed Jun 5 21:43:11 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_nat.c Thu Jun 27 18:53:55 2002
+@@ -4,7 +4,6 @@
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/ether.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_nat.h>
+@@ -89,7 +88,7 @@
+ check_option(flags, OPT_SNAT_TARGET);
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(optarg, standard_targets[i])) {
+- natinfo->target = i;
++ natinfo->target = -i - 1;
+ break;
+ }
+ if (i == NUM_STANDARD_TARGETS)
+@@ -124,7 +123,7 @@
+ check_option(flags, OPT_DNAT_TARGET);
+ for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+ if (!strcmp(optarg, standard_targets[i])) {
+- natinfo->target = i;
++ natinfo->target = -i - 1;
+ break;
+ }
+ if (i == NUM_STANDARD_TARGETS)
+@@ -137,22 +136,24 @@
+ }
+
+ static void final_check_s(const struct ebt_u_entry *entry,
+- const struct ebt_entry_target *target, const char *name, unsigned int hook)
++ const struct ebt_entry_target *target, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+- if (hook != NF_BR_POST_ROUTING || strcmp(name, "nat"))
++ if (!(hook_mask & (1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+ print_error("Wrong chain for snat");
+- if (to_source_supplied == 0)
++ if (time == 0 && to_source_supplied == 0)
+ print_error("No snat address supplied");
+ }
+
+ static void final_check_d(const struct ebt_u_entry *entry,
+- const struct ebt_entry_target *target, const char *name, unsigned int hook)
++ const struct ebt_entry_target *target, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+- if ( ((hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
++ if (((hook_mask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) ||
+ strcmp(name, "nat")) &&
+- (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
++ ((hook_mask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+ print_error("Wrong chain for dnat");
+- if (to_dest_supplied == 0)
++ if (time == 0 && to_dest_supplied == 0)
+ print_error("No dnat address supplied");
+ }
+
+@@ -161,9 +162,9 @@
+ {
+ struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+- printf("snat - to: ");
++ printf("--to-src ");
+ printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+- printf(" --snat-target %s", standard_targets[natinfo->target]);
++ printf(" --snat-target %s", standard_targets[-natinfo->target - 1]);
+ }
+
+ static void print_d(const struct ebt_u_entry *entry,
+@@ -171,9 +172,9 @@
+ {
+ struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+- printf("dnat - to: ");
++ printf("--to-dst ");
+ printf("%s", ether_ntoa((struct ether_addr *)natinfo->mac));
+- printf(" --dnat-target %s", standard_targets[natinfo->target]);
++ printf(" --dnat-target %s", standard_targets[-natinfo->target - 1]);
+ }
+
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/extensions/ebt_ip.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_ip.c Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <string.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_ip.h>
+@@ -156,7 +155,7 @@
+ unsigned int *flags, struct ebt_entry_match **match)
+ {
+ struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+- char *end, *buffer;
++ char *end;
+ int i;
+
+ switch (c) {
+@@ -194,7 +193,7 @@
+ if (optind > argc)
+ print_error("Missing ip tos argument");
+ i = strtol(argv[optind - 1], &end, 16);
+- if (i < 0 || i > 255 || *buffer != '\0')
++ if (i < 0 || i > 255 || *end != '\0')
+ print_error("Problem with specified ip tos");
+ ipinfo->tos = i;
+ ipinfo->bitmask |= EBT_IP_TOS;
+@@ -219,7 +218,8 @@
+ }
+
+ static void final_check(const struct ebt_u_entry *entry,
+- const struct ebt_entry_match *match, const char *name, unsigned int hook)
++ const struct ebt_entry_match *match, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+ if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+ entry->ethproto != ETH_P_IP)
+@@ -234,34 +234,34 @@
+ int j;
+
+ if (ipinfo->bitmask & EBT_IP_SOURCE) {
+- printf("source ip: ");
++ printf("--ip-src ");
+ if (ipinfo->invflags & EBT_IP_SOURCE)
+ printf("! ");
+ for (j = 0; j < 4; j++)
+ printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
+ (j == 3) ? "" : ".");
+- printf("%s, ", mask_to_dotted(ipinfo->smsk));
++ printf("%s ", mask_to_dotted(ipinfo->smsk));
+ }
+ if (ipinfo->bitmask & EBT_IP_DEST) {
+- printf("dest ip: ");
++ printf("--ip-dst ");
+ if (ipinfo->invflags & EBT_IP_DEST)
+ printf("! ");
+ for (j = 0; j < 4; j++)
+ printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
+ (j == 3) ? "" : ".");
+- printf("%s, ", mask_to_dotted(ipinfo->dmsk));
++ printf("%s ", mask_to_dotted(ipinfo->dmsk));
+ }
+ if (ipinfo->bitmask & EBT_IP_TOS) {
+- printf("ip TOS: ");
++ printf("--ip-tos ");
+ if (ipinfo->invflags & EBT_IP_TOS)
+ printf("! ");
+- printf("0x%02X, ", ipinfo->tos);
++ printf("0x%02X ", ipinfo->tos);
+ }
+ if (ipinfo->bitmask & EBT_IP_PROTO) {
+- printf("ip proto: ");
++ printf("--ip-proto ");
+ if (ipinfo->invflags & EBT_IP_DEST)
+ printf("! ");
+- printf("%d, ", ipinfo->protocol);
++ printf("%d ", ipinfo->protocol);
+ }
+ }
+
+--- ebtables-v2.0pre7/extensions/ebt_arp.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_arp.c Thu Jun 27 18:53:55 2002
+@@ -3,7 +3,6 @@
+ #include <stdlib.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_arp.h>
+@@ -76,9 +75,8 @@
+ #define OPT_PTYPE 0x04
+ #define OPT_IP_S 0x08
+ #define OPT_IP_D 0x10
+-static int parse(int c, char **argv, int argc,
+- const struct ebt_u_entry *entry, unsigned int *flags,
+- struct ebt_entry_match **match)
++static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
++ unsigned int *flags, struct ebt_entry_match **match)
+ {
+ struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+ int i;
+@@ -178,7 +176,8 @@
+ }
+
+ static void final_check(const struct ebt_u_entry *entry,
+-const struct ebt_entry_match *match, const char *name, unsigned int hook)
++ const struct ebt_entry_match *match, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+ if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+ (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
+@@ -195,40 +194,40 @@
+ int i;
+
+ if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+- printf("arp opcode: ");
++ printf("--arp-op ");
+ if (arpinfo->invflags & EBT_ARP_OPCODE)
+ printf("! ");
+ printf("%d ", ntohs(arpinfo->opcode));
+ }
+ if (arpinfo->bitmask & EBT_ARP_HTYPE) {
+- printf("arp htype: ");
++ printf("--arp-htype ");
+ if (arpinfo->invflags & EBT_ARP_HTYPE)
+ printf("! ");
+ printf("%d ", ntohs(arpinfo->htype));
+ }
+ if (arpinfo->bitmask & EBT_ARP_PTYPE) {
+- printf("arp ptype: ");
++ printf("--arp-ptype ");
+ if (arpinfo->invflags & EBT_ARP_PTYPE)
+ printf("! ");
+ printf("0x%x ", ntohs(arpinfo->ptype));
+ }
+ if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+- printf("arp src IP ");
++ printf("--arp-ip-src ");
+ if (arpinfo->invflags & EBT_ARP_SRC_IP)
+ printf("! ");
+ for (i = 0; i < 4; i++)
+ printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
+ (i == 3) ? "" : ".");
+- printf("%s, ", mask_to_dotted(arpinfo->smsk));
++ printf("%s ", mask_to_dotted(arpinfo->smsk));
+ }
+ if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+- printf("arp dst IP ");
++ printf("--arp-ip-dst ");
+ if (arpinfo->invflags & EBT_ARP_DST_IP)
+ printf("! ");
+ for (i = 0; i < 4; i++)
+ printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
+ (i == 3) ? "" : ".");
+- printf("%s, ", mask_to_dotted(arpinfo->dmsk));
++ printf("%s ", mask_to_dotted(arpinfo->dmsk));
+ }
+ }
+
+--- ebtables-v2.0pre7/extensions/ebt_vlan.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_vlan.c Thu Jun 27 18:53:55 2002
+@@ -1,44 +1,68 @@
+ /*
+- * Summary: ebt_vlan userspace module
+- *
+- * Description: 802.1Q Virtual LAN match support module for ebtables project.
+- * Enable to match 802.1Q VLAN tagged frames by VLAN numeric
+- * identifier (12-bites field) and frame priority (3-bites field)
++ * Summary: ebt_vlan - IEEE 802.1Q extension module for userspace
++ *
++ * Description: 802.1Q Virtual LAN match support module for ebtables project.
++ * Enables to match 802.1Q:
++ * 1) VLAN-tagged frames by VLAN numeric identifier (12 - bits field)
++ * 2) Priority-tagged frames by user_priority (3 bits field)
++ * 3) Encapsulated Frame by ethernet protocol type/length
+ *
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+- * Nick Fedchik <nick@fedchik.org.ua>
+- *
+- * May, 2002
++ * Nick Fedchik <nick@fedchik.org.ua>
++ * June, 2002
++ *
++ * License: GNU GPL
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
+ */
+
+ #include <stdio.h>
+-#include <string.h>
+ #include <stdlib.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+-#include <linux/netfilter_bridge/ebtables.h>
++#include <string.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_vlan.h>
+
+-#define VLAN_ID '1'
+-#define VLAN_PRIO '2'
+-
++#define GET_BITMASK(_MASK_) vlaninfo->bitmask & _MASK_
++#define SET_BITMASK(_MASK_) vlaninfo->bitmask |= _MASK_
++#define INV_FLAG(_inv_flag_) (vlaninfo->invflags & _inv_flag_) ? "!" : ""
++
++#define VLAN_ID 0
++#define VLAN_PRIO 1
++#define VLAN_ENCAP 2
+ static struct option opts[] = {
+- {"vlan-id", required_argument, 0, VLAN_ID},
+- {"vlan-prio", required_argument, 0, VLAN_PRIO},
+- {0}
++ {"vlan-id", required_argument, NULL, VLAN_ID},
++ {"vlan-prio", required_argument, NULL, VLAN_PRIO},
++ {"vlan-encap", required_argument, NULL, VLAN_ENCAP},
++ {NULL}
+ };
+
++
+ /*
+- * Print out help for ebtables -h vlan
++ * Print out local help by "ebtables -h vlan"
+ */
+ static void print_help ()
+ {
+- printf ("802.1Q VLAN options:\n"
+- "--vlan-id [!] id : VLAN ID 1-4095 (integer)\n"
+- "--vlan-prio [!] prio : VLAN Priority 0-7 (integer)\n");
++ printf ("802.1Q VLAN extension options:\n"
++ "--vlan-id [!]id : VLAN-tagged frame identifier, 0,1-4094 (integer)\n"
++ "--vlan-prio [!]prio : Priority-tagged frame user_priority, 0-7 (integer)\n"
++ "--vlan-encap [!]proto : Encapsulated protocol (hexadecimal)\n");
+ }
+
+ /*
+@@ -49,40 +73,58 @@
+ struct ebt_vlan_info *vlaninfo =
+ (struct ebt_vlan_info *) match->data;
+ /*
+- * Just clean initial values
++ * Set initial values
+ */
+- vlaninfo->id = 0;
++ vlaninfo->id = 1; /* Default VID for VLAN-tagged 802.1Q frames */
+ vlaninfo->prio = 0;
++ vlaninfo->encap = 0;
+ vlaninfo->invflags = 0;
+ vlaninfo->bitmask = 0;
+ }
+
++/*
++ * option flags definition
++ */
+ #define OPT_VLAN_ID 0x01
+ #define OPT_VLAN_PRIO 0x02
++#define OPT_VLAN_ENCAP 0x04
++
++/*
++ * Parse passed arguments values (ranges, flags, etc...)
++ * int c - parameter number from static struct option opts[]
++ * int argc - total amout of arguments (std argc value)
++ *
++ */
+ static int
+-parse (int c, char **argv, int argc,
+- const struct ebt_u_entry *entry, unsigned int *flags,
+- struct ebt_entry_match **match)
++parse (int c,
++ char **argv,
++ int argc,
++ const struct ebt_u_entry *entry,
++ unsigned int *flags, struct ebt_entry_match **match)
+ {
+ struct ebt_vlan_info *vlaninfo =
+ (struct ebt_vlan_info *) (*match)->data;
+- unsigned short i;
++ unsigned long i;
+ char *end;
+-
++ __u16 encap;
+ switch (c) {
+ case VLAN_ID:
++ /*
++ * ebtables.c:check_option(unsigned int *flags, unsigned int mask)
++ * checking for multiple usage of same option
++ */
+ check_option (flags, OPT_VLAN_ID);
+ /*
+- * Check If we got inversed arg for VID,
++ * Check If we got inversed arg for vlan-id option,
+ * otherwise unset inversion flag
+ */
+ if (check_inverse (optarg))
+ vlaninfo->invflags |= EBT_VLAN_ID;
+ /*
+- * Check arg value presense
++ * Check arg value presence
+ */
+ if (optind > argc)
+- print_error ("Missing VLAN ID argument\n");
++ print_error ("Missing VLAN ID argument value\n");
+ /*
+ * Convert argv to long int,
+ * set *end to end of argv string,
+@@ -90,15 +132,19 @@
+ */
+ (unsigned short) i = strtol (argv[optind - 1], &end, 10);
+ /*
+- * Check arg val range
++ * Check arg val range
+ */
+- if (i < 1 || i >= 4096 || *end != '\0') {
+- i = 0;
++ if (i > 4094 || *end != '\0')
+ print_error
+- ("Problem with specified VLAN ID range\n");
+- }
++ ("Specified VLAN ID is out of range (0-4094)\n");
++ /*
++ * Set up parameter value
++ */
+ vlaninfo->id = i;
+- vlaninfo->bitmask|=EBT_VLAN_ID;
++ /*
++ * Set up parameter presence flag
++ */
++ SET_BITMASK (EBT_VLAN_ID);
+ break;
+
+ case VLAN_PRIO:
+@@ -107,25 +153,58 @@
+ vlaninfo->invflags |= EBT_VLAN_PRIO;
+ if (optind > argc)
+ print_error
+- ("Missing VLAN Priority level argument\n");
++ ("Missing user_priority argument value\n");
+ /*
+ * Convert argv to long int,
+ * set *end to end of argv string,
+ * base set 10 for decimal only
+ */
+- (unsigned short) i = strtol (argv[optind - 1], &end, 10);
++ (unsigned char) i = strtol (argv[optind - 1], &end, 10);
+ /*
+ * Check arg val range
+ */
+- if (i >= 8 || *end != '\0') {
+- i = 0;
++ if (i >= 8 || *end != '\0')
+ print_error
+- ("Problem with specified VLAN Priority range\n");
+- }
++ ("Specified user_priority is out of range (0-7)\n");
++ /*
++ * Set up parameter value
++ */
+ vlaninfo->prio = i;
+- vlaninfo->bitmask|=EBT_VLAN_PRIO;
++ /*
++ * Set up parameter presence flag
++ */
++ SET_BITMASK (EBT_VLAN_PRIO);
+ break;
+
++ case VLAN_ENCAP:
++ check_option (flags, OPT_VLAN_ENCAP);
++ if (check_inverse (optarg))
++ vlaninfo->invflags |= EBT_VLAN_ENCAP;
++ if (optind > argc)
++ print_error
++ ("Missing encapsulated frame type argument value\n");
++ /*
++ * Parameter can be decimal, hexadecimal, or string.
++ * Check arg val range (still raw area)
++ */
++ (unsigned short) encap = strtol (argv[optind - 1], &end, 16);
++ if (*end == '\0' && (encap < ETH_ZLEN || encap > 0xFFFF))
++ print_error
++ ("Specified encapsulated frame type is out of range\n");
++ if (*end != '\0')
++ if (name_to_number (argv[optind - 1], &encap) == -1)
++ print_error
++ ("Problem with the specified encapsulated"
++ "protocol\n");
++ /*
++ * Set up parameter value (network notation)
++ */
++ vlaninfo->encap = htons (encap);
++ /*
++ * Set up parameter presence flag
++ */
++ SET_BITMASK (EBT_VLAN_ENCAP);
++ break;
+ default:
+ return 0;
+ }
+@@ -133,19 +212,31 @@
+ }
+
+ /*
+- * Final check
++ * Final check - logical conditions
+ */
+ static void
+ final_check (const struct ebt_u_entry *entry,
+ const struct ebt_entry_match *match,
+- const char *name, unsigned int hook)
++ const char *name, unsigned int hook, unsigned int time)
+ {
++
++ struct ebt_vlan_info *vlaninfo =
++ (struct ebt_vlan_info *) match->data;
+ /*
+- * Is any proto supplied there? Or specified proto isn't 802.1Q?
++ * Is any proto param specified there? Or specified proto isn't 802.1Q?
+ */
+ if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
+ print_error
+- ("For matching 802.1Q VLAN the protocol must be specified as 802_1Q\n");
++ ("For use 802.1Q extension the protocol must be specified as 802_1Q\n");
++ /*
++ * Check if specified vlan-id=0 (priority-tagged frame condition)
++ * when vlan-prio was specified.
++ */
++ if (GET_BITMASK (EBT_VLAN_PRIO)) {
++ if (vlaninfo->id && GET_BITMASK (EBT_VLAN_ID))
++ print_error
++ ("For use user_priority the specified vlan-id must be 0\n");
++ }
+ }
+
+ /*
+@@ -158,21 +249,36 @@
+ struct ebt_vlan_info *vlaninfo =
+ (struct ebt_vlan_info *) match->data;
+
++ char ethertype_name[21];
+ /*
+ * Print VLAN ID if they are specified
+ */
+- if (vlaninfo->bitmask & EBT_VLAN_ID) {
+- printf ("vlan id: %s%d, ",
+- vlaninfo->invflags & EBT_VLAN_ID ? "!" : "",
+- vlaninfo->id);
++ if (GET_BITMASK (EBT_VLAN_ID)) {
++ printf ("--%s %s%d ",
++ opts[VLAN_ID].name,
++ INV_FLAG (EBT_VLAN_ID), vlaninfo->id);
+ }
+ /*
+- * Print VLAN priority if they are specified
++ * Print user priority if they are specified
+ */
+- if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+- printf ("vlan prio: %s%d, ",
+- vlaninfo->invflags & EBT_VLAN_PRIO ? "!" : "",
+- vlaninfo->prio);
++ if (GET_BITMASK (EBT_VLAN_PRIO)) {
++ printf ("--%s %s%d ",
++ opts[VLAN_PRIO].name,
++ INV_FLAG (EBT_VLAN_PRIO), vlaninfo->prio);
++ }
++ /*
++ * Print encapsulated frame type if they are specified
++ */
++ if (GET_BITMASK (EBT_VLAN_ENCAP)) {
++ printf ("--%s %s",
++ opts[VLAN_ENCAP].name, INV_FLAG (EBT_VLAN_ENCAP));
++ bzero (ethertype_name, 21);
++ if (!number_to_name
++ (ntohs (vlaninfo->encap), ethertype_name)) {
++ printf ("%s ", ethertype_name);
++ } else {
++ printf ("%2.4X ", ntohs (vlaninfo->encap));
++ }
+ }
+ }
+
+@@ -207,6 +313,13 @@
+ */
+ if (vlaninfo1->bitmask & EBT_VLAN_PRIO) {
+ if (vlaninfo1->prio != vlaninfo2->prio)
++ return 0;
++ };
++ /*
++ * Compare VLAN Encap if they are present
++ */
++ if (vlaninfo1->bitmask & EBT_VLAN_ENCAP) {
++ if (vlaninfo1->encap != vlaninfo2->encap)
+ return 0;
+ };
+ return 1;
+--- ebtables-v2.0pre7/extensions/ebt_log.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_log.c Thu Jun 27 18:53:55 2002
+@@ -2,7 +2,6 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+ #include <linux/netfilter_bridge/ebt_log.h>
+@@ -143,7 +142,8 @@
+ }
+
+ static void final_check(const struct ebt_u_entry *entry,
+- const struct ebt_entry_watcher *watcher, const char *name, unsigned int hook)
++ const struct ebt_entry_watcher *watcher, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+ return;
+ }
+@@ -153,13 +153,13 @@
+ {
+ struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+
+- printf("log: log-level = %s - log-prefix = \"%s\"",
++ printf("--log-level %s --log-prefix \"%s\"",
+ eight_priority[loginfo->loglevel].c_name,
+ loginfo->prefix);
+ if (loginfo->bitmask & EBT_LOG_IP)
+- printf(" - log-ip");
++ printf(" --log-ip");
+ if (loginfo->bitmask & EBT_LOG_ARP)
+- printf(" - log-arp");
++ printf(" --log-arp");
+ printf(" ");
+ }
+
+--- ebtables-v2.0pre7/extensions/ebt_standard.c Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/extensions/ebt_standard.c Thu Jun 27 18:53:55 2002
+@@ -1,6 +1,6 @@
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <sys/socket.h>
+-#include <linux/netfilter_bridge/ebtables.h>
+ #include <getopt.h>
+ #include "../include/ebtables_u.h"
+
+@@ -26,21 +26,34 @@
+ }
+
+ static void final_check(const struct ebt_u_entry *entry,
+- const struct ebt_entry_target *target, const char *name, unsigned int hook)
++ const struct ebt_entry_target *target, const char *name,
++ unsigned int hook_mask, unsigned int time)
+ {
+ }
+
++struct ebt_u_entries *nr_to_chain(int nr);
+ static void print(const struct ebt_u_entry *entry,
+ const struct ebt_entry_target *target)
+ {
+- __u8 verdict = ((struct ebt_standard_target *)target)->verdict;
++ int verdict = ((struct ebt_standard_target *)target)->verdict;
+
++ if (verdict >= 0) {
++ struct ebt_u_entries *entries;
++
++ entries = nr_to_chain(verdict + NF_BR_NUMHOOKS);
++ printf("%s", entries->name);
++ return;
++ }
+ if (verdict == EBT_CONTINUE)
+- printf("Continue ");
+- else if (verdict == EBT_ACCEPT)
+- printf("Accept ");
++ printf("CONTINUE ");
++ else if (verdict == EBT_ACCEPT)
++ printf("ACCEPT ");
++ else if (verdict == EBT_DROP)
++ printf("DROP ");
++ else if (verdict == EBT_RETURN)
++ printf("RETURN ");
+ else
+- printf("Drop ");
++ print_error("BUG: Bad standard target"); // this is a bug
+ }
+
+ static int compare(const struct ebt_entry_target *t1,
+--- ebtables-v2.0pre7/ChangeLog Thu Jun 6 19:22:14 2002
++++ ebtables-v2.0pre8.001/ChangeLog Thu Jun 27 18:53:55 2002
+@@ -1,6 +1,14 @@
+-20020606
+- * more useful message when the kernel can't find an ebtables module
+- * some minor code clean-up (no real impact).
++20020625
++ * user defined chains support: added -N, -X, -E options.
++20020621
++ * some unlogged changes (due to lazyness)
++ * change the output for -L to make it look like it would look when
++ the user inputs the command.
++ * try to autoload modules
++ * some minor bugfixes
++ * add user defined chains support (without new commands yet,
++ deliberately)
++ * comparing rules didn't take the logical devices into account
+ 20020520
+ * update help for -s and -d
+ * add VLAN in ethertypes
+--- ebtables-v2.0pre7/ebtables.8 Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/ebtables.8 Thu Jun 27 18:53:55 2002
+@@ -1,4 +1,4 @@
+-.TH EBTABLES 8 "01 May 2002"
++.TH EBTABLES 8 "26 June 2002"
+ .\"
+ .\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+ .\" It is based on the iptables man page.
+@@ -21,14 +21,18 @@
+ .\"
+ .\"
+ .SH NAME
+-ebtables(v.2.0) \- ethernet bridge packet table administration
++ebtables (v.2.0) \- ethernet bridge packet table administration
+ .SH SYNOPSIS
+-.BR "ebtables -[ADI] " "chain rule-specification [options]"
++.BR "ebtables -[ADI] " "chain rule-specification " [ options ]
+ .br
+ .BR "ebtables -P " "chain target"
+ .br
+ .BR "ebtables -[FLZ] [" "chain" "]"
+ .br
++.BR "ebtables -[NX] " chain
++.br
++.BR "ebtables -E " "old-chain-name new-chain-name"
++.br
+ .B "ebtables -L DB"
+ .br
+ .BR "ebtables -[b] [" "y/n" "]"
+@@ -53,6 +57,7 @@
+ .IR ACCEPT ,
+ .IR DROP ,
+ .IR CONTINUE ,
++.IR RETURN ,
+ an extention.
+ .PP
+ .I ACCEPT
+@@ -61,7 +66,11 @@
+ means the frame has to be dropped.
+ .I CONTINUE
+ means the next rule has to be checked. This can be handy to know how many
+-frames pass a certain point in the chain or to log those frames. For the
++frames pass a certain point in the chain or to log those frames.
++.I RETURN
++means stop traversing this chain and resume at the next rule in the
++previous (calling) chain.
++For the
+ other targets see the
+ .B "TARGET EXTENSIONS"
+ section.
+@@ -70,7 +79,7 @@
+ .TP
+ .B "-t, --table"
+ This option specifies the frame matching table which the command should
+-operate on. The tables are:
++operate on. If specified it should be the first option. The tables are:
+ .BR filter ,
+ this is the default table and contains three chains:
+ .B INPUT
+@@ -154,7 +163,23 @@
+ .B ACCEPT
+ , either
+ .BR DROP .
+-.SS PARAMETERS
++.TP
++.B "-N, --new-chain"
++Create a new user-defined chain by the given name.
++.TP
++.B "-X, --delete-chain"
++Delete the specified user-defined chain. There must be no references to the
++chain,
++.B ebtables
++will complain if there are.
++.TP
++.B "-E, --rename-chain"
++Rename the specified chain to the new name. This has no effect on the
++structure of the table. It is also allowed to rename a base chain, f.e.
++if you like PREBRIDGING more than PREROUTING. Be sure to talk about the
++standard chain names when you would ask a question on a mailing list.
++.SS
++PARAMETERS
+ The following parameters make up a rule specification (as used in the add
+ and delete commands). A "!" argument before the specification inverts the
+ test for that specification. Apart from these standard parameters, there are others, see
+@@ -265,6 +290,10 @@
+ .BR CONTINUE ,
+ or a target extension, see
+ .BR "TARGET EXTENSIONS" .
++.TP
++.BR "-M, --modprobe " "\fIcommand\fP"
++When talking to the kernel, use this
++.IR command " to try to automatically load missing kernel modules."
+ .SH MATCH EXTENSIONS
+ .B ebtables
+ extensions are precompiled into the userspace tool. So there is no need
+@@ -316,16 +345,23 @@
+ .BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+ The ARP IP destination address specification.
+ .SS vlan
+-Specify 802.1Q VLAN specific fields. These will only work if the protocol equals
+-.BR 802_1Q .
+-For more details see
++Specify 802.1Q Tag Control Information fields. These will only work if the protocol equals
++.BR 802_1Q.
++Also see extension help by
+ .BR "ebtables -h vlan" .
+ .TP
+ .BR "--vlan-id " "[!] \fIid\fP"
+-The VLAN identifier (decimal number from 0 to 4095).
++The VLAN identifier field, VID (decimal number from 0 to 4094).
+ .TP
+ .BR "--vlan-prio " "[!] \fIprio\fP"
+-The VLAN priority type, this can be a decimal number from 0 to 7. The default value is 0.
++The user_priority field, this can be a decimal number from 0 to 7.
++Required VID to be 0 (null VID) or not specified vlan-id parameter (in this case VID deliberately be set to 0).
++.TP
++.BR "--vlan-encap " "[!] \fItype\fP"
++The encapsulated ethernet frame type/length, this can be a hexadecimal number from 0x0000 to 0xFFFF.
++Usually it's 0x0800 (IPv4). See also
++.B /etc/ethertypes
++file.
+ .SH WATCHER EXTENSION(S)
+ Watchers are things that only look at frames passing by. These watchers only see the
+ frame if the frame passes all the matches of the rule.
+@@ -380,7 +416,8 @@
+ knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP doesn't
+-make sense, but you could do that too.
++make sense, but you could do that too. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .TP
+ .B dnat
+ The
+@@ -405,7 +442,8 @@
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP only makes
+ sense in the BROUTING chain but using the redirect target is more logical
+-there.
++there. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .TP
+ .B redirect
+ The
+@@ -423,7 +461,8 @@
+ knows what to do.
+ The default target is ACCEPT. Making it CONTINUE could let you use
+ multiple target extensions on the same frame. Making it DROP in the
+-BROUTING chain will let the frames be routed.
++BROUTING chain will let the frames be routed. RETURN is also allowed. Note
++that using RETURN in a base chain will result in the CONTINUE behaviour.
+ .SH FILES
+ .I /etc/ethertypes
+ .SH BUGS
+--- ebtables-v2.0pre7/ethertypes Mon Jun 3 19:54:55 2002
++++ ebtables-v2.0pre8.001/ethertypes Thu Jun 27 18:53:55 2002
+@@ -7,7 +7,7 @@
+ # programs using this file should not be case sensitive
+ # that's all :-))
+ IPV4 0800 put your comments behind, on the same line, after a tab
+-X25 0800 or whitespace
++X25 0805 or whitespace
+ ARP 0806
+ 802_1Q 8100 802.1Q Virtual LAN tagged frame
+ IPX 8137
+@@ -30,5 +30,4 @@
+ PPP_SES 8864 PPPoE session messages
+ ATMMPOA 884C MultiProtocol over ATM
+ ATMFATE 8884 Frame-based ATM Transport over Ethernet
+-
+-
++LOOP 9000
+--- ebtables-v2.0pre7/include/ebtables_u.h Wed Jun 5 21:43:27 2002
++++ ebtables-v2.0pre8.001/include/ebtables_u.h Thu Jun 27 18:53:55 2002
+@@ -28,11 +28,23 @@
+
+ struct ebt_u_entries
+ {
+- __u8 policy;
++ int policy;
+ __u32 nentries;
++ // counter offset for this chain
++ unsigned int counter_offset;
++ // used for udc
++ unsigned int hook_mask;
++ char name[EBT_CHAIN_MAXNAMELEN];
+ struct ebt_u_entry *entries;
+ };
+
++struct ebt_u_chain_list
++{
++ struct ebt_u_entries *udc;
++ struct ebt_u_chain_list *next;
++ // this is only used internally, in communications.c
++ char *kernel_start;
++};
+
+ struct ebt_u_replace
+ {
+@@ -41,8 +53,8 @@
+ // nr of rules in the table
+ unsigned int nentries;
+ struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
+- // how many counters in front of it?
+- unsigned int counter_entry[NF_BR_NUMHOOKS];
++ // user defined chains (udc) list
++ struct ebt_u_chain_list *udc;
+ // nr of counters userspace expects back
+ unsigned int num_counters;
+ // where the kernel will put the old counters
+@@ -107,7 +119,7 @@
+ struct ebt_entry_match **match);
+ void (*final_check)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_match *match,
+- const char *name, unsigned int hook);
++ const char *name, unsigned int hook_mask, unsigned int time);
+ void (*print)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_match *match);
+ int (*compare)(const struct ebt_entry_match *m1,
+@@ -134,7 +146,7 @@
+ struct ebt_entry_watcher **watcher);
+ void (*final_check)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_watcher *watch, const char *name,
+- unsigned int hook);
++ unsigned int hook_mask, unsigned int time);
+ void (*print)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_watcher *watcher);
+ int (*compare)(const struct ebt_entry_watcher *w1,
+@@ -158,7 +170,7 @@
+ struct ebt_entry_target **target);
+ void (*final_check)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_target *target, const char *name,
+- unsigned int hook);
++ unsigned int hook_mask, unsigned int time);
+ void (*print)(const struct ebt_u_entry *entry,
+ const struct ebt_entry_target *target);
+ int (*compare)(const struct ebt_entry_target *t1,
+@@ -175,7 +187,7 @@
+ void register_match(struct ebt_u_match *);
+ void register_watcher(struct ebt_u_watcher *);
+ void register_target(struct ebt_u_target *t);
+-void get_table(struct ebt_u_replace *repl);
++int get_table(struct ebt_u_replace *repl);
+ struct ebt_u_target *find_target(const char *name);
+ struct ebt_u_match *find_match(const char *name);
+ struct ebt_u_watcher *find_watcher(const char *name);
+@@ -185,7 +197,8 @@
+ void get_dbinfo(struct brdb_dbinfo *nr);
+ void get_db(int len, struct brdb_dbentry *db);
+ void deliver_allowdb(__u16 *decision);
+-int name_to_protocol(char *name);
++int name_to_number(char *name, __u16 *proto);
++int number_to_name(unsigned short proto, char *name);
+ void check_option(unsigned int *flags, unsigned int mask);
+ int check_inverse(const char option[]);
+ #define print_bug(format, args...) \
diff --git a/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz b/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz
new file mode 100644
index 0000000..9dcd7c5
--- /dev/null
+++ b/userspace/patches/zipped/ebtables-v2.0pre8.tar.gz
Binary files differ