summaryrefslogtreecommitdiffstats
path: root/kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff')
-rw-r--r--kernel/patches/incremental-patches/ebtables-v2.0_vs_2.4.18.pre9.001.diff1155
1 files changed, 1155 insertions, 0 deletions
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"
+