WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 3 of 3] Remus: support for network buffering

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 3 of 3] Remus: support for network buffering
From: Brendan Cully <brendan@xxxxxxxxx>
Date: Thu, 12 Nov 2009 17:10:24 -0800
Cc: andy@xxxxxxxxx
Delivery-date: Thu, 12 Nov 2009 17:15:22 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <patchbomb.1258074621@xxxxxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <patchbomb.1258074621@xxxxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mercurial-patchbomb/1.3.1+375-2dee9a359262
# HG changeset patch
# User Brendan Cully <brendan@xxxxxxxxx>
# Date 1258074170 28800
# Node ID 6caed9eff54375d6fb561dab0ea1213e37e98339
# Parent  4e36da19dc8f433910be8adabd8a3e4e5cead5d6
Remus: support for network buffering

This currently relies on the third-party IMQ patch (linuximq.net) being present
in dom0. The plan is to replace this with a direct hook into netback eventually.

This patch includes a pared-down and patched copy of ebtables to install IMQ
on a VIF.

Signed-off-by: Brendan Cully <brendan@xxxxxxxxx>

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -205,6 +205,8 @@
 ^tools/pygrub/build/.*$
 ^tools/python/build/.*$
 ^tools/python/xen/util/path\.py$
+^tools/remus/imqebt/imqebt$
+^tools/remus/kmod/.*(\.cmd|\.mod|\.ko|\.mod\.c|\.symvers|\.xen)$
 ^tools/security/secpol_tool$
 ^tools/security/xen/.*$
 ^tools/security/xensec_tool$
diff --git a/tools/remus/Makefile b/tools/remus/Makefile
--- a/tools/remus/Makefile
+++ b/tools/remus/Makefile
@@ -1,20 +1,17 @@
 XEN_ROOT=../..
 include $(XEN_ROOT)/tools/Rules.mk
 
+SUBDIRS-y := imqebt kmod
+
 SCRIPTS = remus
 
 .PHONY: all
-all: build
-
-.PHONY: build
-build:
-       echo "Nothing to do"
+all: subdirs-all
 
 .PHONY: install
-install:
+install: subdirs-install
        $(INSTALL_DIR) $(DESTDIR)$(BINDIR)
        $(INSTALL_PYTHON_PROG) $(SCRIPTS) $(DESTDIR)$(BINDIR)
 
 .PHONY: clean
-clean:
-       echo "Nothing to do"
+clean: subdirs-clean
diff --git a/tools/remus/imqebt/Makefile b/tools/remus/imqebt/Makefile
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/Makefile
@@ -0,0 +1,97 @@
+# ebtables Makefile (reworked for Remus IMQ control)
+
+XEN_ROOT=../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+PROGNAME:=ebtables
+PROGRELEASE:=1
+PROGVERSION_:=2.0.9
+PROGVERSION:=$(PROGVERSION_)-$(PROGRELEASE)
+PROGDATE:=June\ 2009
+
+ifeq ($(shell uname -m),sparc64)
+CFLAGS+=-DEBT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+include extensions/Makefile
+
+OBJECTS2:=getethertype.o communication.o libebtc.o \
+useful_functions.o ebtables.o
+
+OBJECTS:=$(OBJECTS2) $(EXT_OBJS) $(EXT_LIBS)
+
+KERNEL_INCLUDES?=include/
+
+ETHERTYPESPATH?=$(ETCDIR)
+ETHERTYPESFILE:=$(ETHERTYPESPATH)/ethertypes
+
+PIPE_DIR?=/tmp/$(PROGNAME)-v$(PROGVERSION)
+PIPE=$(PIPE_DIR)/ebtablesd_pipe
+EBTD_CMDLINE_MAXLN?=2048
+EBTD_ARGC_MAX?=50
+
+PROGSPECS:=-DPROGVERSION=\"$(PROGVERSION)\" \
+       -DPROGNAME=\"$(PROGNAME)\" \
+       -DPROGDATE=\"$(PROGDATE)\" \
+       -D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\" \
+       -DEBTD_ARGC_MAX=$(EBTD_ARGC_MAX) \
+       -DEBTD_CMDLINE_MAXLN=$(EBTD_CMDLINE_MAXLN)
+
+# Uncomment for debugging (slower)
+#PROGSPECS+=-DEBT_DEBUG
+#CFLAGS+=-ggdb
+
+PROGRAMS = imqebt
+
+.PHONY: all
+all: build
+
+.PHONY: build
+build: $(PROGRAMS)
+
+# a little scripting for a static binary, making one for ebtables-restore
+# should be completely analogous
+imqebt: extensions/ebt_*.c extensions/ebtable_*.c ebtables.c communication.c 
ebtables-standalone.c getethertype.c libebtc.c useful_functions.c
+       cp ebtables-standalone.c ebtables-standalone.c_ ; \
+       cp include/ebtables_u.h include/ebtables_u.h_ ; \
+       sed "s/ main(/ pseudomain(/" ebtables-standalone.c > 
ebtables-standalone.c__ ; \
+       mv ebtables-standalone.c__ ebtables-standalone.c ; \
+       printf "\nint main(int argc, char *argv[])\n{\n "  >> 
ebtables-standalone.c ; \
+       for arg in $(EXT_FUNC) \
+       ; do \
+       sed s/_init/_$${arg}_init/ extensions/ebt_$${arg}.c > 
extensions/ebt_$${arg}.c_ ; \
+       mv extensions/ebt_$${arg}.c_ extensions/ebt_$${arg}.c ; \
+       printf "\t%s();\n" _$${arg}_init >> ebtables-standalone.c ; \
+       printf "extern void %s(void);\n" _$${arg}_init >> include/ebtables_u.h 
; \
+       done ; \
+       for arg in $(EXT_TABLES) \
+       ; do \
+       sed s/_init/_t_$${arg}_init/ extensions/ebtable_$${arg}.c > 
extensions/ebtable_$${arg}.c_ ; \
+       mv extensions/ebtable_$${arg}.c_ extensions/ebtable_$${arg}.c ; \
+       printf "\t%s();\n" _t_$${arg}_init >> ebtables-standalone.c ; \
+       printf "extern void %s(void);\n" _t_$${arg}_init >> 
include/ebtables_u.h ; \
+       done ; \
+       printf "\n\tpseudomain(argc, argv);\n\treturn 0;\n}\n" >> 
ebtables-standalone.c ;\
+       $(CC) $(CFLAGS) $(PROGSPECS) -o $@ $^ -I$(KERNEL_INCLUDES) -Iinclude ; \
+       for arg in $(EXT_FUNC) \
+       ; do \
+       sed "s/ .*_init/ _init/" extensions/ebt_$${arg}.c > 
extensions/ebt_$${arg}.c_ ; \
+       mv extensions/ebt_$${arg}.c_ extensions/ebt_$${arg}.c ; \
+       done ; \
+       for arg in $(EXT_TABLES) \
+       ; do \
+       sed "s/ .*_init/ _init/" extensions/ebtable_$${arg}.c > 
extensions/ebtable_$${arg}.c_ ; \
+       mv extensions/ebtable_$${arg}.c_ extensions/ebtable_$${arg}.c ; \
+       done ; \
+       mv ebtables-standalone.c_ ebtables-standalone.c ; \
+       mv include/ebtables_u.h_ include/ebtables_u.h
+
+.PHONY: install
+install: build
+       $(INSTALL_DIR) $(DESTDIR)$(PRIVATE_BINDIR)
+       $(INSTALL_PROG) $(PROGRAMS) $(DESTDIR)$(PRIVATE_BINDIR)
+
+.PHONY: clean
+clean:
+       rm -f imqebt
+       rm -f *.o *~ *.so
diff --git a/tools/remus/imqebt/README b/tools/remus/imqebt/README
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/README
@@ -0,0 +1,2 @@
+This is a fork of ebtables for installing IMQ on a bridged device.
+Like the original code, it is released under the GPL.
\ No newline at end of file
diff --git a/tools/remus/imqebt/communication.c 
b/tools/remus/imqebt/communication.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/communication.c
@@ -0,0 +1,762 @@
+/*
+ * communication.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ */
+
+/*
+ * All the userspace/kernel communication is in this file.
+ * The other code should not have to know anything about the way the
+ * kernel likes the structure of the table data.
+ * The other code works with linked lists. So, the translation is done here.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "include/ebtables_u.h"
+
+extern char* hooknames[NF_BR_NUMHOOKS];
+
+#ifdef KERNEL_64_USERSPACE_32
+#define sparc_cast (uint64_t)
+#else
+#define sparc_cast
+#endif
+
+int sockfd = -1;
+
+static int get_sockfd(void)
+{
+       int ret = 0;
+       if (sockfd == -1) {
+               sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+               if (sockfd < 0) {
+                       ebt_print_error("Problem getting a socket, "
+                                       "you probably don't have the right "
+                                       "permissions");
+                       ret = -1;
+               }
+       }
+       return ret;
+}
+
+static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl)
+{
+       struct ebt_replace *new;
+       struct ebt_u_entry *e;
+       struct ebt_u_match_list *m_l;
+       struct ebt_u_watcher_list *w_l;
+       struct ebt_u_entries *entries;
+       char *p, *base;
+       int i, j;
+       unsigned int entries_size = 0, *chain_offsets;
+
+       new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+       if (!new)
+               ebt_print_memory();
+       new->valid_hooks = u_repl->valid_hooks;
+       strcpy(new->name, u_repl->name);
+       new->nentries = u_repl->nentries;
+       new->num_counters = u_repl->num_counters;
+       new->counters = sparc_cast u_repl->counters;
+       chain_offsets = (unsigned int *)malloc(u_repl->num_chains * 
sizeof(unsigned int));
+       /* Determine size */
+       for (i = 0; i < u_repl->num_chains; i++) {
+               if (!(entries = u_repl->chains[i]))
+                       continue;
+               chain_offsets[i] = entries_size;
+               entries_size += sizeof(struct ebt_entries);
+               j = 0;
+               e = entries->entries->next;
+               while (e != entries->entries) {
+                       j++;
+                       entries_size += sizeof(struct ebt_entry);
+                       m_l = e->m_list;
+                       while (m_l) {
+                               entries_size += m_l->m->match_size +
+                                  sizeof(struct ebt_entry_match);
+                               m_l = m_l->next;
+                       }
+                       w_l = e->w_list;
+                       while (w_l) {
+                               entries_size += w_l->w->watcher_size +
+                                  sizeof(struct ebt_entry_watcher);
+                               w_l = w_l->next;
+                       }
+                       entries_size += e->t->target_size +
+                          sizeof(struct ebt_entry_target);
+                       e = e->next;
+               }
+               /* A little sanity check */
+               if (j != entries->nentries)
+                       ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j,
+                          entries->nentries, entries->name);
+       }
+
+       new->entries_size = entries_size;
+       p = (char *)malloc(entries_size);
+       if (!p)
+               ebt_print_memory();
+
+       /* Put everything in one block */
+       new->entries = sparc_cast p;
+       for (i = 0; i < u_repl->num_chains; i++) {
+               struct ebt_entries *hlp;
+
+               hlp = (struct ebt_entries *)p;
+               if (!(entries = u_repl->chains[i]))
+                       continue;
+               if (i < NF_BR_NUMHOOKS)
+                       new->hook_entry[i] = sparc_cast hlp;
+               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 = entries->entries->next;
+               while (e != entries->entries) {
+                       struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+                       tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
+                       tmp->invflags = e->invflags;
+                       tmp->ethproto = e->ethproto;
+                       strcpy(tmp->in, e->in);
+                       strcpy(tmp->out, e->out);
+                       strcpy(tmp->logical_in, e->logical_in);
+                       strcpy(tmp->logical_out, e->logical_out);
+                       memcpy(tmp->sourcemac, e->sourcemac,
+                          sizeof(tmp->sourcemac));
+                       memcpy(tmp->sourcemsk, e->sourcemsk,
+                          sizeof(tmp->sourcemsk));
+                       memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
+                       memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
+
+                       base = p;
+                       p += sizeof(struct ebt_entry);
+                       m_l = e->m_list;
+                       while (m_l) {
+                               memcpy(p, m_l->m, m_l->m->match_size +
+                                  sizeof(struct ebt_entry_match));
+                               p += m_l->m->match_size +
+                                  sizeof(struct ebt_entry_match);
+                               m_l = m_l->next;
+                       }
+                       tmp->watchers_offset = p - base;
+                       w_l = e->w_list;
+                       while (w_l) {
+                               memcpy(p, w_l->w, w_l->w->watcher_size +
+                                  sizeof(struct ebt_entry_watcher));
+                               p += w_l->w->watcher_size +
+                                  sizeof(struct ebt_entry_watcher);
+                               w_l = w_l->next;
+                       }
+                       tmp->target_offset = p - base;
+                       memcpy(p, e->t, e->t->target_size +
+                          sizeof(struct ebt_entry_target));
+                       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;
+               }
+       }
+
+       /* Sanity check */
+       if (p - (char *)new->entries != new->entries_size)
+               ebt_print_bug("Entries_size bug");
+       free(chain_offsets);
+       return new;
+}
+
+static void store_table_in_file(char *filename, struct ebt_replace *repl)
+{
+       char *data;
+       int size;
+       int fd;
+
+       /* Start from an empty file with right priviliges */
+       if (!(fd = creat(filename, 0600))) {
+               ebt_print_error("Couldn't create file %s", filename);
+               return;
+       }
+
+       size = sizeof(struct ebt_replace) + repl->entries_size +
+          repl->nentries * sizeof(struct ebt_counter);
+       data = (char *)malloc(size);
+       if (!data)
+               ebt_print_memory();
+       memcpy(data, repl, sizeof(struct ebt_replace));
+       memcpy(data + sizeof(struct ebt_replace), (char *)repl->entries,
+          repl->entries_size);
+       /* Initialize counters to zero, deliver_counters() can update them */
+       memset(data + sizeof(struct ebt_replace) + repl->entries_size,
+          0, repl->nentries * sizeof(struct ebt_counter));
+       if (write(fd, data, size) != size)
+               ebt_print_error("Couldn't write everything to file %s",
+                               filename);
+       close(fd);
+       free(data);
+}
+
+void ebt_deliver_table(struct ebt_u_replace *u_repl)
+{
+       socklen_t optlen;
+       struct ebt_replace *repl;
+
+       /* Translate the struct ebt_u_replace to a struct ebt_replace */
+       repl = translate_user2kernel(u_repl);
+       if (u_repl->filename != NULL) {
+               store_table_in_file(u_repl->filename, repl);
+               goto free_repl;
+       }
+       /* Give the data to the kernel */
+       optlen = sizeof(struct ebt_replace) + repl->entries_size;
+       if (get_sockfd())
+               goto free_repl;
+       if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+               goto free_repl;
+       if (u_repl->command == 8) { /* The ebtables module may not
+                                    * yet be loaded with --atomic-commit */
+               ebtables_insmod("ebtables");
+               if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES,
+                   repl, optlen))
+                       goto free_repl;
+       }
+
+       ebt_print_error("The kernel doesn't support a certain ebtables"
+                   " extension, consider recompiling your kernel or insmod"
+                   " the extension");
+free_repl:
+       if (repl) {
+               free(repl->entries);
+               free(repl);
+       }
+}
+
+static int store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+{
+       int size = repl->nentries * sizeof(struct ebt_counter), ret = 0;
+       unsigned int entries_size;
+       struct ebt_replace hlp;
+       FILE *file;
+
+       if (!(file = fopen(filename, "r+b"))) {
+               ebt_print_error("Could not open file %s", filename);
+               return -1;
+       }
+       /* Find out entries_size and then set the file pointer to the
+        * counters */
+       if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
+          || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
+          sizeof(unsigned int) ||
+          fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) {
+               ebt_print_error("File %s is corrupt", filename);
+               ret = -1;
+               goto close_file;
+       }
+       if (fwrite(repl->counters, sizeof(char), size, file) != size) {
+               ebt_print_error("Could not write everything to file %s",
+                               filename);
+               ret = -1;
+       }
+close_file:
+       fclose(file);
+       return 0;
+}
+
+/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel
+ * and resets the counterchanges to CNT_NORM */
+void ebt_deliver_counters(struct ebt_u_replace *u_repl)
+{
+       struct ebt_counter *old, *new, *newcounters;
+       socklen_t optlen;
+       struct ebt_replace repl;
+       struct ebt_cntchanges *cc = u_repl->cc->next, *cc2;
+       struct ebt_u_entries *entries = NULL;
+       struct ebt_u_entry *next = NULL;
+       int i, chainnr = 0;
+
+       if (u_repl->nentries == 0)
+               return;
+
+       newcounters = (struct ebt_counter *)
+          malloc(u_repl->nentries * sizeof(struct ebt_counter));
+       if (!newcounters)
+               ebt_print_memory();
+       memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
+       old = u_repl->counters;
+       new = newcounters;
+       while (cc != u_repl->cc) {
+               if (!next || next == entries->entries) {
+                       while (chainnr < u_repl->num_chains && (!(entries = 
u_repl->chains[chainnr++]) ||
+                              (next = entries->entries->next) == 
entries->entries));
+                       if (chainnr == u_repl->num_chains)
+                               break;
+               }
+               if (cc->type == CNT_NORM) {
+                       /* 'Normal' rule, meaning we didn't do anything to it
+                        * So, we just copy */
+                       *new = *old;
+                       next->cnt = *new;
+                       next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+                       old++; /* We've used an old counter */
+                       new++; /* We've set a new counter */
+                       next = next->next;
+               } else if (cc->type == CNT_DEL) {
+                       old++; /* Don't use this old counter */
+               } else {
+                       if (cc->type == CNT_CHANGE) {
+                               if (cc->change % 3 == 1)
+                                       new->pcnt = old->pcnt + 
next->cnt_surplus.pcnt;
+                               else if (cc->change % 3 == 2)
+                                       new->pcnt = old->pcnt - 
next->cnt_surplus.pcnt;
+                               else
+                                       new->pcnt = next->cnt.pcnt;
+                               if (cc->change / 3 == 1)
+                                       new->bcnt = old->bcnt + 
next->cnt_surplus.bcnt;
+                               else if (cc->change / 3 == 2)
+                                       new->bcnt = old->bcnt - 
next->cnt_surplus.bcnt;
+                               else
+                                       new->bcnt = next->cnt.bcnt;
+                       } else
+                               *new = next->cnt;
+                       next->cnt = *new;
+                       next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+                       if (cc->type == CNT_ADD)
+                               new++;
+                       else {
+                               old++;
+                               new++;
+                       }
+                       next = next->next;
+               }
+               cc = cc->next;
+       }
+
+       free(u_repl->counters);
+       u_repl->counters = newcounters;
+       u_repl->num_counters = u_repl->nentries;
+       /* Reset the counterchanges to CNT_NORM and delete the unused cc */
+       i = 0;
+       cc = u_repl->cc->next;
+       while (cc != u_repl->cc) {
+               if (cc->type == CNT_DEL) {
+                       cc->prev->next = cc->next;
+                       cc->next->prev = cc->prev;
+                       cc2 = cc->next;
+                       free(cc);
+                       cc = cc2;
+               } else {
+                       cc->type = CNT_NORM;
+                       cc->change = 0;
+                       i++;
+                       cc = cc->next;
+               }
+       }
+       if (i != u_repl->nentries)
+               ebt_print_bug("i != u_repl->nentries");
+       if (u_repl->filename != NULL) {
+               store_counters_in_file(u_repl->filename, u_repl);
+               return;
+       }
+       optlen = u_repl->nentries * sizeof(struct ebt_counter) +
+          sizeof(struct ebt_replace);
+       /* Now put the stuff in the kernel's struct ebt_replace */
+       repl.counters = sparc_cast u_repl->counters;
+       repl.num_counters = u_repl->num_counters;
+       memcpy(repl.name, u_repl->name, sizeof(repl.name));
+
+       if (get_sockfd())
+               return;
+       if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+               ebt_print_bug("Couldn't update kernel counters");
+}
+
+static int
+ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
+{
+       struct ebt_u_match_list *new;
+       int ret = 0;
+
+       new = (struct ebt_u_match_list *)
+          malloc(sizeof(struct ebt_u_match_list));
+       if (!new)
+               ebt_print_memory();
+       new->m = (struct ebt_entry_match *)
+          malloc(m->match_size + sizeof(struct ebt_entry_match));
+       if (!new->m)
+               ebt_print_memory();
+       memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+       new->next = NULL;
+       **l = new;
+       *l = &new->next;
+       if (ebt_find_match(new->m->u.name) == NULL) {
+               ebt_print_error("Kernel match %s unsupported by userspace tool",
+                               new->m->u.name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int
+ebt_translate_watcher(struct ebt_entry_watcher *w,
+   struct ebt_u_watcher_list ***l)
+{
+       struct ebt_u_watcher_list *new;
+       int ret = 0;
+
+       new = (struct ebt_u_watcher_list *)
+          malloc(sizeof(struct ebt_u_watcher_list));
+       if (!new)
+               ebt_print_memory();
+       new->w = (struct ebt_entry_watcher *)
+          malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
+       if (!new->w)
+               ebt_print_memory();
+       memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+       new->next = NULL;
+       **l = new;
+       *l = &new->next;
+       if (ebt_find_watcher(new->w->u.name) == NULL) {
+               ebt_print_error("Kernel watcher %s unsupported by userspace "
+                               "tool", new->w->u.name);
+               ret = -1;
+       }
+       return ret;
+}
+
+static int
+ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt,
+   int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl,
+   unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc)
+{
+       /* An entry */
+       if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+               struct ebt_u_entry *new;
+               struct ebt_u_match_list **m_l;
+               struct ebt_u_watcher_list **w_l;
+               struct ebt_entry_target *t;
+
+               new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+               if (!new)
+                       ebt_print_memory();
+               new->bitmask = e->bitmask;
+               /*
+                * Plain userspace code doesn't know about
+                * EBT_ENTRY_OR_ENTRIES
+                */
+               new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
+               new->invflags = e->invflags;
+               new->ethproto = e->ethproto;
+               strcpy(new->in, e->in);
+               strcpy(new->out, e->out);
+               strcpy(new->logical_in, e->logical_in);
+               strcpy(new->logical_out, e->logical_out);
+               memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
+               memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
+               memcpy(new->destmac, e->destmac, sizeof(new->destmac));
+               memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
+               if (*totalcnt >= u_repl->nentries)
+                       ebt_print_bug("*totalcnt >= u_repl->nentries");
+               new->cnt = u_repl->counters[*totalcnt];
+               new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0;
+               new->cc = *cc;
+               *cc = (*cc)->next;
+               new->m_list = NULL;
+               new->w_list = NULL;
+               new->next = (*u_e)->next;
+               new->next->prev = new;
+               (*u_e)->next = new;
+               new->prev = *u_e;
+               *u_e = new;
+               m_l = &new->m_list;
+               EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
+               w_l = &new->w_list;
+               EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
+
+               t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+               new->t = (struct ebt_entry_target *)
+                  malloc(t->target_size + sizeof(struct ebt_entry_target));
+               if (!new->t)
+                       ebt_print_memory();
+               if (ebt_find_target(t->u.name) == NULL) {
+                       ebt_print_error("Kernel target %s unsupported by "
+                                       "userspace tool", t->u.name);
+                       return -1;
+               }
+               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;
+
+                       if (verdict >= 0) {
+                               tmp += verdict;
+                               for (i = NF_BR_NUMHOOKS; i < 
u_repl->num_chains; i++)
+                                       if (u_repl->chains[i]->kernel_start == 
tmp)
+                                               break;
+                               if (i == u_repl->num_chains)
+                                       ebt_print_bug("Can't find udc for 
jump");
+                               ((struct ebt_standard_target *)new->t)->verdict 
= i-NF_BR_NUMHOOKS;
+                       }
+               }
+
+               (*cnt)++;
+               (*totalcnt)++;
+               return 0;
+       } else { /* A new chain */
+               int i;
+               struct ebt_entries *entries = (struct ebt_entries *)e;
+
+               if (*n != *cnt)
+                       ebt_print_bug("Nr of entries in the chain is wrong");
+               *n = entries->nentries;
+               *cnt = 0;
+               for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+                       if (valid_hooks & (1 << i))
+                               break;
+               *hook = i;
+               *u_e = u_repl->chains[*hook]->entries;
+               return 0;
+       }
+}
+
+/* Initialize all chain headers */
+static int
+ebt_translate_chains(struct ebt_entry *e, 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;
+
+       if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+               for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+                       if (valid_hooks & (1 << i))
+                               break;
+               new = (struct ebt_u_entries *)malloc(sizeof(struct 
ebt_u_entries));
+               if (!new)
+                       ebt_print_memory();
+               if (i == u_repl->max_chains)
+                       ebt_double_chains(u_repl);
+               u_repl->chains[i] = new;
+               if (i >= NF_BR_NUMHOOKS)
+                       new->kernel_start = (char *)e;
+               *hook = i;
+               new->nentries = entries->nentries;
+               new->policy = entries->policy;
+               new->entries = (struct ebt_u_entry *)malloc(sizeof(struct 
ebt_u_entry));
+               if (!new->entries)
+                       ebt_print_memory();
+               new->entries->next = new->entries->prev = new->entries;
+               new->counter_offset = entries->counter_offset;
+               strcpy(new->name, entries->name);
+       }
+       return 0;
+}
+
+static int retrieve_from_file(char *filename, struct ebt_replace *repl,
+   char command)
+{
+       FILE *file;
+       char *hlp = NULL, *entries;
+       struct ebt_counter *counters;
+       int size, ret = 0;
+
+       if (!(file = fopen(filename, "r+b"))) {
+               ebt_print_error("Could not open file %s", filename);
+               return -1;
+       }
+       /* Make sure table name is right if command isn't -L or --atomic-commit 
*/
+       if (command != 'L' && command != 8) {
+               hlp = (char *)malloc(strlen(repl->name) + 1);
+               if (!hlp)
+                       ebt_print_memory();
+               strcpy(hlp, repl->name);
+       }
+       if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
+          != sizeof(struct ebt_replace)) {
+               ebt_print_error("File %s is corrupt", filename);
+               ret = -1;
+               goto close_file;
+       }
+       if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) {
+               ebt_print_error("File %s contains wrong table name or is "
+                               "corrupt", filename);
+               ret = -1;
+               goto close_file;
+       } else if (!ebt_find_table(repl->name)) {
+               ebt_print_error("File %s contains invalid table name",
+                               filename);
+               ret = -1;
+               goto close_file;
+       }
+
+       size = sizeof(struct ebt_replace) +
+          repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+       fseek(file, 0, SEEK_END);
+       if (size != ftell(file)) {
+               ebt_print_error("File %s has wrong size", filename);
+               ret = -1;
+               goto close_file;
+       }
+       entries = (char *)malloc(repl->entries_size);
+       if (!entries)
+               ebt_print_memory();
+       repl->entries = sparc_cast entries;
+       if (repl->nentries) {
+               counters = (struct ebt_counter *)
+                  malloc(repl->nentries * sizeof(struct ebt_counter));
+               repl->counters = sparc_cast counters;
+               if (!repl->counters)
+                       ebt_print_memory();
+       } else
+               repl->counters = sparc_cast NULL;
+       /* Copy entries and counters */
+       if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
+          fread((char *)repl->entries, sizeof(char), repl->entries_size, file)
+          != repl->entries_size ||
+          fseek(file, sizeof(struct ebt_replace) + repl->entries_size,
+                SEEK_SET)
+          || fread((char *)repl->counters, sizeof(char),
+          repl->nentries * sizeof(struct ebt_counter), file)
+          != repl->nentries * sizeof(struct ebt_counter)) {
+               ebt_print_error("File %s is corrupt", filename);
+               free(entries);
+               repl->entries = NULL;
+               ret = -1;
+       }
+close_file:
+       fclose(file);
+       free(hlp);
+       return ret;
+}
+
+static int retrieve_from_kernel(struct ebt_replace *repl, char command,
+                               int init)
+{
+       socklen_t optlen;
+       int optname;
+       char *entries;
+
+       optlen = sizeof(struct ebt_replace);
+       if (get_sockfd())
+               return -1;
+       /* --atomic-init || --init-table */
+       if (init)
+               optname = EBT_SO_GET_INIT_INFO;
+       else
+               optname = EBT_SO_GET_INFO;
+       if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+               return -1;
+
+       if ( !(entries = (char *)malloc(repl->entries_size)) )
+               ebt_print_memory();
+       repl->entries = sparc_cast entries;
+       if (repl->nentries) {
+               struct ebt_counter *counters;
+
+               if (!(counters = (struct ebt_counter *)
+                  malloc(repl->nentries * sizeof(struct ebt_counter))) )
+                       ebt_print_memory();
+               repl->counters = sparc_cast counters;
+       }
+       else
+               repl->counters = sparc_cast NULL;
+
+       /* We want to receive the counters */
+       repl->num_counters = repl->nentries;
+       optlen += repl->entries_size + repl->num_counters *
+          sizeof(struct ebt_counter);
+       if (init)
+               optname = EBT_SO_GET_INIT_ENTRIES;
+       else
+               optname = EBT_SO_GET_ENTRIES;
+       if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+               ebt_print_bug("Hmm, what is wrong??? bug#1");
+
+       return 0;
+}
+
+int ebt_get_table(struct ebt_u_replace *u_repl, int init)
+{
+       int i, j, k, hook;
+       struct ebt_replace repl;
+       struct ebt_u_entry *u_e = NULL;
+       struct ebt_cntchanges *new_cc, *cc;
+
+       strcpy(repl.name, u_repl->name);
+       if (u_repl->filename != NULL) {
+               if (init)
+                       ebt_print_bug("Getting initial table data from a file 
is impossible");
+               if (retrieve_from_file(u_repl->filename, &repl, 
u_repl->command))
+                       return -1;
+               /* -L with a wrong table name should be dealt with silently */
+               strcpy(u_repl->name, repl.name);
+       } else if (retrieve_from_kernel(&repl, u_repl->command, init))
+               return -1;
+
+       /* Translate the struct ebt_replace to a struct ebt_u_replace */
+       u_repl->valid_hooks = repl.valid_hooks;
+       u_repl->nentries = repl.nentries;
+       u_repl->num_counters = repl.num_counters;
+       u_repl->counters = repl.counters;
+       u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct 
ebt_cntchanges));
+       if (!u_repl->cc)
+               ebt_print_memory();
+       u_repl->cc->next = u_repl->cc->prev = u_repl->cc;
+       cc = u_repl->cc;
+       for (i = 0; i < repl.nentries; i++) {
+               new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct 
ebt_cntchanges));
+               if (!new_cc)
+                       ebt_print_memory();
+               new_cc->type = CNT_NORM;
+               new_cc->change = 0;
+               new_cc->prev = cc;
+               cc->next = new_cc;
+               cc = new_cc;
+       }
+       if (repl.nentries) {
+               new_cc->next = u_repl->cc;
+               u_repl->cc->prev = new_cc;
+       }
+       u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, 
sizeof(void *));
+       u_repl->max_chains = EBT_ORI_MAX_CHAINS;
+       hook = -1;
+       /* FIXME: Clean up when an error is encountered */
+       EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
+          &hook, u_repl, u_repl->valid_hooks);
+       if (hook >= NF_BR_NUMHOOKS)
+               u_repl->num_chains = hook + 1;
+       else
+               u_repl->num_chains = NF_BR_NUMHOOKS;
+       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 */
+       cc = u_repl->cc->next;
+       hook = -1;
+       EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size,
+          ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl,
+          u_repl->valid_hooks, (char *)repl.entries, &cc);
+       if (k != u_repl->nentries)
+               ebt_print_bug("Wrong total nentries");
+       free(repl.entries);
+       return 0;
+}
diff --git a/tools/remus/imqebt/ebtables-standalone.c 
b/tools/remus/imqebt/ebtables-standalone.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/ebtables-standalone.c
@@ -0,0 +1,14 @@
+#include <string.h>
+#include "include/ebtables_u.h"
+
+static struct ebt_u_replace replace;
+void ebt_early_init_once();
+
+int main(int argc, char *argv[])
+{
+       ebt_silent = 0;
+       ebt_early_init_once();
+       strcpy(replace.name, "filter");
+       do_command(argc, argv, EXEC_STYLE_PRG, &replace);
+       return 0;
+}
diff --git a/tools/remus/imqebt/ebtables.c b/tools/remus/imqebt/ebtables.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/ebtables.c
@@ -0,0 +1,1233 @@
+/*
+ * ebtables.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code was stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+
+/* Checks whether a command has already been specified */
+#define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->flags & 
OPT_ZERO)
+
+#define OPT_COMMAND    0x01
+#define OPT_TABLE      0x02
+#define OPT_IN         0x04
+#define OPT_OUT                0x08
+#define OPT_JUMP       0x10
+#define OPT_PROTOCOL   0x20
+#define OPT_SOURCE     0x40
+#define OPT_DEST       0x80
+#define OPT_ZERO       0x100
+#define OPT_LOGICALIN  0x200
+#define OPT_LOGICALOUT 0x400
+#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */
+#define OPT_COUNT      0x1000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_INCR   0x2000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_DECR   0x4000 /* This value is also defined in libebtc.c */
+
+/* Default command line options. Do not mess around with the already
+ * assigned numbers unless you know what you are doing */
+static struct option ebt_original_options[] =
+{
+       { "append"         , required_argument, 0, 'A' },
+       { "insert"         , required_argument, 0, 'I' },
+       { "delete"         , required_argument, 0, 'D' },
+       { "list"           , optional_argument, 0, 'L' },
+       { "Lc"             , no_argument      , 0, 4   },
+       { "Ln"             , no_argument      , 0, 5   },
+       { "Lx"             , no_argument      , 0, 6   },
+       { "Lmac2"          , no_argument      , 0, 12  },
+       { "zero"           , optional_argument, 0, 'Z' },
+       { "flush"          , optional_argument, 0, 'F' },
+       { "policy"         , required_argument, 0, 'P' },
+       { "in-interface"   , required_argument, 0, 'i' },
+       { "in-if"          , required_argument, 0, 'i' },
+       { "logical-in"     , required_argument, 0, 2   },
+       { "logical-out"    , required_argument, 0, 3   },
+       { "out-interface"  , required_argument, 0, 'o' },
+       { "out-if"         , required_argument, 0, 'o' },
+       { "version"        , no_argument      , 0, 'V' },
+       { "help"           , no_argument      , 0, 'h' },
+       { "jump"           , required_argument, 0, 'j' },
+       { "set-counters"   , required_argument, 0, 'c' },
+       { "change-counters", required_argument, 0, 'C' },
+       { "proto"          , required_argument, 0, 'p' },
+       { "protocol"       , required_argument, 0, 'p' },
+       { "db"             , required_argument, 0, 'b' },
+       { "source"         , required_argument, 0, 's' },
+       { "src"            , required_argument, 0, 's' },
+       { "destination"    , required_argument, 0, 'd' },
+       { "dst"            , required_argument, 0, 'd' },
+       { "table"          , required_argument, 0, 't' },
+       { "modprobe"       , required_argument, 0, 'M' },
+       { "new-chain"      , required_argument, 0, 'N' },
+       { "rename-chain"   , required_argument, 0, 'E' },
+       { "delete-chain"   , optional_argument, 0, 'X' },
+       { "atomic-init"    , no_argument      , 0, 7   },
+       { "atomic-commit"  , no_argument      , 0, 8   },
+       { "atomic-file"    , required_argument, 0, 9   },
+       { "atomic-save"    , no_argument      , 0, 10  },
+       { "init-table"     , no_argument      , 0, 11  },
+       { 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+/* Holds all the data */
+static struct ebt_u_replace *replace;
+
+/* The chosen table */
+static struct ebt_u_table *table;
+
+/* The pointers in here are special:
+ * The struct ebt_target pointer is actually a struct ebt_u_target pointer.
+ * I do not feel like using a union.
+ * We need a struct ebt_u_target pointer because we know the address of the 
data
+ * they point to won't change. We want to allow that the struct ebt_u_target.t
+ * member can change.
+ * The same holds for the struct ebt_match and struct ebt_watcher pointers */
+static struct ebt_u_entry *new_entry;
+
+
+static int global_option_offset;
+#define OPTION_OFFSET 256
+static struct option *merge_options(struct option *oldopts,
+   const struct option *newopts, unsigned int *options_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       if (!newopts || !oldopts || !options_offset)
+               ebt_print_bug("merge wrong");
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *options_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       if (!merge)
+               ebt_print_memory();
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *options_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+       /* Only free dynamically allocated stuff */
+       if (oldopts != ebt_original_options)
+               free(oldopts);
+
+       return merge;
+}
+
+static void merge_match(struct ebt_u_match *m)
+{
+       ebt_options = merge_options
+          (ebt_options, m->extra_ops, &(m->option_offset));
+}
+
+static void merge_watcher(struct ebt_u_watcher *w)
+{
+       ebt_options = merge_options
+          (ebt_options, w->extra_ops, &(w->option_offset));
+}
+
+static void merge_target(struct ebt_u_target *t)
+{
+       ebt_options = merge_options
+          (ebt_options, t->extra_ops, &(t->option_offset));
+}
+
+/* Be backwards compatible, so don't use '+' in kernel */
+#define IF_WILDCARD 1
+static void print_iface(const char *iface)
+{
+       char *c;
+
+       if ((c = strchr(iface, IF_WILDCARD)))
+               *c = '+';
+       printf("%s ", iface);
+       if (c)
+               *c = IF_WILDCARD;
+}
+
+/* We use replace->flags, so we can't use the following values:
+ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
+#define LIST_N    0x04
+#define LIST_C    0x08
+#define LIST_X    0x10
+#define LIST_MAC2 0x20
+
+/* Helper function for list_rules() */
+static void list_em(struct ebt_u_entries *entries)
+{
+       int i, j, space = 0, digits;
+       struct ebt_u_entry *hlp;
+       struct ebt_u_match_list *m_l;
+       struct ebt_u_watcher_list *w_l;
+       struct ebt_u_match *m;
+       struct ebt_u_watcher *w;
+       struct ebt_u_target *t;
+
+       if (replace->flags & LIST_MAC2)
+               ebt_printstyle_mac = 2;
+       else
+               ebt_printstyle_mac = 0;
+       hlp = entries->entries->next;
+       if (replace->flags & LIST_X && entries->policy != EBT_ACCEPT) {
+               printf("ebtables -t %s -P %s %s\n", replace->name,
+                  entries->name, ebt_standard_targets[-entries->policy - 1]);
+       } else if (!(replace->flags & LIST_X)) {
+               printf("\nBridge chain: %s, entries: %d, policy: %s\n",
+                  entries->name, entries->nentries,
+                  ebt_standard_targets[-entries->policy - 1]);
+       }
+
+       if (replace->flags & LIST_N) {
+               i = entries->nentries;
+               while (i > 9) {
+                       space++;
+                       i /= 10;
+               }
+       }
+
+       for (i = 0; i < entries->nentries; i++) {
+               if (replace->flags & LIST_N) {
+                       digits = 0;
+                       /* A little work to get nice rule numbers. */
+                       j = i + 1;
+                       while (j > 9) {
+                               digits++;
+                               j /= 10;
+                       }
+                       for (j = 0; j < space - digits; j++)
+                               printf(" ");
+                       printf("%d. ", i + 1);
+               }
+               if (replace->flags & LIST_X)
+                       printf("ebtables -t %s -A %s ",
+                          replace->name, entries->name);
+
+               /* The standard target's print() uses this to find out
+                * the name of a udc */
+               hlp->replace = replace;
+
+               /* 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("-p ");
+                       if (hlp->invflags & EBT_IPROTO)
+                               printf("! ");
+                       if (hlp->bitmask & EBT_802_3)
+                               printf("Length ");
+                       else {
+                               struct ethertypeent *ent;
+
+                               ent = 
getethertypebynumber(ntohs(hlp->ethproto));
+                               if (!ent)
+                                       printf("0x%x ", ntohs(hlp->ethproto));
+                               else
+                                       printf("%s ", ent->e_name);
+                       }
+               }
+               if (hlp->bitmask & EBT_SOURCEMAC) {
+                       printf("-s ");
+                       if (hlp->invflags & EBT_ISOURCE)
+                               printf("! ");
+                       ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk);
+                       printf(" ");
+               }
+               if (hlp->bitmask & EBT_DESTMAC) {
+                       printf("-d ");
+                       if (hlp->invflags & EBT_IDEST)
+                               printf("! ");
+                       ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk);
+                       printf(" ");
+               }
+               if (hlp->in[0] != '\0') {
+                       printf("-i ");
+                       if (hlp->invflags & EBT_IIN)
+                               printf("! ");
+                       print_iface(hlp->in);
+               }
+               if (hlp->logical_in[0] != '\0') {
+                       printf("--logical-in ");
+                       if (hlp->invflags & EBT_ILOGICALIN)
+                               printf("! ");
+                       print_iface(hlp->logical_in);
+               }
+               if (hlp->logical_out[0] != '\0') {
+                       printf("--logical-out ");
+                       if (hlp->invflags & EBT_ILOGICALOUT)
+                               printf("! ");
+                       print_iface(hlp->logical_out);
+               }
+               if (hlp->out[0] != '\0') {
+                       printf("-o ");
+                       if (hlp->invflags & EBT_IOUT)
+                               printf("! ");
+                       print_iface(hlp->out);
+               }
+
+               m_l = hlp->m_list;
+               while (m_l) {
+                       m = ebt_find_match(m_l->m->u.name);
+                       if (!m)
+                               ebt_print_bug("Match not found");
+                       m->print(hlp, m_l->m);
+                       m_l = m_l->next;
+               }
+               w_l = hlp->w_list;
+               while (w_l) {
+                       w = ebt_find_watcher(w_l->w->u.name);
+                       if (!w)
+                               ebt_print_bug("Watcher not found");
+                       w->print(hlp, w_l->w);
+                       w_l = w_l->next;
+               }
+
+               printf("-j ");
+               if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
+                       printf("%s ", hlp->t->u.name);
+               t = ebt_find_target(hlp->t->u.name);
+               if (!t)
+                       ebt_print_bug("Target '%s' not found", hlp->t->u.name);
+               t->print(hlp, hlp->t);
+               if (replace->flags & LIST_C) {
+                       uint64_t pcnt = hlp->cnt.pcnt;
+                       uint64_t bcnt = hlp->cnt.bcnt;
+
+                       if (replace->flags & LIST_X)
+                               printf("-c %llu %llu", pcnt, bcnt);
+                       else
+                               printf(", pcnt = %"PRIu64" -- bcnt = %"PRIu64, 
pcnt, bcnt);
+               }
+               printf("\n");
+               hlp = hlp->next;
+       }
+}
+
+static void print_help(void)
+{
+       struct ebt_u_match_list *m_l;
+       struct ebt_u_watcher_list *w_l;
+
+       PRINT_VERSION;
+       printf(
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[NX] [chain]\n"
+"ebtables -E old-chain-name new-chain-name\n\n"
+"Commands:\n"
+"--append -A chain             : append to chain\n"
+"--delete -D chain             : delete matching rule from chain\n"
+"--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
+"--change-counters -C chain\n"
+"          [rulenum] pcnt bcnt : change counters of existing rule\n"
+"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+"--list   -L [chain]           : list the rules in a chain or in all chains\n"
+"--flush  -F [chain]           : delete all rules in chain or in all chains\n"
+"--init-table                  : replace the kernel table with the initial 
table\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"
+"--atomic-commit               : update the kernel w/t table contained in 
<FILE>\n"
+"--atomic-init                 : put the initial kernel table into <FILE>\n"
+"--atomic-save                 : put the current kernel table into <FILE>\n"
+"--atomic-file file            : set <FILE> to file\n\n"
+"Options:\n"
+"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+"--src    -s [!] address[/mask]: source mac address\n"
+"--dst    -d [!] address[/mask]: destination mac address\n"
+"--in-if  -i [!] name[+]       : network input interface name\n"
+"--out-if -o [!] name[+]       : network output interface name\n"
+"--logical-in  [!] name[+]     : logical bridge input interface name\n"
+"--logical-out [!] name[+]     : logical bridge output interface name\n"
+"--set-counters -c chain\n"
+"          pcnt bcnt           : set the counters of the to be added rule\n"
+"--modprobe -M program         : try to insert modules using this program\n"
+"--version -V                  : print package version\n\n"
+"Environment variable:\n"
+ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its 
value"
+"\n\n");
+       m_l = new_entry->m_list;
+       while (m_l) {
+               ((struct ebt_u_match *)m_l->m)->help();
+               printf("\n");
+               m_l = m_l->next;
+       }
+       w_l = new_entry->w_list;
+       while (w_l) {
+               ((struct ebt_u_watcher *)w_l->w)->help();
+               printf("\n");
+               w_l = w_l->next;
+       }
+       ((struct ebt_u_target *)new_entry->t)->help();
+       printf("\n");
+       if (table->help)
+               table->help(ebt_hooknames);
+}
+
+/* Execute command L */
+static void list_rules(void)
+{
+       int i;
+
+       if (!(replace->flags & LIST_X))
+               printf("Bridge table: %s\n", table->name);
+       if (replace->selected_chain != -1)
+               list_em(ebt_to_chain(replace));
+       else {
+               /* Create new chains and rename standard chains when necessary 
*/
+               if (replace->flags & LIST_X && replace->num_chains > 
NF_BR_NUMHOOKS) {
+                       for (i = NF_BR_NUMHOOKS; i < replace->num_chains; i++)
+                               printf("ebtables -t %s -N %s\n", replace->name, 
replace->chains[i]->name);
+                       for (i = 0; i < NF_BR_NUMHOOKS; i++)
+                               if (replace->chains[i] && 
strcmp(replace->chains[i]->name, ebt_hooknames[i]))
+                                       printf("ebtables -t %s -E %s %s\n", 
replace->name, ebt_hooknames[i], replace->chains[i]->name);
+               }
+               for (i = 0; i < replace->num_chains; i++)
+                       if (replace->chains[i])
+                               list_em(replace->chains[i]);
+       }
+}
+
+static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
+{
+       char *colon = strchr(argv, ':'), *buffer;
+
+       if (colon) {
+               *colon = '\0';
+               if (*(colon + 1) == '\0')
+                       *rule_nr_end = -1; /* Until the last rule */
+               else {
+                       *rule_nr_end = strtol(colon + 1, &buffer, 10);
+                       if (*buffer != '\0' || *rule_nr_end == 0)
+                               return -1;
+               }
+       }
+       if (colon == argv)
+               *rule_nr = 1; /* Beginning with the first rule */
+       else {
+               *rule_nr = strtol(argv, &buffer, 10);
+               if (*buffer != '\0' || *rule_nr == 0)
+                       return -1;
+       }
+       if (!colon)
+               *rule_nr_end = *rule_nr;
+       return 0;
+}
+
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it (too annoying to take the increased
+ * counters in the kernel into account). */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int 
*rule_nr_end, int exec_style)
+{
+       char *buffer;
+       int ret = 0;
+
+       if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < 
'0' || argv[optind][1] > '9')) ||
+           (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0'  && 
argv[optind + 1][1] > '9')))
+               ebt_print_error2("The command -C needs at least 2 arguments");
+       if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 
2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
+               if (optind + 3 != argc)
+                       ebt_print_error2("No extra options allowed with -C 
start_nr[:end_nr] pcnt bcnt");
+               if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
+                       ebt_print_error2("Something is wrong with the rule 
number specification '%s'", argv[optind]);
+               optind++;
+       }
+
+       if (argv[optind][0] == '+') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+                       ebt_print_error2("Incrementing rule counters (%s) not 
allowed in daemon mode", argv[optind]);
+               ret += 1;
+               new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, 
&buffer, 10);
+       } else if (argv[optind][0] == '-') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+                       ebt_print_error2("Decrementing rule counters (%s) not 
allowed in daemon mode", argv[optind]);
+               ret += 2;
+               new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, 
&buffer, 10);
+       } else
+               new_entry->cnt_surplus.pcnt = strtoull(argv[optind], &buffer, 
10);
+
+       if (*buffer != '\0')
+               goto invalid;
+       optind++;
+       if (argv[optind][0] == '+') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+                       goto daemon_incr;
+               ret += 3;
+               new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, 
&buffer, 10);
+       } else if (argv[optind][0] == '-') {
+               if (exec_style == EXEC_STYLE_DAEMON)
+                       goto daemon_decr;
+               ret += 6;
+               new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, 
&buffer, 10);
+       } else
+               new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 
10);
+
+       if (*buffer != '\0')
+               goto invalid;
+       optind++;
+       return ret;
+invalid:
+       ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
+}
+
+static int parse_iface(char *iface, char *option)
+{
+       char *c;
+
+       if ((c = strchr(iface, '+'))) {
+               if (*(c + 1) != '\0') {
+                       ebt_print_error("Spurious characters after '+' wildcard 
for '%s'", option);
+                       return -1;
+               } else
+                       *c = IF_WILDCARD;
+       }
+       return 0;
+}
+
+void ebt_early_init_once(void)
+{
+       ebt_iterate_matches(merge_match);
+       ebt_iterate_watchers(merge_watcher);
+       ebt_iterate_targets(merge_target);
+}
+
+/* We use exec_style instead of #ifdef's because ebtables.so is a shared 
object. */
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_)
+{
+       char *buffer;
+       int c, i;
+       int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> 
-L <that>) */
+       int chcounter = 0; /* Needed for -C */
+       int policy = 0;
+       int rule_nr = 0;
+       int rule_nr_end = 0;
+       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;
+
+       opterr = 0;
+       ebt_modprobe = NULL;
+
+       replace = replace_;
+
+       /* The daemon doesn't use the environment variable */
+       if (exec_style == EXEC_STYLE_PRG) {
+               buffer = getenv(ATOMIC_ENV_VARIABLE);
+               if (buffer) {
+                       replace->filename = malloc(strlen(buffer) + 1);
+                       if (!replace->filename)
+                               ebt_print_memory();
+                       strcpy(replace->filename, buffer);
+                       buffer = NULL;
+               }
+       }
+
+       replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */
+       replace->selected_chain = -1;
+       replace->command = 'h';
+
+       if (!new_entry) {
+               new_entry = (struct ebt_u_entry *)malloc(sizeof(struct 
ebt_u_entry));
+               if (!new_entry)
+                       ebt_print_memory();
+       }
+       /* Put some sane values in our new entry */
+       ebt_initialize_entry(new_entry);
+       new_entry->replace = replace;
+
+       /* The scenario induced by this loop makes that:
+        * '-t'  ,'-M' and --atomic (if specified) have to come
+        * before '-A' and the like */
+
+       /* Getopt saves the day */
+       while ((c = getopt_long(argc, argv,
+          "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, 
NULL)) != -1) {
+               switch (c) {
+
+               case 'A': /* Add a rule */
+               case 'D': /* Delete a rule */
+               case 'C': /* Change counters */
+               case 'P': /* Define policy */
+               case 'I': /* Insert a rule */
+               case 'N': /* Make a user defined chain */
+               case 'E': /* Rename chain */
+               case 'X': /* Delete chain */
+                       /* We allow -N chainname -P policy */
+                       if (replace->command == 'N' && c == 'P') {
+                               replace->command = c;
+                               optind--; /* No table specified */
+                               goto handle_P;
+                       }
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not 
allowed");
+
+                       replace->command = c;
+                       replace->flags |= OPT_COMMAND;
+                       if (!(replace->flags & OPT_KERNELDATA))
+                               ebt_get_kernel_table(replace, 0);
+                       if (optarg && (optarg[0] == '-' || !strcmp(optarg, 
"!")))
+                               ebt_print_error2("No chain name specified");
+                       if (c == 'N') {
+                               if (ebt_get_chainnr(replace, optarg) != -1)
+                                       ebt_print_error2("Chain %s already 
exists", optarg);
+                               else if (ebt_find_target(optarg))
+                                       ebt_print_error2("Target with name %s 
exists", optarg);
+                               else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
+                                       ebt_print_error2("Chain name length 
can't exceed %d",
+                                                       EBT_CHAIN_MAXNAMELEN - 
1);
+                               else if (strchr(optarg, ' ') != NULL)
+                                       ebt_print_error2("Use of ' ' not 
allowed in chain names");
+                               ebt_new_chain(replace, optarg, EBT_ACCEPT);
+                               /* This is needed to get -N x -P y working */
+                               replace->selected_chain = 
ebt_get_chainnr(replace, optarg);
+                               break;
+                       } else if (c == 'X') {
+                               if (optind >= argc) {
+                                       replace->selected_chain = -1;
+                                       ebt_delete_chain(replace);
+                                       break;
+                               }
+
+                               if (optind < argc - 1)
+                                       ebt_print_error2("No extra options 
allowed with -X");
+
+                               if ((replace->selected_chain = 
ebt_get_chainnr(replace, argv[optind])) == -1)
+                                       ebt_print_error2("Chain '%s' doesn't 
exist", argv[optind]);
+                               ebt_delete_chain(replace);
+                               if (ebt_errormsg[0] != '\0')
+                                       return -1;
+                               optind++;
+                               break;
+                       }
+
+                       if ((replace->selected_chain = ebt_get_chainnr(replace, 
optarg)) == -1)
+                               ebt_print_error2("Chain '%s' doesn't exist", 
optarg);
+                       if (c == 'E') {
+                               if (optind >= argc)
+                                       ebt_print_error2("No new chain name 
specified");
+                               else if (optind < argc - 1)
+                                       ebt_print_error2("No extra options 
allowed with -E");
+                               else if (strlen(argv[optind]) >= 
EBT_CHAIN_MAXNAMELEN)
+                                       ebt_print_error2("Chain name length 
can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1);
+                               else if (ebt_get_chainnr(replace, argv[optind]) 
!= -1)
+                                       ebt_print_error2("Chain '%s' already 
exists", argv[optind]);
+                               else if (ebt_find_target(argv[optind]))
+                                       ebt_print_error2("Target with name '%s' 
exists", argv[optind]);
+                               else if (strchr(argv[optind], ' ') != NULL)
+                                       ebt_print_error2("Use of ' ' not 
allowed in chain names");
+                               ebt_rename_chain(replace, argv[optind]);
+                               optind++;
+                               break;
+                       } else if (c == 'D' && optind < argc && 
(argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) 
{
+                               if (optind != argc - 1)
+                                       ebt_print_error2("No extra options 
allowed with -D start_nr[:end_nr]");
+                               if (parse_rule_range(argv[optind], &rule_nr, 
&rule_nr_end))
+                                       ebt_print_error2("Problem with the 
specified rule number(s) '%s'", argv[optind]);
+                               optind++;
+                       } else if (c == 'C') {
+                               if ((chcounter = 
parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style)) == 
-1)
+                                       return -1;
+                       } else if (c == 'I') {
+                               if (optind >= argc || (argv[optind][0] == '-' 
&& (argv[optind][1] < '0' || argv[optind][1] > '9')))
+                                       rule_nr = 1;
+                               else {
+                                       rule_nr = strtol(argv[optind], &buffer, 
10);
+                                       if (*buffer != '\0')
+                                               ebt_print_error2("Problem with 
the specified rule number '%s'", argv[optind]);
+                                       optind++;
+                               }
+                       } else if (c == 'P') {
+handle_P:
+                               if (optind >= argc)
+                                       ebt_print_error2("No policy specified");
+                               for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+                                       if (!strcmp(argv[optind], 
ebt_standard_targets[i])) {
+                                               policy = -i -1;
+                                               if (policy == EBT_CONTINUE)
+                                                       ebt_print_error2("Wrong 
policy '%s'", argv[optind]);
+                                               break;
+                                       }
+                               if (i == NUM_STANDARD_TARGETS)
+                                       ebt_print_error2("Unknown policy '%s'", 
argv[optind]);
+                               optind++;
+                       }
+                       break;
+               case 'L': /* List */
+               case 'F': /* Flush */
+               case 'Z': /* Zero counters */
+                       if (c == 'Z') {
+                               if ((replace->flags & OPT_ZERO) || 
(replace->flags & OPT_COMMAND && replace->command != 'L'))
+print_zero:
+                                       ebt_print_error2("Command -Z only 
allowed together with command -L");
+                               replace->flags |= OPT_ZERO;
+                       } else {
+                               if (replace->flags & OPT_COMMAND)
+                                       ebt_print_error2("Multiple commands are 
not allowed");
+                               replace->command = c;
+                               replace->flags |= OPT_COMMAND;
+                               if (replace->flags & OPT_ZERO && c != 'L')
+                                       goto print_zero;
+                       }
+
+#ifdef SILENT_DAEMON
+                       if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("-L not supported in daemon 
mode");
+#endif
+
+                       if (!(replace->flags & OPT_KERNELDATA))
+                               ebt_get_kernel_table(replace, 0);
+                       i = -1;
+                       if (optind < argc && argv[optind][0] != '-') {
+                               if ((i = ebt_get_chainnr(replace, 
argv[optind])) == -1)
+                                       ebt_print_error2("Chain '%s' doesn't 
exist", argv[optind]);
+                               optind++;
+                       }
+                       if (i != -1) {
+                               if (c == 'Z')
+                                       zerochain = i;
+                               else
+                                       replace->selected_chain = i;
+                       }
+                       break;
+               case 'V': /* Version */
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not 
allowed");
+                       replace->command = 'V';
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2(PROGNAME" v"PROGVERSION" 
("PROGDATE")\n");
+                       PRINT_VERSION;
+                       exit(0);
+               case 'M': /* Modprobe */
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Please put the -M option 
earlier");
+                       free(ebt_modprobe);
+                       ebt_modprobe = optarg;
+                       break;
+               case 'h': /* Help */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("-h not supported in daemon 
mode");
+#endif
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not 
allowed");
+                       replace->command = 'h';
+
+                       /* All other arguments should be extension names */
+                       while (optind < argc) {
+                               struct ebt_u_match *m;
+                               struct ebt_u_watcher *w;
+
+                               if (!strcasecmp("list_extensions", 
argv[optind])) {
+                                       ebt_list_extensions();
+                                       exit(0);
+                               }
+                               if ((m = ebt_find_match(argv[optind])))
+                                       ebt_add_match(new_entry, m);
+                               else if ((w = ebt_find_watcher(argv[optind])))
+                                       ebt_add_watcher(new_entry, w);
+                               else {
+                                       if (!(t = 
ebt_find_target(argv[optind])))
+                                               ebt_print_error2("Extension 
'%s' not found", argv[optind]);
+                                       if (replace->flags & OPT_JUMP)
+                                               ebt_print_error2("Sorry, you 
can only see help for one target extension at a time");
+                                       replace->flags |= OPT_JUMP;
+                                       new_entry->t = (struct ebt_entry_target 
*)t;
+                               }
+                               optind++;
+                       }
+                       break;
+               case 't': /* Table */
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Please put the -t option 
first");
+                       ebt_check_option2(&(replace->flags), OPT_TABLE);
+                       if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+                               ebt_print_error2("Table name length cannot 
exceed %d characters", EBT_TABLE_MAXNAMELEN - 1);
+                       strcpy(replace->name, optarg);
+                       break;
+               case 'i': /* Input interface */
+               case 2  : /* Logical input interface */
+               case 'o': /* Output interface */
+               case 3  : /* Logical output interface */
+               case 'j': /* Target */
+               case 'p': /* Net family protocol */
+               case 's': /* Source mac */
+               case 'd': /* Destination mac */
+               case 'c': /* Set counters */
+                       if (!OPT_COMMANDS)
+                               ebt_print_error2("No command specified");
+                       if (replace->command != 'A' && replace->command != 'D' 
&& replace->command != 'I' && replace->command != 'C')
+                               ebt_print_error2("Command and option do not 
match");
+                       if (c == 'i') {
+                               ebt_check_option2(&(replace->flags), OPT_IN);
+                               if (replace->selected_chain > 2 && 
replace->selected_chain < NF_BR_BROUTING)
+                                       ebt_print_error2("Use -i only in INPUT, 
FORWARD, PREROUTING and BROUTING chains");
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_IIN;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+big_iface_length:
+                                       ebt_print_error2("Interface name length 
cannot exceed %d characters", IFNAMSIZ - 1);
+                               strcpy(new_entry->in, optarg);
+                               if (parse_iface(new_entry->in, "-i"))
+                                       return -1;
+                               break;
+                       } else if (c == 2) {
+                               ebt_check_option2(&(replace->flags), 
OPT_LOGICALIN);
+                               if (replace->selected_chain > 2 && 
replace->selected_chain < NF_BR_BROUTING)
+                                       ebt_print_error2("Use --logical-in only 
in INPUT, FORWARD, PREROUTING and BROUTING chains");
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_ILOGICALIN;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+                               strcpy(new_entry->logical_in, optarg);
+                               if (parse_iface(new_entry->logical_in, 
"--logical-in"))
+                                       return -1;
+                               break;
+                       } else if (c == 'o') {
+                               ebt_check_option2(&(replace->flags), OPT_OUT);
+                               if (replace->selected_chain < 2 || 
replace->selected_chain == NF_BR_BROUTING)
+                                       ebt_print_error2("Use -o only in 
OUTPUT, FORWARD and POSTROUTING chains");
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_IOUT;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+                               strcpy(new_entry->out, optarg);
+                               if (parse_iface(new_entry->out, "-o"))
+                                       return -1;
+                               break;
+                       } else if (c == 3) {
+                               ebt_check_option2(&(replace->flags), 
OPT_LOGICALOUT);
+                               if (replace->selected_chain < 2 || 
replace->selected_chain == NF_BR_BROUTING)
+                                       ebt_print_error2("Use --logical-out 
only in OUTPUT, FORWARD and POSTROUTING chains");
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_ILOGICALOUT;
+
+                               if (strlen(optarg) >= IFNAMSIZ)
+                                       goto big_iface_length;
+                               strcpy(new_entry->logical_out, optarg);
+                               if (parse_iface(new_entry->logical_out, 
"--logical-out"))
+                                       return -1;    
+                               break;
+                       } else if (c == 'j') {
+                               ebt_check_option2(&(replace->flags), OPT_JUMP);
+                               for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+                                       if (!strcmp(optarg, 
ebt_standard_targets[i])) {
+                                               t = 
ebt_find_target(EBT_STANDARD_TARGET);
+                                               ((struct ebt_standard_target *) 
t->t)->verdict = -i - 1;
+                                               break;
+                                       }
+                               if (-i - 1 == EBT_RETURN && 
replace->selected_chain < NF_BR_NUMHOOKS) {
+                                       ebt_print_error2("Return target only 
for user defined chains");
+                               } else if (i != NUM_STANDARD_TARGETS)
+                                       break;
+
+                               if ((i = ebt_get_chainnr(replace, optarg)) != 
-1) {
+                                       if (i < NF_BR_NUMHOOKS)
+                                               ebt_print_error2("Don't jump to 
a standard chain");
+                                       t = 
ebt_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 = ebt_find_target(optarg);
+                                       /* -j standard not allowed either */
+                                       if (!t || t == (struct ebt_u_target 
*)new_entry->t)
+                                               ebt_print_error2("Illegal 
target name '%s'", optarg);
+                                       new_entry->t = (struct ebt_entry_target 
*)t;
+                                       
ebt_find_target(EBT_STANDARD_TARGET)->used = 0;
+                                       t->used = 1;
+                               }
+                               break;
+                       } else if (c == 's') {
+                               ebt_check_option2(&(replace->flags), 
OPT_SOURCE);
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_ISOURCE;
+
+                               if (ebt_get_mac_and_mask(optarg, 
new_entry->sourcemac, new_entry->sourcemsk))
+                                       ebt_print_error2("Problem with 
specified source mac '%s'", optarg);
+                               new_entry->bitmask |= EBT_SOURCEMAC;
+                               break;
+                       } else if (c == 'd') {
+                               ebt_check_option2(&(replace->flags), OPT_DEST);
+                               if (ebt_check_inverse2(optarg))
+                                       new_entry->invflags |= EBT_IDEST;
+
+                               if (ebt_get_mac_and_mask(optarg, 
new_entry->destmac, new_entry->destmsk))
+                                       ebt_print_error2("Problem with 
specified destination mac '%s'", optarg);
+                               new_entry->bitmask |= EBT_DESTMAC;
+                               break;
+                       } else if (c == 'c') {
+                               ebt_check_option2(&(replace->flags), OPT_COUNT);
+                               if (ebt_check_inverse2(optarg))
+                                       ebt_print_error2("Unexpected '!' after 
-c");
+                               if (optind >= argc || optarg[0] == '-' || 
argv[optind][0] == '-')
+                                       ebt_print_error2("Option -c needs 2 
arguments");
+
+                               new_entry->cnt.pcnt = strtoull(optarg, &buffer, 
10);
+                               if (*buffer != '\0')
+                                       ebt_print_error2("Packet counter '%s' 
invalid", optarg);
+                               new_entry->cnt.bcnt = strtoull(argv[optind], 
&buffer, 10);
+                               if (*buffer != '\0')
+                                       ebt_print_error2("Packet counter '%s' 
invalid", argv[optind]);
+                               optind++;
+                               break;
+                       }
+                       ebt_check_option2(&(replace->flags), OPT_PROTOCOL);
+                       if (ebt_check_inverse2(optarg))
+                               new_entry->invflags |= EBT_IPROTO;
+
+                       new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+                       i = strtol(optarg, &buffer, 16);
+                       if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+                               ebt_print_error2("Problem with the specified 
protocol");
+                       if (*buffer != '\0') {
+                               struct ethertypeent *ent;
+
+                               if (!strcasecmp(optarg, "LENGTH")) {
+                                       new_entry->bitmask |= EBT_802_3;
+                                       break;
+                               }
+                               ent = getethertypebyname(optarg);
+                               if (!ent)
+                                       ebt_print_error2("Problem with the 
specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", 
optarg);
+                               new_entry->ethproto = ent->e_ethertype;
+                       } else
+                               new_entry->ethproto = i;
+
+                       if (new_entry->ethproto < 0x0600)
+                               ebt_print_error2("Sorry, protocols have values 
above or equal to 0x0600");
+                       break;
+               case 4  : /* Lc */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--Lc is not supported in 
daemon mode");
+#endif
+                       ebt_check_option2(&(replace->flags), LIST_C);
+                       if (replace->command != 'L')
+                               ebt_print_error("Use --Lc with -L");
+                       replace->flags |= LIST_C;
+                       break;
+               case 5  : /* Ln */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--Ln is not supported in 
daemon mode");
+#endif
+                       ebt_check_option2(&(replace->flags), LIST_N);
+                       if (replace->command != 'L')
+                               ebt_print_error2("Use --Ln with -L");
+                       if (replace->flags & LIST_X)
+                               ebt_print_error2("--Lx is not compatible with 
--Ln");
+                       replace->flags |= LIST_N;
+                       break;
+               case 6  : /* Lx */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--Lx is not supported in 
daemon mode");
+#endif
+                       ebt_check_option2(&(replace->flags), LIST_X);
+                       if (replace->command != 'L')
+                               ebt_print_error2("Use --Lx with -L");
+                       if (replace->flags & LIST_N)
+                               ebt_print_error2("--Lx is not compatible with 
--Ln");
+                       replace->flags |= LIST_X;
+                       break;
+               case 12 : /* Lmac2 */
+#ifdef SILENT_DAEMON
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error("--Lmac2 is not supported in 
daemon mode");
+#endif
+                       ebt_check_option2(&(replace->flags), LIST_MAC2);
+                       if (replace->command != 'L')
+                               ebt_print_error2("Use --Lmac2 with -L");
+                       replace->flags |= LIST_MAC2;
+                       break;
+               case 8 : /* atomic-commit */
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--atomic-commit is not 
supported in daemon mode");
+                       replace->command = c;
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not 
allowed");
+                       replace->flags |= OPT_COMMAND;
+                       if (!replace->filename)
+                               ebt_print_error2("No atomic file specified");
+                       /* Get the information from the file */
+                       ebt_get_table(replace, 0);
+                       /* We don't want the kernel giving us its counters,
+                        * they would overwrite the counters extracted from
+                        * the file */
+                       replace->num_counters = 0;
+                       /* Make sure the table will be written to the kernel */
+                       free(replace->filename);
+                       replace->filename = NULL;
+                       break;
+               case 7 : /* atomic-init */
+               case 10: /* atomic-save */
+               case 11: /* init-table */
+                       if (exec_style == EXEC_STYLE_DAEMON) {
+                               if (c == 7) {
+                                       ebt_print_error2("--atomic-init is not 
supported in daemon mode");
+                               } else if (c == 10)
+                                       ebt_print_error2("--atomic-save is not 
supported in daemon mode");
+                               ebt_print_error2("--init-table is not supported 
in daemon mode");
+                       }
+                       replace->command = c;
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("Multiple commands are not 
allowed");
+                       if (c != 11 && !replace->filename)
+                               ebt_print_error2("No atomic file specified");
+                       replace->flags |= OPT_COMMAND;
+                       {
+                               char *tmp = replace->filename;
+
+                               /* Get the kernel table */
+                               replace->filename = NULL;
+                               ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
+                               replace->filename = tmp;
+                       }
+                       break;
+               case 9 : /* atomic */
+                       if (exec_style == EXEC_STYLE_DAEMON)
+                               ebt_print_error2("--atomic is not supported in 
daemon mode");
+                       if (OPT_COMMANDS)
+                               ebt_print_error2("--atomic has to come before 
the command");
+                       /* A possible memory leak here, but this is not
+                        * executed in daemon mode */
+                       replace->filename = (char *)malloc(strlen(optarg) + 1);
+                       strcpy(replace->filename, optarg);
+                       break;
+               case 1 :
+                       if (!strcmp(optarg, "!"))
+                               ebt_check_inverse2(optarg);
+                       else
+                               ebt_print_error2("Bad argument : '%s'", optarg);
+                       /* ebt_check_inverse() did optind++ */
+                       optind--;
+                       continue;
+               default:
+                       /* Is it a target option? */
+                       t = (struct ebt_u_target *)new_entry->t;
+                       if ((t->parse(c - t->option_offset, argv, argc, 
new_entry, &t->flags, &t->t))) {
+                               if (ebt_errormsg[0] != '\0')
+                                       return -1;
+                               goto check_extension;
+                       }
+
+                       /* Is it a match_option? */
+                       for (m = ebt_matches; m; m = m->next)
+                               if (m->parse(c - m->option_offset, argv, argc, 
new_entry, &m->flags, &m->m))
+                                       break;
+
+                       if (m != NULL) {
+                               if (ebt_errormsg[0] != '\0')
+                                       return -1;
+                               if (m->used == 0) {
+                                       ebt_add_match(new_entry, m);
+                                       m->used = 1;
+                               }
+                               goto check_extension;
+                       }
+
+                       /* Is it a watcher option? */
+                       for (w = ebt_watchers; w; w = w->next)
+                               if (w->parse(c - w->option_offset, argv, argc, 
new_entry, &w->flags, &w->w))
+                                       break;
+
+                       if (w == NULL && c == '?')
+                               ebt_print_error2("Unknown argument: '%s'", 
argv[optind - 1], (char)optopt, (char)c);
+                       else if (w == NULL) {
+                               if (!strcmp(t->name, "standard"))
+                                       ebt_print_error2("Unknown argument: 
don't forget the -t option");
+                               else
+                                       ebt_print_error2("Target-specific 
option does not correspond with specified target");
+                       }
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       if (w->used == 0) {
+                               ebt_add_watcher(new_entry, w);
+                               w->used = 1;
+                       }
+check_extension:
+                       if (replace->command != 'A' && replace->command != 'I' 
&&
+                           replace->command != 'D' && replace->command != 'C')
+                               ebt_print_error2("Extensions only for -A, -I, 
-D and -C");
+               }
+               ebt_invert = 0;
+       }
+
+       /* Just in case we didn't catch an error */
+       if (ebt_errormsg[0] != '\0')
+               return -1;
+
+       if (!(table = ebt_find_table(replace->name)))
+               ebt_print_error2("Bad table name");
+
+       if (replace->command == 'h' && !(replace->flags & OPT_ZERO)) {
+               print_help();
+               if (exec_style == EXEC_STYLE_PRG)
+                       exit(0);
+       }
+
+       /* Do the final checks */
+       if (replace->command == 'A' || replace->command == 'I' ||
+          replace->command == 'D' || replace->command == 'C') {
+               /* This will put the hook_mask right for the chains */
+               ebt_check_for_loops(replace);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+               entries = ebt_to_chain(replace);
+               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);
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       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);
+                       if (ebt_errormsg[0] != '\0')
+                               return -1;
+                       w_l = w_l->next;
+               }
+               t->final_check(new_entry, t->t, replace->name,
+                  entries->hook_mask, 0);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       }
+       /* So, the extensions can work with the host endian.
+        * The kernel does not have to do this of course */
+       new_entry->ethproto = htons(new_entry->ethproto);
+
+       if (replace->command == 'P') {
+               if (replace->selected_chain < NF_BR_NUMHOOKS && policy == 
EBT_RETURN)
+                       ebt_print_error2("Policy RETURN only allowed for user 
defined chains");
+               ebt_change_policy(replace, policy);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       } else if (replace->command == 'L') {
+               list_rules();
+               if (!(replace->flags & OPT_ZERO) && exec_style == 
EXEC_STYLE_PRG)
+                       exit(0);
+       }
+       if (replace->flags & OPT_ZERO) {
+               replace->selected_chain = zerochain;
+               ebt_zero_counters(replace);
+       } else if (replace->command == 'F') {
+               ebt_flush_chains(replace);
+       } else if (replace->command == 'A' || replace->command == 'I') {
+               ebt_add_rule(replace, new_entry, rule_nr);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+               /* Makes undoing the add easier (jumps to delete_the_rule) */
+               if (rule_nr <= 0)
+                       rule_nr--;
+               rule_nr_end = rule_nr;
+
+               /* a jump to a udc requires checking for loops */
+               if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) &&
+               ((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) {
+                       /* FIXME: this can be done faster */
+                       ebt_check_for_loops(replace);
+                       if (ebt_errormsg[0] != '\0')
+                               goto delete_the_rule;
+               }
+
+               /* Do the final_check(), for all entries.
+                * This is needed when adding a rule that has a chain target */
+               i = -1;
+               while (++i != replace->num_chains) {
+                       struct ebt_u_entry *e;
+
+                       entries = replace->chains[i];
+                       if (!entries) {
+                               if (i < NF_BR_NUMHOOKS)
+                                       continue;
+                               else
+                                       ebt_print_bug("whoops\n");
+                       }
+                       e = entries->entries->next;
+                       while (e != entries->entries) {
+                               /* Userspace extensions use host endian */
+                               e->ethproto = ntohs(e->ethproto);
+                               ebt_do_final_checks(replace, e, entries);
+                               if (ebt_errormsg[0] != '\0')
+                                       goto delete_the_rule;
+                               e->ethproto = htons(e->ethproto);
+                               e = e->next;
+                       }
+               }
+               /* Don't reuse the added rule */
+               new_entry = NULL;
+       } else if (replace->command == 'D') {
+delete_the_rule:
+               ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       } else if (replace->command == 'C') {
+               ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, 
&(new_entry->cnt_surplus), chcounter);
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+       }
+       /* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+        * --init-table fall through */
+
+       if (ebt_errormsg[0] != '\0')
+               return -1;
+       if (table->check)
+               table->check(replace);
+
+       if (exec_style == EXEC_STYLE_PRG) {/* Implies ebt_errormsg[0] == '\0' */
+               ebt_deliver_table(replace);
+
+               if (replace->nentries)
+                       ebt_deliver_counters(replace);
+       }
+       return 0;
+}
diff --git a/tools/remus/imqebt/extensions/Makefile 
b/tools/remus/imqebt/extensions/Makefile
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/extensions/Makefile
@@ -0,0 +1,29 @@
+#! /usr/bin/make
+
+EXT_FUNC+=standard imq
+EXT_TABLES+=filter
+EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+EXT_LIBS+=$(foreach T,$(EXT_FUNC), extensions/libebt_$(T).so)
+EXT_LIBS+=$(foreach T,$(EXT_TABLES), extensions/libebtable_$(T).so)
+EXT_LIBSI+=$(foreach T,$(EXT_FUNC), -lebt_$(T))
+EXT_LIBSI+=$(foreach T,$(EXT_TABLES), -lebtable_$(T))
+
+extensions/ebt_%.so: extensions/ebt_%.o
+       $(CC) -shared -o $@ -lc $< -nostartfiles
+
+extensions/libebt_%.so: extensions/ebt_%.so
+       mv $< $@
+
+extensions/ebtable_%.so: extensions/ebtable_%.o
+       $(CC) -shared -o $@ -lc $< -nostartfiles
+
+extensions/libebtable_%.so: extensions/ebtable_%.so
+       mv $< $@
+
+extensions/ebt_%.o: extensions/ebt_%.c include/ebtables_u.h
+       $(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< 
-I$(KERNEL_INCLUDES)
+
+extensions/ebtable_%.o: extensions/ebtable_%.c
+       $(CC) $(CFLAGS) $(CFLAGS_SH_LIB) $(PROGSPECS) -c -o $@ $< 
-I$(KERNEL_INCLUDES)
+
diff --git a/tools/remus/imqebt/extensions/ebt_imq.c 
b/tools/remus/imqebt/extensions/ebt_imq.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/extensions/ebt_imq.c
@@ -0,0 +1,84 @@
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_imq.h>
+
+#define IMQ_TODEV '1'
+
+static struct option opts[] =
+{
+       { "todev"           , required_argument, 0, IMQ_TODEV },
+       { 0 }
+};
+
+static void help(void)
+{
+  printf(
+    "IMQ options:\n"
+    "  --todev <N>         enqueue to imq<N>, defaults to 0\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+  struct ebt_imq_info *imqinfo = (struct ebt_imq_info *)target->data;
+
+  imqinfo->todev = 0;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_target **target)
+{
+  struct ebt_imq_info *imqinfo = (struct ebt_imq_info *)(*target)->data;
+
+  switch(c) {
+  case IMQ_TODEV:
+    imqinfo->todev = atoi(optarg);
+  }
+
+  return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+  struct ebt_imq_info *imqinfo = (struct ebt_imq_info *)target->data;
+
+  printf("--todev %d", imqinfo->todev);
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+  struct ebt_imq_info *imqinfo1 = (struct ebt_imq_info *)t1->data;
+  struct ebt_imq_info *imqinfo2 = (struct ebt_imq_info *)t2->data;
+
+  if (imqinfo1->todev != imqinfo2->todev)
+    return 0;
+
+  return 1;
+}
+
+static struct ebt_u_target imq_target =
+{
+       .name           = "imq",
+       .size           = sizeof(struct ebt_imq_info),
+       .help           = help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .compare        = compare,
+       .extra_ops      = opts,
+};
+
+void _init(void)
+{
+       ebt_register_target(&imq_target);
+}
diff --git a/tools/remus/imqebt/extensions/ebt_standard.c 
b/tools/remus/imqebt/extensions/ebt_standard.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/extensions/ebt_standard.c
@@ -0,0 +1,90 @@
+/* ebt_standard
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@xxxxxxxxxx>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+
+static struct option opts[] =
+{
+       {0}
+};
+
+static void print_help(void)
+{
+       printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n"
+              "The target can also be a user defined chain.\n");
+}
+
+static void init(struct ebt_entry_target *t)
+{
+       ((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_target **target)
+{
+       return 0;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+       int verdict = ((struct ebt_standard_target *)target)->verdict;
+
+       if (verdict >= 0) {
+               struct ebt_u_entries *entries;
+
+               entries = entry->replace->chains[verdict + NF_BR_NUMHOOKS];
+               printf("%s", entries->name);
+               return;
+       }
+       if (verdict == EBT_CONTINUE)
+               printf("CONTINUE ");
+       else if (verdict == EBT_ACCEPT)
+               printf("ACCEPT ");
+       else if (verdict == EBT_DROP)
+               printf("DROP ");
+       else if (verdict == EBT_RETURN)
+               printf("RETURN ");
+       else
+               ebt_print_bug("Bad standard target");
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+       return ((struct ebt_standard_target *)t1)->verdict ==
+          ((struct ebt_standard_target *)t2)->verdict;
+}
+
+static struct ebt_u_target standard =
+{
+       .name           = "standard",
+       .size           = sizeof(struct ebt_standard_target) -
+                         sizeof(struct ebt_entry_target),
+       .help           = print_help,
+       .init           = init,
+       .parse          = parse,
+       .final_check    = final_check,
+       .print          = print,
+       .compare        = compare,
+       .extra_ops      = opts,
+};
+
+void _init(void)
+{
+       ebt_register_target(&standard);
+}
diff --git a/tools/remus/imqebt/extensions/ebtable_filter.c 
b/tools/remus/imqebt/extensions/ebtable_filter.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/extensions/ebtable_filter.c
@@ -0,0 +1,35 @@
+/* ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@xxxxxxxxxx>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+   (1 << NF_BR_LOCAL_OUT))
+
+static void print_help(const char **hn)
+{
+       int i;
+
+       printf("Supported chains for the filter table:\n");
+       for (i = 0; i < NF_BR_NUMHOOKS; i++)
+               if (FILTER_VALID_HOOKS & (1 << i))
+                       printf("%s ", hn[i]);
+       printf("\n");
+}
+
+static struct ebt_u_table table =
+{
+       .name           = "filter",
+       .help           = print_help,
+};
+
+void _init(void)
+{
+       ebt_register_table(&table);
+}
diff --git a/tools/remus/imqebt/getethertype.c 
b/tools/remus/imqebt/getethertype.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/getethertype.c
@@ -0,0 +1,162 @@
+/*
+* getethertype.c
+*
+* This file was part of the NYS Library.
+*
+** The NYS Library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library 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 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
+*/
+
+/********************************************************************
+* Description: Ethertype name service switch and the ethertypes 
+* database access functions
+* Author: Nick Fedchik <fnm@xxxxxxxxxx>
+* Checker: Bart De Schuymer <bdschuym@xxxxxxxxxx>
+* Origin: uClibc-0.9.16/libc/inet/getproto.c
+* Created at: Mon Nov 11 12:20:11 EET 2002
+********************************************************************/
+
+
+#include <ctype.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+
+#include "ethernetdb.h"
+
+#define        MAXALIASES      35
+
+static FILE *etherf = NULL;
+static char line[BUFSIZ + 1];
+static struct ethertypeent et_ent;
+static char *ethertype_aliases[MAXALIASES];
+static int ethertype_stayopen;
+
+void setethertypeent(int f)
+{
+       if (etherf == NULL)
+               etherf = fopen(_PATH_ETHERTYPES, "r");
+       else
+               rewind(etherf);
+       ethertype_stayopen |= f;
+}
+
+void endethertypeent(void)
+{
+       if (etherf) {
+               fclose(etherf);
+               etherf = NULL;
+       }
+       ethertype_stayopen = 0;
+}
+
+struct ethertypeent *getethertypeent(void)
+{
+       char *e;
+       char *endptr;
+       register char *cp, **q;
+
+       if (etherf == NULL
+           && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
+               return (NULL);
+       }
+
+again:
+       if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
+               return (NULL);
+       }
+       if (*e == '#')
+               goto again;
+       cp = strpbrk(e, "#\n");
+       if (cp == NULL)
+               goto again;
+       *cp = '\0';
+       et_ent.e_name = e;
+       cp = strpbrk(e, " \t");
+       if (cp == NULL)
+               goto again;
+       *cp++ = '\0';
+       while (*cp == ' ' || *cp == '\t')
+               cp++;
+       e = strpbrk(cp, " \t");
+       if (e != NULL)
+               *e++ = '\0';
+// Check point
+       et_ent.e_ethertype = strtol(cp, &endptr, 16);
+       if (*endptr != '\0'
+           || (et_ent.e_ethertype < ETH_ZLEN
+               || et_ent.e_ethertype > 0xFFFF))
+               goto again;     // Skip invalid etherproto type entry
+       q = et_ent.e_aliases = ethertype_aliases;
+       if (e != NULL) {
+               cp = e;
+               while (cp && *cp) {
+                       if (*cp == ' ' || *cp == '\t') {
+                               cp++;
+                               continue;
+                       }
+                       if (q < &ethertype_aliases[MAXALIASES - 1])
+                               *q++ = cp;
+                       cp = strpbrk(cp, " \t");
+                       if (cp != NULL)
+                               *cp++ = '\0';
+               }
+       }
+       *q = NULL;
+       return (&et_ent);
+}
+
+
+struct ethertypeent *getethertypebyname(const char *name)
+{
+       register struct ethertypeent *e;
+       register char **cp;
+
+       setethertypeent(ethertype_stayopen);
+       while ((e = getethertypeent()) != NULL) {
+               if (strcasecmp(e->e_name, name) == 0)
+                       break;
+               for (cp = e->e_aliases; *cp != 0; cp++)
+                       if (strcasecmp(*cp, name) == 0)
+                               goto found;
+       }
+found:
+       if (!ethertype_stayopen)
+               endethertypeent();
+       return (e);
+}
+
+struct ethertypeent *getethertypebynumber(int type)
+{
+       register struct ethertypeent *e;
+
+       setethertypeent(ethertype_stayopen);
+       while ((e = getethertypeent()) != NULL)
+               if (e->e_ethertype == type)
+                       break;
+       if (!ethertype_stayopen)
+               endethertypeent();
+       return (e);
+}
diff --git a/tools/remus/imqebt/include/ebtables_u.h 
b/tools/remus/imqebt/include/ebtables_u.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/ebtables_u.h
@@ -0,0 +1,379 @@
+/*
+ * $Id: ebtables.c,v 1.03 2002/01/19
+ *
+ * Copyright (C) 2001-2002 Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef EBTABLES_U_H
+#define EBTABLES_U_H
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter/x_tables.h>
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP           132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP           33
+#endif
+
+#define EXEC_STYLE_PRG         0
+#define EXEC_STYLE_DAEMON      1
+
+#ifndef EBT_MIN_ALIGN
+#define EBT_MIN_ALIGN (__alignof__(struct _xt_align))
+#endif
+#define EBT_ALIGN(s) (((s) + (EBT_MIN_ALIGN-1)) & ~(EBT_MIN_ALIGN-1))
+#define ERRORMSG_MAXLEN 128
+
+struct ebt_u_entries
+{
+       int policy;
+       unsigned int nentries;
+       /* counter offset for this chain */
+       unsigned int counter_offset;
+       /* used for udc */
+       unsigned int hook_mask;
+       char *kernel_start;
+       char name[EBT_CHAIN_MAXNAMELEN];
+       struct ebt_u_entry *entries;
+};
+
+struct ebt_cntchanges
+{
+       unsigned short type;
+       unsigned short change; /* determines incremental/decremental/change */
+       struct ebt_cntchanges *prev;
+       struct ebt_cntchanges *next;
+};
+
+#define EBT_ORI_MAX_CHAINS 10
+struct ebt_u_replace
+{
+       char name[EBT_TABLE_MAXNAMELEN];
+       unsigned int valid_hooks;
+       /* nr of rules in the table */
+       unsigned int nentries;
+       unsigned int num_chains;
+       unsigned int max_chains;
+       struct ebt_u_entries **chains;
+       /* nr of counters userspace expects back */
+       unsigned int num_counters;
+       /* where the kernel will put the old counters */
+       struct ebt_counter *counters;
+       /*
+        * can be used e.g. to know if a standard option
+        * has been specified twice
+        */
+       unsigned int flags;
+       /* we stick the specified command (e.g. -A) in here */
+       char command;
+       /*
+        * here we stick the chain to do our thing on (can be -1 if unspecified)
+        */
+       int selected_chain;
+       /* used for the atomic option */
+       char *filename;
+       /* tells what happened to the old rules (counter changes) */
+       struct ebt_cntchanges *cc;
+};
+
+struct ebt_u_table
+{
+       char name[EBT_TABLE_MAXNAMELEN];
+       void (*check)(struct ebt_u_replace *repl);
+       void (*help)(const char **);
+       struct ebt_u_table *next;
+};
+
+struct ebt_u_match_list
+{
+       struct ebt_u_match_list *next;
+       struct ebt_entry_match *m;
+};
+
+struct ebt_u_watcher_list
+{
+       struct ebt_u_watcher_list *next;
+       struct ebt_entry_watcher *w;
+};
+
+struct ebt_u_entry
+{
+       unsigned int bitmask;
+       unsigned int invflags;
+       uint16_t ethproto;
+       char in[IFNAMSIZ];
+       char logical_in[IFNAMSIZ];
+       char out[IFNAMSIZ];
+       char logical_out[IFNAMSIZ];
+       unsigned char sourcemac[ETH_ALEN];
+       unsigned char sourcemsk[ETH_ALEN];
+       unsigned char destmac[ETH_ALEN];
+       unsigned char destmsk[ETH_ALEN];
+       struct ebt_u_match_list *m_list;
+       struct ebt_u_watcher_list *w_list;
+       struct ebt_entry_target *t;
+       struct ebt_u_entry *prev;
+       struct ebt_u_entry *next;
+       struct ebt_counter cnt;
+       struct ebt_counter cnt_surplus; /* for increasing/decreasing a counter 
and for option 'C' */
+       struct ebt_cntchanges *cc;
+       /* the standard target needs this to know the name of a udc when
+        * printing out rules. */
+       struct ebt_u_replace *replace;
+};
+
+struct ebt_u_match
+{
+       char name[EBT_FUNCTION_MAXNAMELEN];
+       /* size of the real match data */
+       unsigned int size;
+       void (*help)(void);
+       void (*init)(struct ebt_entry_match *m);
+       int (*parse)(int c, char **argv, int argc,
+               const struct ebt_u_entry *entry, unsigned int *flags,
+               struct ebt_entry_match **match);
+       void (*final_check)(const struct ebt_u_entry *entry,
+          const struct ebt_entry_match *match,
+          const char *name, unsigned int hookmask, 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,
+          const struct ebt_entry_match *m2);
+       const struct option *extra_ops;
+       /*
+        * can be used e.g. to check for multiple occurance of the same option
+        */
+       unsigned int flags;
+       unsigned int option_offset;
+       struct ebt_entry_match *m;
+       /*
+        * if used == 1 we no longer have to add it to
+        * the match chain of the new entry
+        * be sure to put it back on 0 when finished
+        */
+       unsigned int used;
+       struct ebt_u_match *next;
+};
+
+struct ebt_u_watcher
+{
+       char name[EBT_FUNCTION_MAXNAMELEN];
+       unsigned int size;
+       void (*help)(void);
+       void (*init)(struct ebt_entry_watcher *w);
+       int (*parse)(int c, char **argv, int argc,
+          const struct ebt_u_entry *entry, unsigned int *flags,
+          struct ebt_entry_watcher **watcher);
+       void (*final_check)(const struct ebt_u_entry *entry,
+          const struct ebt_entry_watcher *watch, const char *name,
+          unsigned int hookmask, 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,
+          const struct ebt_entry_watcher *w2);
+       const struct option *extra_ops;
+       unsigned int flags;
+       unsigned int option_offset;
+       struct ebt_entry_watcher *w;
+       unsigned int used;
+       struct ebt_u_watcher *next;
+};
+
+struct ebt_u_target
+{
+       char name[EBT_FUNCTION_MAXNAMELEN];
+       unsigned int size;
+       void (*help)(void);
+       void (*init)(struct ebt_entry_target *t);
+       int (*parse)(int c, char **argv, int argc,
+          const struct ebt_u_entry *entry, unsigned int *flags,
+          struct ebt_entry_target **target);
+       void (*final_check)(const struct ebt_u_entry *entry,
+          const struct ebt_entry_target *target, const char *name,
+          unsigned int hookmask, 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,
+          const struct ebt_entry_target *t2);
+       const struct option *extra_ops;
+       unsigned int option_offset;
+       unsigned int flags;
+       struct ebt_entry_target *t;
+       unsigned int used;
+       struct ebt_u_target *next;
+};
+
+/* libebtc.c */
+
+extern struct ebt_u_table *ebt_tables;
+extern struct ebt_u_match *ebt_matches;
+extern struct ebt_u_watcher *ebt_watchers;
+extern struct ebt_u_target *ebt_targets;
+
+void ebt_register_table(struct ebt_u_table *);
+void ebt_register_match(struct ebt_u_match *);
+void ebt_register_watcher(struct ebt_u_watcher *);
+void ebt_register_target(struct ebt_u_target *t);
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init);
+struct ebt_u_target *ebt_find_target(const char *name);
+struct ebt_u_match *ebt_find_match(const char *name);
+struct ebt_u_watcher *ebt_find_watcher(const char *name);
+struct ebt_u_table *ebt_find_table(const char *name);
+int ebtables_insmod(const char *modname);
+void ebt_list_extensions(void);
+void ebt_initialize_entry(struct ebt_u_entry *e);
+void ebt_cleanup_replace(struct ebt_u_replace *replace);
+void ebt_reinit_extensions(void);
+void ebt_double_chains(struct ebt_u_replace *replace);
+void ebt_free_u_entry(struct ebt_u_entry *e);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+                                   const char* arg);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+                                   const char* arg);
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg);
+/**/
+void ebt_change_policy(struct ebt_u_replace *replace, int policy);
+void ebt_flush_chains(struct ebt_u_replace *replace);
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+                         struct ebt_u_entry *new_entry);
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry,
+                 int rule_nr);
+void ebt_delete_rule(struct ebt_u_replace *replace,
+                    struct ebt_u_entry *new_entry, int begin, int end);
+void ebt_zero_counters(struct ebt_u_replace *replace);
+void ebt_change_counters(struct ebt_u_replace *replace,
+                    struct ebt_u_entry *new_entry, int begin, int end,
+                    struct ebt_counter *cnt, int mask);
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int 
policy);
+void ebt_delete_chain(struct ebt_u_replace *replace);
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name);
+/**/
+void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
+                        struct ebt_u_entries *entries);
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err);
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+                             int print_err);
+void ebt_check_for_loops(struct ebt_u_replace *replace);
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m);
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w);
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *));
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *));
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *));
+void __ebt_print_bug(char *file, int line, char *format, ...);
+void __ebt_print_error(char *format, ...);
+
+/* communication.c */
+
+int ebt_get_table(struct ebt_u_replace *repl, int init);
+void ebt_deliver_counters(struct ebt_u_replace *repl);
+void ebt_deliver_table(struct ebt_u_replace *repl);
+
+/* useful_functions.c */
+
+extern int ebt_invert;
+void ebt_check_option(unsigned int *flags, unsigned int mask);
+#define ebt_check_inverse(arg) _ebt_check_inverse(arg, argc, argv)
+int _ebt_check_inverse(const char option[], int argc, char **argv);
+void ebt_print_mac(const unsigned char *mac);
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char 
*mask);
+int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char 
*mask);
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+char *ebt_mask_to_dotted(uint32_t mask);
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr, 
+                                                  struct in6_addr *msk);
+char *ebt_ip6_to_numeric(const struct in6_addr *addrp);
+
+
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_);
+
+struct ethertypeent *parseethertypebynumber(int type);
+
+#define ebt_to_chain(repl)                             \
+({struct ebt_u_entries *_ch = NULL;                    \
+if (repl->selected_chain != -1)                                \
+       _ch = repl->chains[repl->selected_chain];       \
+_ch;})
+#define ebt_print_bug(format, args...) \
+   __ebt_print_bug(__FILE__, __LINE__, format, ##args)
+#define ebt_print_error(format,args...) __ebt_print_error(format, ##args);
+#define ebt_print_error2(format, args...) do {__ebt_print_error(format, 
##args); \
+   return -1;} while (0)
+#define ebt_check_option2(flags,mask)  \
+({ebt_check_option(flags,mask);                \
+ if (ebt_errormsg[0] != '\0')          \
+       return -1;})
+#define ebt_check_inverse2(option)                                     \
+({int __ret = ebt_check_inverse(option);                               \
+if (ebt_errormsg[0] != '\0')                                           \
+       return -1;                                                      \
+if (!optarg) {                                                         \
+       __ebt_print_error("Option without (mandatory) argument");       \
+       return -1;                                                      \
+}                                                                      \
+__ret;})
+#define ebt_print_memory() do {printf("Ebtables: " __FILE__ \
+   " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);} while (0)
+
+/* used for keeping the rule counters right during rule adds or deletes */
+#define CNT_NORM       0
+#define CNT_DEL        1
+#define CNT_ADD        2
+#define CNT_CHANGE     3
+
+extern const char *ebt_hooknames[NF_BR_NUMHOOKS];
+extern const char *ebt_standard_targets[NUM_STANDARD_TARGETS];
+extern char ebt_errormsg[ERRORMSG_MAXLEN];
+extern char *ebt_modprobe;
+extern int ebt_silent;
+extern int ebt_printstyle_mac;
+
+/*
+ * Transforms a target string into the right integer,
+ * returns 0 on success.
+ */
+#define FILL_TARGET(_str, _pos) ({                            \
+       int _i, _ret = 0;                                     \
+       for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++)         \
+               if (!strcmp(_str, ebt_standard_targets[_i])) {\
+                       _pos = -_i - 1;                       \
+                       break;                                \
+               }                                             \
+       if (_i == NUM_STANDARD_TARGETS)                       \
+               _ret = 1;                                     \
+       _ret;                                                 \
+})
+
+/* Transforms the target value to an index into standard_targets[] */
+#define TARGET_INDEX(_value) (-_value - 1)
+/* Returns a target string corresponding to the value */
+#define TARGET_NAME(_value) (ebt_standard_targets[TARGET_INDEX(_value)])
+/* True if the hook mask denotes that the rule is in a base chain */
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+/* Clear the bit in the hook_mask that tells if the rule is on a base chain */
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+#define PRINT_VERSION printf(PROGNAME" v"PROGVERSION" ("PROGDATE")\n")
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
+#endif /* EBTABLES_U_H */
diff --git a/tools/remus/imqebt/include/ethernetdb.h 
b/tools/remus/imqebt/include/ethernetdb.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/ethernetdb.h
@@ -0,0 +1,58 @@
+/*
+* 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
+*/
+
+/* All data returned by the network data base library are supplied in
+   host order and returned in network order (suitable for use in
+   system calls).  */
+
+#ifndef        _ETHERNETDB_H
+#define        _ETHERNETDB_H   1
+
+#include <features.h>
+#include <netinet/in.h>
+#include <stdint.h>
+
+/* Absolute file name for network data base files.  */
+#ifndef        _PATH_ETHERTYPES
+#define        _PATH_ETHERTYPES        "/etc/ethertypes"
+#endif                         /* _PATH_ETHERTYPES */
+
+struct ethertypeent {
+       char *e_name;           /* Official ethernet type name.  */
+       char **e_aliases;       /* Alias list.  */
+       int e_ethertype;        /* Ethernet type number.  */
+};
+
+/* Open ethertype data base files and mark them as staying open even
+   after a later search if STAY_OPEN is non-zero.  */
+extern void setethertypeent(int __stay_open) __THROW;
+
+/* Close ethertype data base files and clear `stay open' flag.  */
+extern void endethertypeent(void) __THROW;
+
+/* Get next entry from ethertype data base file.  Open data base if
+   necessary.  */
+extern struct ethertypeent *getethertypeent(void) __THROW;
+
+/* Return entry from ethertype data base for network with NAME.  */
+extern struct ethertypeent *getethertypebyname(__const char *__name)
+    __THROW;
+
+/* Return entry from ethertype data base which number is PROTO.  */
+extern struct ethertypeent *getethertypebynumber(int __ethertype) __THROW;
+
+
+#endif                         /* ethernetdb.h */
diff --git a/tools/remus/imqebt/include/linux/if_ether.h 
b/tools/remus/imqebt/include/linux/if_ether.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/if_ether.h
@@ -0,0 +1,146 @@
+/*
+ * INET                An implementation of the TCP/IP protocol suite for the 
LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version:    @(#)if_ether.h  1.0.1a  02/08/94
+ *
+ * Author:     Fred N. van Kempen, <waltje@xxxxxxxxxxxxxxxxxxx>
+ *             Donald Becker, <becker@xxxxxxxxx>
+ *             Alan Cox, <alan@xxxxxxxxxxxxxxxxxxx>
+ *             Steve Whitehouse, <gw7rrm@xxxxxxxxxxxxxxxxxxx>
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+#include <linux/types.h>
+
+/*
+ *     IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
+ *     and FCS/CRC (frame check sequence).
+ */
+
+#define ETH_ALEN       6               /* Octets in one ethernet addr   */
+#define ETH_HLEN       14              /* Total octets in header.       */
+#define ETH_ZLEN       60              /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN   1500            /* Max. octets in payload        */
+#define ETH_FRAME_LEN  1514            /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN    4               /* Octets in the FCS             */
+
+/*
+ *     These are the defined Ethernet Protocol ID's.
+ */
+
+#define ETH_P_LOOP     0x0060          /* Ethernet Loopback packet     */
+#define ETH_P_PUP      0x0200          /* Xerox PUP packet             */
+#define ETH_P_PUPAT    0x0201          /* Xerox PUP Addr Trans packet  */
+#define ETH_P_IP       0x0800          /* Internet Protocol packet     */
+#define ETH_P_X25      0x0805          /* CCITT X.25                   */
+#define ETH_P_ARP      0x0806          /* Address Resolution packet    */
+#define        ETH_P_BPQ       0x08FF          /* G8BPQ AX.25 Ethernet Packet  
[ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_IEEEPUP  0x0a00          /* Xerox IEEE802.3 PUP packet */
+#define ETH_P_IEEEPUPAT        0x0a01          /* Xerox IEEE802.3 PUP Addr 
Trans packet */
+#define ETH_P_DEC       0x6000          /* DEC Assigned proto           */
+#define ETH_P_DNA_DL    0x6001          /* DEC DNA Dump/Load            */
+#define ETH_P_DNA_RC    0x6002          /* DEC DNA Remote Console       */
+#define ETH_P_DNA_RT    0x6003          /* DEC DNA Routing              */
+#define ETH_P_LAT       0x6004          /* DEC LAT                      */
+#define ETH_P_DIAG      0x6005          /* DEC Diagnostics              */
+#define ETH_P_CUST      0x6006          /* DEC Customer use             */
+#define ETH_P_SCA       0x6007          /* DEC Systems Comms Arch       */
+#define ETH_P_TEB      0x6558          /* Trans Ether Bridging         */
+#define ETH_P_RARP      0x8035         /* Reverse Addr Res packet      */
+#define ETH_P_ATALK    0x809B          /* Appletalk DDP                */
+#define ETH_P_AARP     0x80F3          /* Appletalk AARP               */
+#define ETH_P_8021Q    0x8100          /* 802.1Q VLAN Extended Header  */
+#define ETH_P_IPX      0x8137          /* IPX over DIX                 */
+#define ETH_P_IPV6     0x86DD          /* IPv6 over bluebook           */
+#define ETH_P_PAUSE    0x8808          /* IEEE Pause frames. See 802.3 31B */
+#define ETH_P_SLOW     0x8809          /* Slow Protocol. See 802.3ad 43B */
+#define ETH_P_WCCP     0x883E          /* Web-cache coordination protocol
+                                        * defined in 
draft-wilson-wrec-wccp-v2-00.txt */
+#define ETH_P_PPP_DISC 0x8863          /* PPPoE discovery messages     */
+#define ETH_P_PPP_SES  0x8864          /* PPPoE session messages       */
+#define ETH_P_MPLS_UC  0x8847          /* MPLS Unicast traffic         */
+#define ETH_P_MPLS_MC  0x8848          /* MPLS Multicast traffic       */
+#define ETH_P_ATMMPOA  0x884c          /* MultiProtocol Over ATM       */
+#define ETH_P_ATMFATE  0x8884          /* Frame-based ATM Transport
+                                        * over Ethernet
+                                        */
+#define ETH_P_PAE      0x888E          /* Port Access Entity (IEEE 802.1X) */
+#define ETH_P_AOE      0x88A2          /* ATA over Ethernet            */
+#define ETH_P_TIPC     0x88CA          /* TIPC                         */
+#define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
+#define ETH_P_EDSA     0xDADA          /* Ethertype DSA [ NOT AN OFFICIALLY 
REGISTERED ID ] */
+
+/*
+ *     Non DIX types. Won't clash for 1500 types.
+ */
+
+#define ETH_P_802_3    0x0001          /* Dummy type for 802.3 frames  */
+#define ETH_P_AX25     0x0002          /* Dummy protocol id for AX.25  */
+#define ETH_P_ALL      0x0003          /* Every packet (be careful!!!) */
+#define ETH_P_802_2    0x0004          /* 802.2 frames                 */
+#define ETH_P_SNAP     0x0005          /* Internal only                */
+#define ETH_P_DDCMP     0x0006          /* DEC DDCMP: Internal only     */
+#define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
+#define ETH_P_LOCALTALK 0x0009         /* Localtalk pseudo type        */
+#define ETH_P_CAN      0x000C          /* Controller Area Network      */
+#define ETH_P_PPPTALK  0x0010          /* Dummy type for Atalk over PPP*/
+#define ETH_P_TR_802_2 0x0011          /* 802.2 frames                 */
+#define ETH_P_MOBITEX  0x0015          /* Mobitex (kaz@xxxxxxxx)       */
+#define ETH_P_CONTROL  0x0016          /* Card specific control frames */
+#define ETH_P_IRDA     0x0017          /* Linux-IrDA                   */
+#define ETH_P_ECONET   0x0018          /* Acorn Econet                 */
+#define ETH_P_HDLC     0x0019          /* HDLC frames                  */
+#define ETH_P_ARCNET   0x001A          /* 1A for ArcNet :-)            */
+#define ETH_P_DSA      0x001B          /* Distributed Switch Arch.     */
+#define ETH_P_TRAILER  0x001C          /* Trailer switch tagging       */
+#define ETH_P_PHONET   0x00F5          /* Nokia Phonet frames          */
+
+/*
+ *     This is an Ethernet frame header.
+ */
+
+struct ethhdr {
+       unsigned char   h_dest[ETH_ALEN];       /* destination eth addr */
+       unsigned char   h_source[ETH_ALEN];     /* source ether addr    */
+       __be16          h_proto;                /* packet type ID field */
+} __attribute__((packed));
+
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
+{
+       return (struct ethhdr *)skb_mac_header(skb);
+}
+
+int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr);
+
+#ifdef CONFIG_SYSCTL
+extern struct ctl_table ether_table[];
+#endif
+
+extern ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len);
+
+/*
+ *     Display a 6 byte device address (MAC) in a readable format.
+ */
+extern char *print_mac(char *buf, const unsigned char *addr);
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_BUF_SIZE   18
+#define DECLARE_MAC_BUF(var) char var[MAC_BUF_SIZE] __maybe_unused
+
+#endif
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/tools/remus/imqebt/include/linux/netfilter_bridge.h 
b/tools/remus/imqebt/include/linux/netfilter_bridge.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/netfilter_bridge.h
@@ -0,0 +1,91 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter.
+ */
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING      0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN         1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD          2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT                3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING     4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING         5
+#define NF_BR_NUMHOOKS         6
+
+#ifdef __KERNEL__
+#include <linux/netfilter.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
+
+enum nf_br_hook_priorities {
+       NF_BR_PRI_FIRST = INT_MIN,
+       NF_BR_PRI_NAT_DST_BRIDGED = -300,
+       NF_BR_PRI_FILTER_BRIDGED = -200,
+       NF_BR_PRI_BRNF = 0,
+       NF_BR_PRI_NAT_DST_OTHER = 100,
+       NF_BR_PRI_FILTER_OTHER = 200,
+       NF_BR_PRI_NAT_SRC = 300,
+       NF_BR_PRI_LAST = INT_MAX,
+};
+
+#ifdef CONFIG_BRIDGE_NETFILTER
+
+#define BRNF_PKT_TYPE                  0x01
+#define BRNF_BRIDGED_DNAT              0x02
+#define BRNF_DONT_TAKE_PARENT          0x04
+#define BRNF_BRIDGED                   0x08
+#define BRNF_NF_BRIDGE_PREROUTING      0x10
+
+
+/* Only used in br_forward.c */
+extern int nf_bridge_copy_header(struct sk_buff *skb);
+static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb)
+{
+       if (skb->nf_bridge &&
+           skb->nf_bridge->mask & (BRNF_BRIDGED | BRNF_BRIDGED_DNAT))
+               return nf_bridge_copy_header(skb);
+       return 0;
+}
+
+static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff 
*skb)
+{
+       switch (skb->protocol) {
+       case __cpu_to_be16(ETH_P_8021Q):
+               return VLAN_HLEN;
+       case __cpu_to_be16(ETH_P_PPP_SES):
+               return PPPOE_SES_HLEN;
+       default:
+               return 0;
+       }
+}
+
+/* This is called by the IP fragmenting code and it ensures there is
+ * enough room for the encapsulating header (if there is one). */
+static inline unsigned int nf_bridge_pad(const struct sk_buff *skb)
+{
+       if (skb->nf_bridge)
+               return nf_bridge_encap_header_len(skb);
+       return 0;
+}
+
+struct bridge_skb_cb {
+       union {
+               __be32 ipv4;
+       } daddr;
+};
+
+#else
+#define nf_bridge_maybe_copy_header(skb)       (0)
+#define nf_bridge_pad(skb)                     (0)
+#endif /* CONFIG_BRIDGE_NETFILTER */
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/tools/remus/imqebt/include/linux/netfilter_bridge/ebt_imq.h 
b/tools/remus/imqebt/include/linux/netfilter_bridge/ebt_imq.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/netfilter_bridge/ebt_imq.h
@@ -0,0 +1,8 @@
+#ifndef __LINUX_BRIDGE_EBT_IMQ_H
+#define __LINUX_BRIDGE_EBT_IMQ_H
+
+struct ebt_imq_info
+{
+  unsigned int todev;
+};
+#endif
diff --git a/tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h 
b/tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,276 @@
+/*
+ *  ebtables
+ *
+ *     Authors:
+ *     Bart De Schuymer                <bdschuym@xxxxxxxxxx>
+ *
+ *  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
+ */
+
+/* Local copy of the kernel file, needed for Sparc64 support */
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/if_ether.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+
+/* 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
+/* ebtables target modules store the verdict inside an int. We can
+ * reclaim a part of this int for backwards compatible extensions.
+ * The 4 lsb are more than enough to store the verdict. */
+#define EBT_VERDICT_BITS 0x0000000F
+
+struct ebt_counter
+{
+       uint64_t pcnt;
+       uint64_t bcnt;
+};
+
+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 */
+#ifdef KERNEL_64_USERSPACE_32
+       uint64_t hook_entry[NF_BR_NUMHOOKS];
+#else
+       struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+#endif
+       /* nr of counters userspace expects back */
+       unsigned int num_counters;
+       /* where the kernel will put the old counters */
+#ifdef KERNEL_64_USERSPACE_32
+       uint64_t counters;
+       uint64_t entries;
+#else
+       struct ebt_counter *counters;
+       char *entries;
+#endif
+};
+
+struct ebt_entries {
+       /* this field is always set to zero
+        * See EBT_ENTRY_OR_ENTRIES.
+        * Must be same size as ebt_entry.bitmask */
+       unsigned int distinguisher;
+       /* the chain name */
+       char name[EBT_CHAIN_MAXNAMELEN];
+       /* counter offset for this chain */
+       unsigned int counter_offset;
+       /* one standard (accept, drop, return) per hook */
+       int policy;
+       /* nr. of entries */
+       unsigned int nentries;
+       /* entry list */
+       char data[0] __attribute__ ((aligned (__alignof__(struct 
ebt_replace))));
+};
+
+/* 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;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct 
ebt_replace))));
+};
+
+struct ebt_entry_watcher
+{
+       union {
+               char name[EBT_FUNCTION_MAXNAMELEN];
+               struct ebt_watcher *watcher;
+       } u;
+       /* size of data */
+       unsigned int watcher_size;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct 
ebt_replace))));
+};
+
+struct ebt_entry_target
+{
+       union {
+               char name[EBT_FUNCTION_MAXNAMELEN];
+               struct ebt_target *target;
+       } u;
+       /* size of data */
+       unsigned int target_size;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+       unsigned char data[0] __attribute__ ((aligned (__alignof__(struct 
ebt_replace))));
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target
+{
+       struct ebt_entry_target target;
+       int verdict;
+#ifdef KERNEL_64_USERSPACE_32
+       unsigned int pad;
+#endif
+};
+
+/* one entry */
+struct ebt_entry {
+       /* this needs to be the first field */
+       unsigned int bitmask;
+       unsigned int invflags;
+       uint16_t ethproto;
+       /* the physical in-dev */
+       char in[IFNAMSIZ];
+       /* the logical in-dev */
+       char logical_in[IFNAMSIZ];
+       /* the physical out-dev */
+       char out[IFNAMSIZ];
+       /* the logical out-dev */
+       char logical_out[IFNAMSIZ];
+       unsigned char sourcemac[ETH_ALEN];
+       unsigned char sourcemsk[ETH_ALEN];
+       unsigned char destmac[ETH_ALEN];
+       unsigned char destmsk[ETH_ALEN];
+       /* sizeof ebt_entry + matches */
+       unsigned int watchers_offset;
+       /* sizeof ebt_entry + matches + watchers */
+       unsigned int target_offset;
+       /* sizeof ebt_entry + matches + watchers + target */
+       unsigned int next_offset;
+       unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct 
ebt_replace))));
+};
+
+/* {g,s}etsockopt numbers */
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+/* blatently stolen from ip_tables.h
+ * fn returns 0 to continue iteration */
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry_match *__match;                    \
+                                                           \
+       for (__i = sizeof(struct ebt_entry);                \
+            __i < (e)->watchers_offset;                    \
+            __i += __match->match_size +                   \
+            sizeof(struct ebt_entry_match)) {              \
+               __match = (void *)(e) + __i;                \
+                                                           \
+               __ret = fn(__match , ## args);              \
+               if (__ret != 0)                             \
+                       break;                              \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (e)->watchers_offset)            \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry_watcher *__watcher;                \
+                                                           \
+       for (__i = e->watchers_offset;                      \
+            __i < (e)->target_offset;                      \
+            __i += __watcher->watcher_size +               \
+            sizeof(struct ebt_entry_watcher)) {            \
+               __watcher = (void *)(e) + __i;              \
+                                                           \
+               __ret = fn(__watcher , ## args);            \
+               if (__ret != 0)                             \
+                       break;                              \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (e)->target_offset)              \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+       unsigned int __i;                                   \
+       int __ret = 0;                                      \
+       struct ebt_entry *__entry;                          \
+                                                           \
+       for (__i = 0; __i < (size);) {                      \
+               __entry = (void *)(entries) + __i;          \
+               __ret = fn(__entry , ## args);              \
+               if (__ret != 0)                             \
+                       break;                              \
+               if (__entry->bitmask != 0)                  \
+                       __i += __entry->next_offset;        \
+               else                                        \
+                       __i += sizeof(struct ebt_entries);  \
+       }                                                   \
+       if (__ret == 0) {                                   \
+               if (__i != (size))                          \
+                       __ret = -EINVAL;                    \
+       }                                                   \
+       __ret;                                              \
+})
+
+#endif
diff --git a/tools/remus/imqebt/include/linux/types.h 
b/tools/remus/imqebt/include/linux/types.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/types.h
@@ -0,0 +1,209 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <asm/types.h>
+
+#ifndef __ASSEMBLY__
+#ifdef __KERNEL__
+
+#define DECLARE_BITMAP(name,bits) \
+       unsigned long name[BITS_TO_LONGS(bits)]
+
+#endif
+
+#include <linux/posix_types.h>
+
+#ifdef __KERNEL__
+
+typedef __u32 __kernel_dev_t;
+
+typedef __kernel_fd_set                fd_set;
+typedef __kernel_dev_t         dev_t;
+typedef __kernel_ino_t         ino_t;
+typedef __kernel_mode_t                mode_t;
+typedef __kernel_nlink_t       nlink_t;
+typedef __kernel_off_t         off_t;
+typedef __kernel_pid_t         pid_t;
+typedef __kernel_daddr_t       daddr_t;
+typedef __kernel_key_t         key_t;
+typedef __kernel_suseconds_t   suseconds_t;
+typedef __kernel_timer_t       timer_t;
+typedef __kernel_clockid_t     clockid_t;
+typedef __kernel_mqd_t         mqd_t;
+
+typedef _Bool                  bool;
+
+typedef __kernel_uid32_t       uid_t;
+typedef __kernel_gid32_t       gid_t;
+typedef __kernel_uid16_t        uid16_t;
+typedef __kernel_gid16_t        gid16_t;
+
+typedef unsigned long          uintptr_t;
+
+#ifdef CONFIG_UID16
+/* This is defined by include/asm-{arch}/posix_types.h */
+typedef __kernel_old_uid_t     old_uid_t;
+typedef __kernel_old_gid_t     old_gid_t;
+#endif /* CONFIG_UID16 */
+
+#if defined(__GNUC__)
+typedef __kernel_loff_t                loff_t;
+#endif
+
+/*
+ * The following typedefs are also protected by individual ifdefs for
+ * historical reasons:
+ */
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef __kernel_size_t                size_t;
+#endif
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef __kernel_ssize_t       ssize_t;
+#endif
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef __kernel_ptrdiff_t     ptrdiff_t;
+#endif
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef __kernel_time_t                time_t;
+#endif
+
+#ifndef _CLOCK_T
+#define _CLOCK_T
+typedef __kernel_clock_t       clock_t;
+#endif
+
+#ifndef _CADDR_T
+#define _CADDR_T
+typedef __kernel_caddr_t       caddr_t;
+#endif
+
+/* bsd */
+typedef unsigned char          u_char;
+typedef unsigned short         u_short;
+typedef unsigned int           u_int;
+typedef unsigned long          u_long;
+
+/* sysv */
+typedef unsigned char          unchar;
+typedef unsigned short         ushort;
+typedef unsigned int           uint;
+typedef unsigned long          ulong;
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+typedef                __u8            u_int8_t;
+typedef                __s8            int8_t;
+typedef                __u16           u_int16_t;
+typedef                __s16           int16_t;
+typedef                __u32           u_int32_t;
+typedef                __s32           int32_t;
+
+#endif /* !(__BIT_TYPES_DEFINED__) */
+
+typedef                __u8            uint8_t;
+typedef                __u16           uint16_t;
+typedef                __u32           uint32_t;
+
+#if defined(__GNUC__)
+typedef                __u64           uint64_t;
+typedef                __u64           u_int64_t;
+typedef                __s64           int64_t;
+#endif
+
+/* this is a special 64bit data type that is 8-byte aligned */
+#define aligned_u64 __u64 __attribute__((aligned(8)))
+#define aligned_be64 __be64 __attribute__((aligned(8)))
+#define aligned_le64 __le64 __attribute__((aligned(8)))
+
+/**
+ * The type used for indexing onto a disc or disc partition.
+ *
+ * Linux always considers sectors to be 512 bytes long independently
+ * of the devices real block size.
+ *
+ * blkcnt_t is the type of the inode's block count.
+ */
+#ifdef CONFIG_LBD
+typedef u64 sector_t;
+typedef u64 blkcnt_t;
+#else
+typedef unsigned long sector_t;
+typedef unsigned long blkcnt_t;
+#endif
+
+/*
+ * The type of an index into the pagecache.  Use a #define so asm/types.h
+ * can override it.
+ */
+#ifndef pgoff_t
+#define pgoff_t unsigned long
+#endif
+
+#endif /* __KERNEL__ */
+
+/*
+ * Below are truly Linux-specific types that should never collide with
+ * any application/library that wants linux/types.h.
+ */
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+#ifdef __KERNEL__
+typedef unsigned __bitwise__ gfp_t;
+typedef unsigned __bitwise__ fmode_t;
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+typedef u64 phys_addr_t;
+#else
+typedef u32 phys_addr_t;
+#endif
+
+typedef phys_addr_t resource_size_t;
+
+typedef struct {
+       volatile int counter;
+} atomic_t;
+
+#ifdef CONFIG_64BIT
+typedef struct {
+       volatile long counter;
+} atomic64_t;
+#endif
+
+struct ustat {
+       __kernel_daddr_t        f_tfree;
+       __kernel_ino_t          f_tinode;
+       char                    f_fname[6];
+       char                    f_fpack[6];
+};
+
+#endif /* __KERNEL__ */
+#endif /*  __ASSEMBLY__ */
+#endif /* _LINUX_TYPES_H */
diff --git a/tools/remus/imqebt/libebtc.c b/tools/remus/imqebt/libebtc.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/libebtc.c
@@ -0,0 +1,1280 @@
+/*
+ * libebtc.c, January 2004
+ *
+ * Contains the functions with which to make a table in userspace.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace);
+static int iterate_entries(struct ebt_u_replace *replace, int type);
+
+/* The standard names */
+const char *ebt_hooknames[NF_BR_NUMHOOKS] =
+{
+       [NF_BR_PRE_ROUTING]"PREROUTING",
+       [NF_BR_LOCAL_IN]"INPUT",
+       [NF_BR_FORWARD]"FORWARD",
+       [NF_BR_LOCAL_OUT]"OUTPUT",
+       [NF_BR_POST_ROUTING]"POSTROUTING",
+       [NF_BR_BROUTING]"BROUTING"
+};
+
+/* The four target names */
+const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
+{
+       "ACCEPT",
+       "DROP",
+       "CONTINUE",
+       "RETURN",
+};
+
+/* The lists of supported tables, matches, watchers and targets */
+struct ebt_u_table *ebt_tables;
+struct ebt_u_match *ebt_matches;
+struct ebt_u_watcher *ebt_watchers;
+struct ebt_u_target *ebt_targets;
+
+/* Find the right structure belonging to a name */
+struct ebt_u_target *ebt_find_target(const char *name)
+{
+       struct ebt_u_target *t = ebt_targets;
+
+       while (t && strcmp(t->name, name))
+               t = t->next;
+       return t;
+}
+
+struct ebt_u_match *ebt_find_match(const char *name)
+{
+       struct ebt_u_match *m = ebt_matches;
+
+       while (m && strcmp(m->name, name))
+               m = m->next;
+       return m;
+}
+
+struct ebt_u_watcher *ebt_find_watcher(const char *name)
+{
+       struct ebt_u_watcher *w = ebt_watchers;
+
+       while (w && strcmp(w->name, name))
+               w = w->next;
+       return w;
+}
+
+struct ebt_u_table *ebt_find_table(const char *name)
+{
+       struct ebt_u_table *t = ebt_tables;
+
+       while (t && strcmp(t->name, name))
+               t = t->next;
+       return t;
+}
+
+/* Prints all registered extensions */
+void ebt_list_extensions()
+{
+       struct ebt_u_table *tbl = ebt_tables;
+        struct ebt_u_target *t = ebt_targets;
+        struct ebt_u_match *m = ebt_matches;
+        struct ebt_u_watcher *w = ebt_watchers;
+
+       PRINT_VERSION;
+       printf("Loaded userspace extensions:\n\nLoaded tables:\n");
+        while (tbl) {
+               printf("%s\n", tbl->name);
+                tbl = tbl->next;
+       }
+       printf("\nLoaded targets:\n");
+        while (t) {
+               printf("%s\n", t->name);
+                t = t->next;
+       }
+       printf("\nLoaded matches:\n");
+        while (m) {
+               printf("%s\n", m->name);
+                m = m->next;
+       }
+       printf("\nLoaded watchers:\n");
+        while (w) {
+               printf("%s\n", w->name);
+                w = w->next;
+       }
+}
+
+/* Get the table from the kernel or from a binary file
+ * init: 1 = ask the kernel for the initial contents of a table, i.e. the
+ *           way it looks when the table is insmod'ed
+ *       0 = get the current data in the table */
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
+{
+       if (!ebt_find_table(replace->name)) {
+               ebt_print_error("Bad table name '%s'", replace->name);
+               return -1;
+       }
+       /* Get the kernel's information */
+       if (ebt_get_table(replace, init)) {
+               if (ebt_errormsg[0] != '\0')
+                       return -1;
+               ebtables_insmod("ebtables");
+               if (ebt_get_table(replace, init)) {
+                       ebt_print_error("The kernel doesn't support the 
ebtables '%s' table", replace->name);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/* Put sane values into a new entry */
+void ebt_initialize_entry(struct ebt_u_entry *e)
+{
+       e->bitmask = EBT_NOPROTO;
+       e->invflags = 0;
+       e->ethproto = 0;
+       strcpy(e->in, "");
+       strcpy(e->out, "");
+       strcpy(e->logical_in, "");
+       strcpy(e->logical_out, "");
+       e->m_list = NULL;
+       e->w_list = NULL;
+       e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET);
+       ebt_find_target(EBT_STANDARD_TARGET)->used = 1;
+       e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 
0;
+
+       if (!e->t)
+               ebt_print_bug("Couldn't load standard target");
+       ((struct ebt_standard_target *)((struct ebt_u_target 
*)e->t)->t)->verdict = EBT_CONTINUE;
+}
+
+/* Free up the memory of the table held in userspace, *replace can be reused */
+void ebt_cleanup_replace(struct ebt_u_replace *replace)
+{
+       int i;
+       struct ebt_u_entries *entries;
+       struct ebt_cntchanges *cc1, *cc2;
+       struct ebt_u_entry *u_e1, *u_e2;
+
+       replace->name[0] = '\0';
+       replace->valid_hooks = 0;
+       replace->nentries = 0;
+       replace->num_counters = 0;
+       replace->flags = 0;
+       replace->command = 0;
+       replace->selected_chain = -1;
+       free(replace->filename);
+       replace->filename = NULL;
+       free(replace->counters);
+       replace->counters = NULL;
+
+       for (i = 0; i < replace->num_chains; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               u_e1 = entries->entries->next;
+               while (u_e1 != entries->entries) {
+                       ebt_free_u_entry(u_e1);
+                       u_e2 = u_e1->next;
+                       free(u_e1);
+                       u_e1 = u_e2;
+               }
+               free(entries->entries);
+               free(entries);
+               replace->chains[i] = NULL;
+       }
+       cc1 = replace->cc->next;
+       while (cc1 != replace->cc) {
+               cc2 = cc1->next;
+               free(cc1);
+               cc1 = cc2;
+       }
+       replace->cc->next = replace->cc->prev = replace->cc;
+}
+
+/* Should be called, e.g., between 2 rule adds */
+void ebt_reinit_extensions()
+{
+       struct ebt_u_match *m;
+       struct ebt_u_watcher *w;
+       struct ebt_u_target *t;
+       int size;
+
+       /* The init functions should determine by themselves whether they are
+        * called for the first time or not (when necessary). */
+       for (m = ebt_matches; m; m = m->next) {
+               if (m->used) {
+                       size = EBT_ALIGN(m->size) + sizeof(struct 
ebt_entry_match);
+                       m->m = (struct ebt_entry_match *)malloc(size);
+                       if (!m->m)
+                               ebt_print_memory();
+                       strcpy(m->m->u.name, m->name);
+                       m->m->match_size = EBT_ALIGN(m->size);
+                       m->used = 0;
+               }
+               m->flags = 0; /* An error can occur before used is set, while 
flags is changed. */
+               m->init(m->m);
+       }
+       for (w = ebt_watchers; w; w = w->next) {
+               if (w->used) {
+                       size = EBT_ALIGN(w->size) + sizeof(struct 
ebt_entry_watcher);
+                       w->w = (struct ebt_entry_watcher *)malloc(size);
+                       if (!w->w)
+                               ebt_print_memory();
+                       strcpy(w->w->u.name, w->name);
+                       w->w->watcher_size = EBT_ALIGN(w->size);
+                       w->used = 0;
+               }
+               w->flags = 0;
+               w->init(w->w);
+       }
+       for (t = ebt_targets; t; t = t->next) {
+               if (t->used) {
+                       size = EBT_ALIGN(t->size) + sizeof(struct 
ebt_entry_target);
+                       t->t = (struct ebt_entry_target *)malloc(size);
+                       if (!t->t)
+                               ebt_print_memory();
+                       strcpy(t->t->u.name, t->name);
+                       t->t->target_size = EBT_ALIGN(t->size);
+                       t->used = 0;
+               }
+               t->flags = 0;
+               t->init(t->t);
+       }
+}
+
+/* This doesn't free e, because the calling function might need e->next */
+void ebt_free_u_entry(struct ebt_u_entry *e)
+{
+       struct ebt_u_match_list *m_l, *m_l2;
+       struct ebt_u_watcher_list *w_l, *w_l2;
+
+       m_l = e->m_list;
+       while (m_l) {
+               m_l2 = m_l->next;
+               free(m_l->m);
+               free(m_l);
+               m_l = m_l2;
+       }
+       w_l = e->w_list;
+       while (w_l) {
+               w_l2 = w_l->next;
+               free(w_l->w);
+               free(w_l);
+               w_l = w_l2;
+       }
+       free(e->t);
+}
+
+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) {
+               if (read(procfile, ret, 1024) == -1)
+                       goto fail;
+               /* The kernel adds a '\n' */
+               ret[1023] = '\n';
+               *strchr(ret, '\n') = '\0';
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+char *ebt_modprobe;
+/* Try to load the kernel module, analogous to ip_tables.c */
+int ebtables_insmod(const char *modname)
+{
+       char *buf = NULL;
+       char *argv[3];
+
+       /* If they don't explicitly set it, read out of /proc */
+       if (!ebt_modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               ebt_modprobe = buf; /* Keep the value for possible later use */
+       }
+
+       switch (fork()) {
+       case 0:
+               argv[0] = (char *)ebt_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);
+       }
+
+       return 0;
+}
+
+/* Parse the chain name and return a pointer to the chain base.
+ * Returns NULL on failure. */
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, 
const char* arg)
+{
+       int i;
+       struct ebt_u_entries *chain;
+
+       for (i = 0; i < replace->num_chains; i++) {
+               if (!(chain = replace->chains[i]))
+                       continue;
+               if (!strcmp(arg, chain->name))
+                       return chain;
+       }
+       return NULL;
+}
+
+/* Parse the chain name and return the corresponding chain nr
+ * returns -1 on failure */
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg)
+{
+       int i;
+
+       for (i = 0; i < replace->num_chains; i++) {
+               if (!replace->chains[i])
+                       continue;
+               if (!strcmp(arg, replace->chains[i]->name))
+                       return i;
+       }
+       return -1;
+}
+
+     /*
+************
+************
+**COMMANDS**
+************
+************
+     */
+
+/* Change the policy of selected_chain.
+ * Handing a bad policy to this function is a bug. */
+void ebt_change_policy(struct ebt_u_replace *replace, int policy)
+{
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE)
+               ebt_print_bug("Wrong policy: %d", policy);
+       entries->policy = policy;
+}
+
+void ebt_delete_cc(struct ebt_cntchanges *cc)
+{
+       if (cc->type == CNT_ADD) {
+               cc->prev->next = cc->next;
+               cc->next->prev = cc->prev;
+               free(cc);
+       }
+       cc->type = CNT_DEL;
+}
+
+void ebt_empty_chain(struct ebt_u_entries *entries)
+{
+       struct ebt_u_entry *u_e = entries->entries->next, *tmp;
+       while (u_e != entries->entries) {
+               ebt_delete_cc(u_e->cc);
+               ebt_free_u_entry(u_e);
+               tmp = u_e->next;
+               free(u_e);
+               u_e = tmp;
+       }
+       entries->entries->next = entries->entries->prev = entries->entries;
+       entries->nentries = 0;
+}
+
+/* Flush one chain or the complete table
+ * If selected_chain == -1 then flush the complete table */
+void ebt_flush_chains(struct ebt_u_replace *replace)
+{
+       int i, numdel;
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       /* Flush whole table */
+       if (!entries) {
+               if (replace->nentries == 0)
+                       return;
+               replace->nentries = 0;
+
+               /* Free everything and zero (n)entries */
+               for (i = 0; i < replace->num_chains; i++) {
+                       if (!(entries = replace->chains[i]))
+                               continue;
+                       entries->counter_offset = 0;
+                       ebt_empty_chain(entries);
+               }
+               return;
+       }
+
+       if (entries->nentries == 0)
+               return;
+       replace->nentries -= entries->nentries;
+       numdel = entries->nentries;
+
+       /* Update counter_offset */
+       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               entries->counter_offset -= numdel;
+       }
+
+       entries = ebt_to_chain(replace);
+       ebt_empty_chain(entries);
+}
+
+#define OPT_COUNT      0x1000 /* This value is also defined in ebtables.c */
+/* Returns the rule number on success (starting from 0), -1 on failure
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} */
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+                         struct ebt_u_entry *new_entry)
+{
+       struct ebt_u_entry *u_e;
+       struct ebt_u_match_list *m_l, *m_l2;
+       struct ebt_u_match *m;
+       struct ebt_u_watcher_list *w_l, *w_l2;
+       struct ebt_u_watcher *w;
+       struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+       int i, j, k;
+
+       u_e = entries->entries->next;
+       /* Check for an existing rule (if there are duplicate rules,
+        * take the first occurance) */
+       for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+               if (u_e->ethproto != new_entry->ethproto)
+                       continue;
+               if (strcmp(u_e->in, new_entry->in))
+                       continue;
+               if (strcmp(u_e->out, new_entry->out))
+                       continue;
+               if (strcmp(u_e->logical_in, new_entry->logical_in))
+                       continue;
+               if (strcmp(u_e->logical_out, new_entry->logical_out))
+                       continue;
+               if (new_entry->bitmask & EBT_SOURCEMAC &&
+                   memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+                       continue;
+               if (new_entry->bitmask & EBT_DESTMAC &&
+                   memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+                       continue;
+               if (new_entry->bitmask != u_e->bitmask ||
+                   new_entry->invflags != u_e->invflags)
+                       continue;
+               if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt !=
+                   u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt))
+                       continue;
+               /* Compare all matches */
+               m_l = new_entry->m_list;
+               j = 0;
+               while (m_l) {
+                       m = (struct ebt_u_match *)(m_l->m);
+                       m_l2 = u_e->m_list;
+                       while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
+                               m_l2 = m_l2->next;
+                       if (!m_l2 || !m->compare(m->m, m_l2->m))
+                               goto letscontinue;
+                       j++;
+                       m_l = m_l->next;
+               }
+               /* Now be sure they have the same nr of matches */
+               k = 0;
+               m_l = u_e->m_list;
+               while (m_l) {
+                       k++;
+                       m_l = m_l->next;
+               }
+               if (j != k)
+                       continue;
+
+               /* Compare all watchers */
+               w_l = new_entry->w_list;
+               j = 0;
+               while (w_l) {
+                       w = (struct ebt_u_watcher *)(w_l->w);
+                       w_l2 = u_e->w_list;
+                       while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
+                               w_l2 = w_l2->next;
+                       if (!w_l2 || !w->compare(w->w, w_l2->w))
+                               goto letscontinue;
+                       j++;
+                       w_l = w_l->next;
+               }
+               k = 0;
+               w_l = u_e->w_list;
+               while (w_l) {
+                       k++;
+                       w_l = w_l->next;
+               }
+               if (j != k)
+                       continue;
+               if (strcmp(t->t->u.name, u_e->t->u.name))
+                       continue;
+               if (!t->compare(t->t, u_e->t))
+                       continue;
+               return i;
+letscontinue:;
+       }
+       return -1;
+}
+
+/* Add a rule, rule_nr is the rule to update
+ * rule_nr specifies where the rule should be inserted
+ * rule_nr > 0 : insert the rule right before the rule_nr'th rule
+ *               (the first rule is rule 1)
+ * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule,
+ *               where n denotes the number of rules in the chain
+ * rule_nr == 0: add a new rule at the end of the chain
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} and updates these
+ * pointers so that they point to ebt_{match,watcher,target}, before adding
+ * the rule to the chain. Don't free() the ebt_{match,watcher,target} and
+ * don't reuse the new_entry after a successful call to ebt_add_rule() */
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry 
*new_entry, int rule_nr)
+{
+       int i;
+       struct ebt_u_entry *u_e;
+       struct ebt_u_match_list *m_l;
+       struct ebt_u_watcher_list *w_l;
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+       struct ebt_cntchanges *cc, *new_cc;
+
+       if (rule_nr <= 0)
+               rule_nr += entries->nentries;
+       else
+               rule_nr--;
+       if (rule_nr > entries->nentries || rule_nr < 0) {
+               ebt_print_error("The specified rule number is incorrect");
+               return;
+       }
+       /* Go to the right position in the chain */
+       if (rule_nr == entries->nentries)
+               u_e = entries->entries;
+       else {
+               u_e = entries->entries->next;
+               for (i = 0; i < rule_nr; i++)
+                       u_e = u_e->next;
+       }
+       /* We're adding one rule */
+       replace->nentries++;
+       entries->nentries++;
+       /* Insert the rule */
+       new_entry->next = u_e;
+       new_entry->prev = u_e->prev;
+       u_e->prev->next = new_entry;
+       u_e->prev = new_entry;
+       new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+       if (!new_cc)
+               ebt_print_memory();
+       new_cc->type = CNT_ADD;
+       new_cc->change = 0;
+       if (new_entry->next == entries->entries) {
+               for (i = replace->selected_chain+1; i < replace->num_chains; 
i++)
+                       if (!replace->chains[i] || replace->chains[i]->nentries 
== 0)
+                               continue;
+                       else
+                               break;
+               if (i == replace->num_chains)
+                       cc = replace->cc;
+               else
+                       cc = replace->chains[i]->entries->next->cc;
+       } else
+               cc = new_entry->next->cc;
+       new_cc->next = cc;
+       new_cc->prev = cc->prev;
+       cc->prev->next = new_cc;
+       cc->prev = new_cc;
+       new_entry->cc = new_cc;
+
+       /* Put the ebt_{match, watcher, target} pointers in place */
+       m_l = new_entry->m_list;
+       while (m_l) {
+               m_l->m = ((struct ebt_u_match *)m_l->m)->m;
+               m_l = m_l->next;
+       }
+       w_l = new_entry->w_list;
+       while (w_l) {
+               w_l->w = ((struct ebt_u_watcher *)w_l->w)->w;
+               w_l = w_l->next;
+       }
+       new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
+       /* Update the counter_offset of chains behind this one */
+       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+               entries = replace->chains[i];
+               if (!(entries = replace->chains[i]))
+                       continue;
+               entries->counter_offset++;
+       }
+}
+
+/* If *begin==*end==0 then find the rule corresponding to new_entry,
+ * else make the rule numbers positive (starting from 0) and check
+ * for bad rule numbers. */
+static int check_and_change_rule_number(struct ebt_u_replace *replace,
+   struct ebt_u_entry *new_entry, int *begin, int *end)
+{
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       if (*begin < 0)
+               *begin += entries->nentries + 1;
+       if (*end < 0)
+               *end += entries->nentries + 1;
+
+       if (*begin < 0 || *begin > *end || *end > entries->nentries) {
+               ebt_print_error("Sorry, wrong rule numbers");
+               return -1;
+       }
+
+       if ((*begin * *end == 0) && (*begin + *end != 0))
+               ebt_print_bug("begin and end should be either both zero, "
+                             "either both non-zero");
+       if (*begin != 0) {
+               (*begin)--;
+               (*end)--;
+       } else {
+               *begin = ebt_check_rule_exists(replace, new_entry);
+               *end = *begin;
+               if (*begin == -1) {
+                       ebt_print_error("Sorry, rule does not exist");
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/* Delete a rule or rules
+ * begin == end == 0: delete the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}. */
+void ebt_delete_rule(struct ebt_u_replace *replace,
+                    struct ebt_u_entry *new_entry, int begin, int end)
+{
+       int i,  nr_deletes;
+       struct ebt_u_entry *u_e, *u_e2, *u_e3;
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+               return;
+       /* We're deleting rules */
+       nr_deletes = end - begin + 1;
+       replace->nentries -= nr_deletes;
+       entries->nentries -= nr_deletes;
+       /* Go to the right position in the chain */
+       u_e = entries->entries->next;
+       for (i = 0; i < begin; i++)
+               u_e = u_e->next;
+       u_e3 = u_e->prev;
+       /* Remove the rules */
+       for (i = 0; i < nr_deletes; i++) {
+               u_e2 = u_e;
+               ebt_delete_cc(u_e2->cc);
+               u_e = u_e->next;
+               /* Free everything */
+               ebt_free_u_entry(u_e2);
+               free(u_e2);
+       }
+       u_e3->next = u_e;
+       u_e->prev = u_e3;
+       /* Update the counter_offset of chains behind this one */
+       for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               entries->counter_offset -= nr_deletes;
+       }
+}
+
+/* Change the counters of a rule or rules
+ * begin == end == 0: change counters of the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}.
+ * The mask denotes the following:
+ *    pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement
+ *    bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment
+ * In daemon mode, mask==0 must hold */
+void ebt_change_counters(struct ebt_u_replace *replace,
+                    struct ebt_u_entry *new_entry, int begin, int end,
+                    struct ebt_counter *cnt, int mask)
+{
+       int i;
+       struct ebt_u_entry *u_e;
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+               return;
+       u_e = entries->entries->next;
+       for (i = 0; i < begin; i++)
+               u_e = u_e->next;
+       for (i = end-begin+1; i > 0; i--) {
+               if (mask % 3 == 0) {
+                       u_e->cnt.pcnt = (*cnt).pcnt;
+                       u_e->cnt_surplus.pcnt = 0;
+               } else {
+#ifdef EBT_DEBUG
+                       if (u_e->cc->type != CNT_NORM)
+                               ebt_print_bug("cc->type != CNT_NORM");
+#endif
+                       u_e->cnt_surplus.pcnt = (*cnt).pcnt;
+               }
+
+               if (mask / 3 == 0) {
+                       u_e->cnt.bcnt = (*cnt).bcnt;
+                       u_e->cnt_surplus.bcnt = 0;
+               } else {
+#ifdef EBT_DEBUG
+                       if (u_e->cc->type != CNT_NORM)
+                               ebt_print_bug("cc->type != CNT_NORM");
+#endif
+                       u_e->cnt_surplus.bcnt = (*cnt).bcnt;
+               }
+               if (u_e->cc->type != CNT_ADD)
+                       u_e->cc->type = CNT_CHANGE;
+               u_e->cc->change = mask;
+               u_e = u_e->next;
+       }
+}
+
+/* If selected_chain == -1 then zero all counters,
+ * otherwise, zero the counters of selected_chain */
+void ebt_zero_counters(struct ebt_u_replace *replace)
+{
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+       struct ebt_u_entry *next;
+       int i;
+
+       if (!entries) {
+               for (i = 0; i < replace->num_chains; i++) {
+                       if (!(entries = replace->chains[i]))
+                               continue;
+                       next = entries->entries->next;
+                       while (next != entries->entries) {
+                               if (next->cc->type == CNT_NORM)
+                                       next->cc->type = CNT_CHANGE;
+                               next->cnt.bcnt = next->cnt.pcnt = 0;
+                               next->cc->change = 0;
+                               next = next->next;
+                       }
+               }
+       } else {
+               if (entries->nentries == 0)
+                       return;
+
+               next = entries->entries->next;
+               while (next != entries->entries) {
+                       if (next->cc->type == CNT_NORM)
+                               next->cc->type = CNT_CHANGE;
+                       next->cnt.bcnt = next->cnt.pcnt = 0;
+                       next = next->next;
+               }
+       }
+}
+
+/* Add a new chain and specify its policy */
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy)
+{
+       struct ebt_u_entries *new;
+
+       if (replace->num_chains == replace->max_chains)
+               ebt_double_chains(replace);
+       new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
+       if (!new)
+               ebt_print_memory();
+       replace->chains[replace->num_chains++] = new;
+       new->nentries = 0;
+       new->policy = policy;
+       new->counter_offset = replace->nentries;
+       new->hook_mask = 0;
+       strcpy(new->name, name);
+       new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+       if (!new->entries)
+               ebt_print_memory();
+       new->entries->next = new->entries->prev = new->entries;
+       new->kernel_start = NULL;
+}
+
+/* returns -1 if the chain is referenced, 0 on success */
+static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int 
print_err)
+{
+       int tmp = replace->selected_chain;
+       /* If the chain is referenced, don't delete it,
+        * also decrement jumps to a chain behind the
+        * one we're deleting */
+       replace->selected_chain = chain;
+       if (ebt_check_for_references(replace, print_err))
+               return -1;
+       decrease_chain_jumps(replace);
+       ebt_flush_chains(replace);
+       replace->selected_chain = tmp;
+       free(replace->chains[chain]->entries);
+       free(replace->chains[chain]);
+       memmove(replace->chains+chain, replace->chains+chain+1, 
(replace->num_chains-chain-1)*sizeof(void *));
+       replace->num_chains--;
+       return 0;
+}
+
+/* Selected_chain == -1: delete all non-referenced udc
+ * selected_chain < NF_BR_NUMHOOKS is illegal */
+void ebt_delete_chain(struct ebt_u_replace *replace)
+{
+       if (replace->selected_chain != -1 && replace->selected_chain < 
NF_BR_NUMHOOKS)
+               ebt_print_bug("You can't remove a standard chain");
+       if (replace->selected_chain == -1) {
+               int i = NF_BR_NUMHOOKS;
+
+               while (i < replace->num_chains)
+                       if (ebt_delete_a_chain(replace, i, 0))
+                               i++;
+       } else
+               ebt_delete_a_chain(replace, replace->selected_chain, 1);
+}
+
+/* Rename an existing chain. */
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name)
+{
+       struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+       if (!entries)
+               ebt_print_bug("ebt_rename_chain: entries == NULL");
+       strcpy(entries->name, name);
+}
+
+
+           /*
+*************************
+*************************
+**SPECIALIZED*FUNCTIONS**
+*************************
+*************************
+            */
+
+
+void ebt_double_chains(struct ebt_u_replace *replace)
+{
+       struct ebt_u_entries **new;
+
+       replace->max_chains *= 2;
+       new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void 
*));
+       if (!new)
+               ebt_print_memory();
+       memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *));
+       free(replace->chains);
+       replace->chains = new;
+}
+
+/* Executes the final_check() function for all extensions used by the rule
+ * ebt_check_for_loops should have been executed earlier, to make sure the
+ * hook_mask is correct. The time argument to final_check() is set to 1,
+ * meaning it's the second time the final_check() function is executed. */
+void ebt_do_final_checks(struct ebt_u_replace *replace, 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 = ebt_find_match(m_l->m->u.name);
+               m->final_check(e, m_l->m, replace->name,
+                  entries->hook_mask, 1);
+               if (ebt_errormsg[0] != '\0')
+                       return;
+               m_l = m_l->next;
+       }
+       while (w_l) {
+               w = ebt_find_watcher(w_l->w->u.name);
+               w->final_check(e, w_l->w, replace->name,
+                  entries->hook_mask, 1);
+               if (ebt_errormsg[0] != '\0')
+                       return;
+               w_l = w_l->next;
+       }
+       t = ebt_find_target(e->t->u.name);
+       t->final_check(e, e->t, replace->name,
+          entries->hook_mask, 1);
+}
+
+/* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err)
+{
+       if (print_err)
+               return iterate_entries(replace, 1);
+       else
+               return iterate_entries(replace, 2);
+}
+
+/* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS)
+ * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+                              int print_err)
+{
+       int tmp = replace->selected_chain, ret;
+
+       replace->selected_chain = chain_nr;
+       if (print_err)
+               ret = iterate_entries(replace, 1);
+       else
+               ret = iterate_entries(replace, 2);
+       replace->selected_chain = tmp;
+       return ret;
+}
+
+struct ebt_u_stack
+{
+       int chain_nr;
+       int n;
+       struct ebt_u_entry *e;
+       struct ebt_u_entries *entries;
+};
+
+/* Checks for loops
+ * As a by-product, the hook_mask member of each chain is filled in
+ * correctly. The check functions of the extensions need this hook_mask
+ * to know from which standard chains they can be called. */
+void ebt_check_for_loops(struct ebt_u_replace *replace)
+{
+       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;
+
+       /* Initialize hook_mask to 0 */
+       for (i = 0; i < replace->num_chains; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               if (i < NF_BR_NUMHOOKS)
+                       /* (1 << NF_BR_NUMHOOKS) implies it's a standard chain
+                        * (usefull in the final_check() funtions) */
+                       entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
+               else
+                       entries->hook_mask = 0;
+       }
+       if (replace->num_chains == NF_BR_NUMHOOKS)
+               return;
+       stack = (struct ebt_u_stack *)malloc((replace->num_chains - 
NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack));
+       if (!stack)
+               ebt_print_memory();
+
+       /* Check for loops, starting from every base chain */
+       for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               chain_nr = i;
+
+               e = entries->entries->next;
+               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;
+                       /* Now see if we've been here before */
+                       for (k = 0; k < sp; k++)
+                               if (stack[k].chain_nr == verdict + 
NF_BR_NUMHOOKS) {
+                                       ebt_print_error("Loop from chain '%s' 
to chain '%s'",
+                                          replace->chains[chain_nr]->name,
+                                          
replace->chains[stack[k].chain_nr]->name);
+                                       goto free_stack;
+                               }
+                       entries2 = replace->chains[verdict + NF_BR_NUMHOOKS];
+                       /* check if we've dealt with this chain already */
+                       if (entries2->hook_mask & (1<<i))
+                               goto letscontinue;
+                       entries2->hook_mask |= entries->hook_mask;
+                       /* 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->next;
+                       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:
+       free(stack);
+       return;
+}
+
+/* The user will use the match, so put it in new_entry. The ebt_u_match
+ * pointer is put in the ebt_entry_match pointer. ebt_add_rule will
+ * fill in the final value for new->m. Unless the rule is added to a chain,
+ * the pointer will keep pointing to the ebt_u_match (until the new_entry
+ * is freed). I know, I should use a union for these 2 pointer types... */
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m)
+{
+       struct ebt_u_match_list **m_list, *new;
+
+       for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+       new = (struct ebt_u_match_list *)
+          malloc(sizeof(struct ebt_u_match_list));
+       if (!new)
+               ebt_print_memory();
+       *m_list = new;
+       new->next = NULL;
+       new->m = (struct ebt_entry_match *)m;
+}
+
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w)
+{
+       struct ebt_u_watcher_list **w_list;
+       struct ebt_u_watcher_list *new;
+
+       for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+       new = (struct ebt_u_watcher_list *)
+          malloc(sizeof(struct ebt_u_watcher_list));
+       if (!new)
+               ebt_print_memory();
+       *w_list = new;
+       new->next = NULL;
+       new->w = (struct ebt_entry_watcher *)w;
+}
+
+
+        /*
+*******************
+*******************
+**OTHER*FUNCTIONS**
+*******************
+*******************
+         */
+
+
+/* type = 0 => update chain jumps
+ * type = 1 => check for reference, print error when referenced
+ * type = 2 => check for reference, don't print error when referenced
+ *
+ * Returns 1 when type == 1 and the chain is referenced
+ * returns 0 otherwise */
+static int iterate_entries(struct ebt_u_replace *replace, int type)
+{
+       int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS;
+       struct ebt_u_entries *entries;
+       struct ebt_u_entry *e;
+
+       if (chain_nr < 0)
+               ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr);
+       for (i = 0; i < replace->num_chains; i++) {
+               if (!(entries = replace->chains[i]))
+                       continue;
+               e = entries->entries->next;
+               for (j = 0; j < entries->nentries; j++) {
+                       int chain_jmp;
+
+                       if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+                               e = e->next;
+                               continue;
+                       }
+                       chain_jmp = ((struct ebt_standard_target *)e->t)->
+                                   verdict;
+                       switch (type) {
+                       case 1:
+                       case 2:
+                       if (chain_jmp == chain_nr) {
+                               if (type == 2)
+                                       return 1;
+                               ebt_print_error("Can't delete the chain '%s', 
it's referenced in chain '%s', rule %d",
+                                               replace->chains[chain_nr + 
NF_BR_NUMHOOKS]->name, entries->name, j);
+                               return 1;
+                       }
+                       break;
+                       case 0:
+                       /* Adjust the chain jumps when necessary */
+                       if (chain_jmp > chain_nr)
+                               ((struct ebt_standard_target *)e->t)->verdict--;
+                       break;
+                       } /* End switch */
+                       e = e->next;
+               }
+       }
+       return 0;
+}
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace)
+{
+       iterate_entries(replace, 0);
+}
+
+/* Used in initialization code of modules */
+void ebt_register_match(struct ebt_u_match *m)
+{
+       int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
+       struct ebt_u_match **i;
+
+       m->m = (struct ebt_entry_match *)malloc(size);
+       if (!m->m)
+               ebt_print_memory();
+       strcpy(m->m->u.name, m->name);
+       m->m->match_size = EBT_ALIGN(m->size);
+       m->init(m->m);
+
+       for (i = &ebt_matches; *i; i = &((*i)->next));
+       m->next = NULL;
+       *i = m;
+}
+
+void ebt_register_watcher(struct ebt_u_watcher *w)
+{
+       int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
+       struct ebt_u_watcher **i;
+
+       w->w = (struct ebt_entry_watcher *)malloc(size);
+       if (!w->w)
+               ebt_print_memory();
+       strcpy(w->w->u.name, w->name);
+       w->w->watcher_size = EBT_ALIGN(w->size);
+       w->init(w->w);
+
+       for (i = &ebt_watchers; *i; i = &((*i)->next));
+       w->next = NULL;
+       *i = w;
+}
+
+void ebt_register_target(struct ebt_u_target *t)
+{
+       int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
+       struct ebt_u_target **i;
+
+       t->t = (struct ebt_entry_target *)malloc(size);
+       if (!t->t)
+               ebt_print_memory();
+       strcpy(t->t->u.name, t->name);
+       t->t->target_size = EBT_ALIGN(t->size);
+       t->init(t->t);
+
+       for (i = &ebt_targets; *i; i = &((*i)->next));
+       t->next = NULL;
+       *i = t;
+}
+
+void ebt_register_table(struct ebt_u_table *t)
+{
+       t->next = ebt_tables;
+       ebt_tables = t;
+}
+
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *))
+{
+       struct ebt_u_match *i;
+
+       for (i = ebt_matches; i; i = i->next)
+               f(i);
+}
+
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *))
+{
+       struct ebt_u_watcher *i;
+
+       for (i = ebt_watchers; i; i = i->next)
+               f(i);
+}
+
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *))
+{
+       struct ebt_u_target *i;
+
+       for (i = ebt_targets; i; i = i->next)
+               f(i);
+}
+
+/* Don't use this function, use ebt_print_bug() */
+void __ebt_print_bug(char *file, int line, char *format, ...)
+{
+       va_list l;
+
+       va_start(l, format);
+       fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, 
line);
+       vfprintf(stderr, format, l);
+       fprintf(stderr, "\n");
+       va_end(l);
+       exit (-1);
+}
+
+/* The error messages are put in here when ebt_silent == 1
+ * ebt_errormsg[0] == '\0' implies there was no error */
+char ebt_errormsg[ERRORMSG_MAXLEN];
+/* When error messages should not be printed on the screen, after which
+ * the program exit()s, set ebt_silent to 1. */
+int ebt_silent;
+/* Don't use this function, use ebt_print_error() */
+void __ebt_print_error(char *format, ...)
+{
+       va_list l;
+
+       va_start(l, format);
+       if (ebt_silent && ebt_errormsg[0] == '\0') {
+               vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
+               va_end(l);
+       } else {
+               vfprintf(stderr, format, l);
+               fprintf(stderr, ".\n");
+               va_end(l);
+               exit (-1);
+       }
+}
diff --git a/tools/remus/imqebt/useful_functions.c 
b/tools/remus/imqebt/useful_functions.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/imqebt/useful_functions.c
@@ -0,0 +1,413 @@
+/*
+ * useful_functions.c, January 2004
+ *
+ * Random collection of functions that can be used by extensions.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <stdio.h>
+#include <netinet/ether.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+const unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
+const unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
+const unsigned char msk_type_bridge_group[ETH_ALEN] = 
{255,255,255,255,255,255};
+
+/* 0: default, print only 2 digits if necessary
+ * 2: always print 2 digits, a printed mac address
+ * then always has the same length */
+int ebt_printstyle_mac;
+
+void ebt_print_mac(const unsigned char *mac)
+{
+       if (ebt_printstyle_mac == 2) {
+               int j;
+               for (j = 0; j < ETH_ALEN; j++)
+                       printf("%02x%s", mac[j],
+                               (j==ETH_ALEN-1) ? "" : ":");
+       } else
+               printf("%s", ether_ntoa((struct ether_addr *) mac));
+}
+
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char 
*mask)
+{
+       char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+       if (!memcmp(mac, mac_type_unicast, 6) &&
+           !memcmp(mask, msk_type_unicast, 6))
+               printf("Unicast");
+       else if (!memcmp(mac, mac_type_multicast, 6) &&
+                !memcmp(mask, msk_type_multicast, 6))
+               printf("Multicast");
+       else if (!memcmp(mac, mac_type_broadcast, 6) &&
+                !memcmp(mask, msk_type_broadcast, 6))
+               printf("Broadcast");
+       else if (!memcmp(mac, mac_type_bridge_group, 6) &&
+                !memcmp(mask, msk_type_bridge_group, 6))
+               printf("BGA");
+       else {
+               ebt_print_mac(mac);
+               if (memcmp(mask, hlpmsk, 6)) {
+                       printf("/");
+                       ebt_print_mac(mask);
+               }
+       }
+}
+
+/* Checks the type for validity and calls getethertypebynumber(). */
+struct ethertypeent *parseethertypebynumber(int type)
+{
+       if (type < 1536)
+               ebt_print_error("Ethernet protocols have values >= 0x0600");
+       if (type > 0xffff)
+               ebt_print_error("Ethernet protocols have values <= 0xffff");
+       return getethertypebynumber(type);
+}
+
+/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
+int ebt_get_mac_and_mask(const char *from, unsigned char *to,
+  unsigned char *mask)
+{
+       char *p;
+       int i;
+       struct ether_addr *addr;
+
+       if (strcasecmp(from, "Unicast") == 0) {
+               memcpy(to, mac_type_unicast, ETH_ALEN);
+               memcpy(mask, msk_type_unicast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "Multicast") == 0) {
+               memcpy(to, mac_type_multicast, ETH_ALEN);
+               memcpy(mask, msk_type_multicast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "Broadcast") == 0) {
+               memcpy(to, mac_type_broadcast, ETH_ALEN);
+               memcpy(mask, msk_type_broadcast, ETH_ALEN);
+               return 0;
+       }
+       if (strcasecmp(from, "BGA") == 0) {
+               memcpy(to, mac_type_bridge_group, ETH_ALEN);
+               memcpy(mask, msk_type_bridge_group, ETH_ALEN);
+               return 0;
+       }
+       if ( (p = strrchr(from, '/')) != NULL) {
+               *p = '\0';
+               if (!(addr = ether_aton(p + 1)))
+                       return -1;
+               memcpy(mask, addr, ETH_ALEN);
+       } else
+               memset(mask, 0xff, ETH_ALEN);
+       if (!(addr = ether_aton(from)))
+               return -1;
+       memcpy(to, addr, ETH_ALEN);
+       for (i = 0; i < ETH_ALEN; i++)
+               to[i] &= mask[i];
+       return 0;
+}
+
+/* 0: default
+ * 1: the inverse '!' of the option has already been specified */
+int ebt_invert = 0;
+
+/*
+ * Check if the inverse of the option is specified. This is used
+ * in the parse functions of the extensions and ebtables.c
+ */
+int _ebt_check_inverse(const char option[], int argc, char **argv)
+{
+       if (!option)
+               return ebt_invert;
+       if (strcmp(option, "!") == 0) {
+               if (ebt_invert == 1)
+                       ebt_print_error("Double use of '!' not allowed");
+               if (optind >= argc)
+                       optarg = NULL;
+               else
+                       optarg = argv[optind];
+               optind++;
+               ebt_invert = 1;
+               return 1;
+       }
+       return ebt_invert;
+}
+
+/* Make sure the same option wasn't specified twice. This is used
+ * in the parse functions of the extensions and ebtables.c */
+void ebt_check_option(unsigned int *flags, unsigned int mask)
+{
+       if (*flags & mask)
+               ebt_print_error("Multiple use of same option not allowed");
+       *flags |= mask;
+}
+
+/* Put the ip string into 4 bytes. */
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+       char *p, *q, *end;
+       long int onebyte;
+       int i;
+       char buf[20];
+
+       strncpy(buf, ip, sizeof(buf) - 1);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL)
+                       return -1;
+               *q = '\0';
+               onebyte = strtol(p, &end, 10);
+               if (*end != '\0' || onebyte > 255 || onebyte < 0)   
+                       return -1;
+               ip2[i] = (unsigned char)onebyte;
+               p = q + 1;
+       }
+
+       onebyte = strtol(p, &end, 10);
+       if (*end != '\0' || onebyte > 255 || onebyte < 0)
+               return -1;
+       ip2[3] = (unsigned char)onebyte;
+
+       return 0;
+}
+
+/* Put the mask into 4 bytes. */
+static int ip_mask(char *mask, unsigned char *mask2)
+{
+       char *end;
+       long int bits;
+       uint32_t mask22;
+
+       if (undot_ip(mask, mask2)) {
+               /* not the /a.b.c.e format, maybe the /x format */
+               bits = strtol(mask, &end, 10);
+               if (*end != '\0' || bits > 32 || bits < 0)
+                       return -1;
+               if (bits != 0) {
+                       mask22 = htonl(0xFFFFFFFF << (32 - bits));
+                       memcpy(mask2, &mask22, 4);
+               } else {
+                       mask22 = 0xFFFFFFFF;
+                       memcpy(mask2, &mask22, 4);
+               }
+       }
+       return 0;
+}
+
+/* Set the ip mask and ip address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+{
+       char *p;
+
+       /* first the mask */
+       if ((p = strrchr(address, '/')) != NULL) {
+               *p = '\0';
+               if (ip_mask(p + 1, (unsigned char *)msk)) {
+                       ebt_print_error("Problem with the IP mask '%s'", p + 1);
+                       return;
+               }
+       } else
+               *msk = 0xFFFFFFFF;
+
+       if (undot_ip(address, (unsigned char *)addr)) {
+               ebt_print_error("Problem with the IP address '%s'", address);
+               return;
+       }
+       *addr = *addr & *msk;
+}
+
+
+/* Transform the ip mask into a string ready for output. */
+char *ebt_mask_to_dotted(uint32_t mask)
+{
+       int i;
+       static char buf[20];
+       uint32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask);
+
+       /* don't print /32 */
+       if (mask == 0xFFFFFFFFL) {
+               *buf = '\0';
+               return buf;
+       }
+
+       i = 32;
+       bits = 0xFFFFFFFEL; /* Case 0xFFFFFFFF has just been dealt with */
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+
+       if (i > 0)
+               sprintf(buf, "/%d", i);
+       else if (!i)
+               *buf = '\0';
+       else
+               /* Mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], 
+                  ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
+                  ((unsigned char *)&mask)[3]);
+
+       return buf;
+}
+
+/* Most of the following code is derived from iptables */
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+       memcpy(dst, src, sizeof(struct in6_addr));
+}
+
+int string_to_number_ll(const char *s, unsigned long long min,
+            unsigned long long max, unsigned long long *ret)
+{
+       unsigned long long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtoull(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && (!max || number <= 
max)) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+int string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                       unsigned long *ret)
+{
+       int result;
+       unsigned long long number;
+
+       result = string_to_number_ll(s, min, max, &number);
+       *ret = (unsigned long)number;
+
+       return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+                     unsigned int *ret)
+{
+       int result;
+       unsigned long number;
+
+       result = string_to_number_l(s, min, max, &number);
+       *ret = (unsigned int)number;
+
+       return result;
+}
+
+static struct in6_addr *numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+       return (struct in6_addr *)NULL;
+}
+
+static struct in6_addr *parse_ip6_mask(char *mask)
+{
+       static struct in6_addr maskaddr;
+       struct in6_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 128 bits */
+               memset(&maskaddr, 0xff, sizeof maskaddr);
+               return &maskaddr;
+       }
+       if ((addrp = numeric_to_addr(mask)) != NULL)
+               return addrp;
+       if (string_to_number(mask, 0, 128, &bits) == -1)
+               ebt_print_error("Invalid IPv6 Mask '%s' specified", mask);
+       if (bits != 0) {
+               char *p = (char *)&maskaddr;
+               memset(p, 0xff, bits / 8);
+               memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+               p[bits / 8] = 0xff << (8 - (bits & 7));
+               return &maskaddr;
+       }
+
+       memset(&maskaddr, 0, sizeof maskaddr);
+       return &maskaddr;
+}
+
+/* Set the ipv6 mask and address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr,
+                           struct in6_addr *msk)
+{
+       struct in6_addr *tmp_addr;
+       char buf[256];
+       char *p;
+       int i;
+       int err;
+
+       strncpy(buf, address, sizeof(buf) - 1);
+       /* first the mask */
+       buf[sizeof(buf) - 1] = '\0';
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               tmp_addr = parse_ip6_mask(p + 1);
+       } else
+               tmp_addr = parse_ip6_mask(NULL);
+       in6addrcpy(msk, tmp_addr);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any)))
+               strcpy(buf, "::");
+
+       if ((err=inet_pton(AF_INET6, buf, addr)) < 1) {
+               ebt_print_error("Invalid IPv6 Address '%s' specified", buf);
+               return;
+       }
+
+       for (i = 0; i < 4; i++)
+               addr->s6_addr32[i] &= msk->s6_addr32[i];
+}
+
+/* Transform the ip6 addr into a string ready for output. */
+char *ebt_ip6_to_numeric(const struct in6_addr *addrp)
+{
+       /* 0000:0000:0000:0000:0000:000.000.000.000
+        * 0000:0000:0000:0000:0000:0000:0000:0000 */
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
diff --git a/tools/remus/kmod/Kbuild b/tools/remus/kmod/Kbuild
new file mode 100644
--- /dev/null
+++ b/tools/remus/kmod/Kbuild
@@ -0,0 +1,1 @@
+obj-m := sch_queue.o ebt_imq.o
diff --git a/tools/remus/kmod/Makefile b/tools/remus/kmod/Makefile
new file mode 100644
--- /dev/null
+++ b/tools/remus/kmod/Makefile
@@ -0,0 +1,24 @@
+XEN_ROOT=../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+# Should make makefiles export linux build directory!
+# This is a fragile hack to tide us over
+ifeq ($(KERNELS),linux-2.6-xen)
+LINUX_VER=2.6.18-xen
+endif
+ifeq ($(KERNELS),linux-2.6-xen0)
+LINUX_VER=2.6.18-xen0
+endif
+
+KERNELDIR ?= $(XEN_ROOT)/build-linux-$(LINUX_VER)_$(XEN_TARGET_ARCH)
+
+.PHONY: all
+all:
+       if test -d $(KERNELDIR); then $(MAKE) -C $(KERNELDIR) SUBDIRS=`pwd` 
modules; fi
+
+.PHONY: install
+install:
+       if test -d $(KERNELDIR); then $(MAKE) -C $(KERNELDIR) SUBDIRS=`pwd` 
INSTALL_MOD_PATH=$(DESTDIR) modules_install; fi
+
+clean::
+       -rm -rf *.o *.ko *.mod.c *.mod.o Module.symvers .*.cmd .tmp_versions
diff --git a/tools/remus/kmod/ebt_imq.c b/tools/remus/kmod/ebt_imq.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/kmod/ebt_imq.c
@@ -0,0 +1,45 @@
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netdevice.h>
+#include "ebt_imq.h"
+
+static int ebt_target_imq(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_imq_info *info = (struct ebt_imq_info *) data;
+
+  (*pskb)->imq_flags = info->todev | IMQ_F_ENQUEUE;
+
+  return EBT_CONTINUE;
+}
+
+static int ebt_target_imq_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+  return 0;
+}
+
+static struct ebt_target imq_target =
+{
+  .name                = "imq",
+  .target       = ebt_target_imq,
+  .check       = ebt_target_imq_check,
+  .me          = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+  return ebt_register_target(&imq_target);
+}
+
+static void __exit fini(void)
+{
+  ebt_unregister_target(&imq_target);
+}
+
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
diff --git a/tools/remus/kmod/ebt_imq.h b/tools/remus/kmod/ebt_imq.h
new file mode 100644
--- /dev/null
+++ b/tools/remus/kmod/ebt_imq.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_BRIDGE_EBT_IMQ_H
+#define __LINUX_BRIDGE_EBT_IMQ_H
+
+#define IMQ_F_ENQUEUE 0x80
+
+struct ebt_imq_info
+{
+  unsigned int todev;
+};
+#endif
diff --git a/tools/remus/kmod/sch_queue.c b/tools/remus/kmod/sch_queue.c
new file mode 100644
--- /dev/null
+++ b/tools/remus/kmod/sch_queue.c
@@ -0,0 +1,208 @@
+/*
+ * sch_queue.c Queue traffic until an explicit release command
+ *
+ *             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.
+ *
+ * The operation of the buffer is as follows:
+ * When a checkpoint begins, a barrier is inserted into the
+ *   network queue by a netlink request (it operates by storing
+ *   a pointer to the next packet which arrives and blocking dequeue
+ *   when that packet is at the head of the queue).
+ * When a checkpoint completes (the backup acknowledges receipt),
+ *   currently-queued packets are released.
+ * So it supports two operations, barrier and release.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/pkt_sched.h>
+
+/* xenbus directory */
+#define FIFO_BUF    (10*1024*1024)
+
+#define TCQ_CHECKPOINT 0
+#define TCQ_DEQUEUE    1
+
+struct queue_sched_data {
+  /* this packet is the first packet which should not be delivered.
+   * If it is NULL, queue_enqueue will set it to the next packet it sees. */
+  struct sk_buff *stop;
+};
+
+struct tc_queue_qopt {
+  /* 0: reset stop packet pointer
+   * 1: dequeue to stop pointer */
+  int action;
+};
+
+/* borrowed from drivers/xen/netback/loopback.c */
+static int is_foreign(unsigned long pfn)
+{
+  /* NB. Play it safe for auto-translation mode. */
+  return (xen_feature(XENFEAT_auto_translated_physmap) ||
+         (phys_to_machine_mapping[pfn] & FOREIGN_FRAME_BIT));
+}
+
+static int skb_remove_foreign_references(struct sk_buff *skb)
+{
+  struct page *page;
+  unsigned long pfn;
+  int i, off;
+  char *vaddr;
+
+  BUG_ON(skb_shinfo(skb)->frag_list);
+
+  for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+    pfn = page_to_pfn(skb_shinfo(skb)->frags[i].page);
+    if (!is_foreign(pfn))
+      continue;
+    /*
+      printk("foreign ref found\n");
+    */
+    page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
+    if (unlikely(!page))
+      return 0;
+
+    vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+    off = skb_shinfo(skb)->frags[i].page_offset;
+    memcpy(page_address(page) + off, vaddr + off,
+          skb_shinfo(skb)->frags[i].size);
+    kunmap_skb_frag(vaddr);
+
+    put_page(skb_shinfo(skb)->frags[i].page);
+    skb_shinfo(skb)->frags[i].page = page;
+  }
+
+  return 1;
+}
+
+static int queue_enqueue(struct sk_buff *skb, struct Qdisc* sch)
+{
+  struct queue_sched_data *q = qdisc_priv(sch);
+
+  if (likely(sch->qstats.backlog + skb->len <= FIFO_BUF))
+  {
+    if (!q->stop)
+      q->stop = skb;
+
+    if (!skb_remove_foreign_references(skb)) {
+      printk("error removing foreign ref\n");
+      return qdisc_reshape_fail(skb, sch);
+    }
+
+    return qdisc_enqueue_tail(skb, sch);
+  }
+  printk("queue reported full: %d,%d\n", sch->qstats.backlog, skb->len);
+
+  return qdisc_reshape_fail(skb, sch);
+}
+
+/* dequeue doesn't actually dequeue until the release command is
+ * received. */
+static inline struct sk_buff *queue_dequeue(struct Qdisc* sch)
+{
+  struct queue_sched_data *q = qdisc_priv(sch);
+  struct sk_buff* peek;
+  /*
+  struct timeval tv;
+
+  if (!q->stop) {
+    do_gettimeofday(&tv);
+    printk("packet dequeued at %lu.%06lu\n", tv.tv_sec, tv.tv_usec);
+  }
+  */
+
+  if (sch->flags & TCQ_F_THROTTLED)
+    return NULL;
+  
+  peek = (struct sk_buff *)((sch->q).next);
+
+  /* this pointer comparison may be shady */
+  if (peek == q->stop) {
+    /*
+    do_gettimeofday(&tv);
+    printk("stop packet at %lu.%06lu\n", tv.tv_sec, tv.tv_usec);
+    */
+
+    /* this is the tail of the last round. Release it and block the queue */
+    sch->flags |= TCQ_F_THROTTLED;
+    return NULL;
+  }
+
+  return qdisc_dequeue_head(sch);
+}
+
+static int queue_init(struct Qdisc *sch, struct rtattr *opt)
+{
+  sch->flags |= TCQ_F_THROTTLED;
+
+  return 0;
+}
+
+/* receives two messages:
+ *   0: checkpoint queue (set stop to next packet)
+ *   1: dequeue until stop */
+static int queue_change(struct Qdisc* sch, struct rtattr* opt)
+{
+  struct queue_sched_data *q = qdisc_priv(sch);
+  struct tc_queue_qopt* msg;
+  /*
+  struct timeval tv;
+  */
+
+  if (!opt || RTA_PAYLOAD(opt) < sizeof(*msg))
+    return -EINVAL;
+
+  msg = RTA_DATA(opt);
+
+  if (msg->action == TCQ_CHECKPOINT) {
+    /* reset stop */
+    q->stop = NULL;
+  } else if (msg->action == TCQ_DEQUEUE) {
+    /* dequeue */
+    sch->flags &= ~TCQ_F_THROTTLED;
+    netif_schedule(sch->dev);
+    /*
+    do_gettimeofday(&tv);
+    printk("queue release at %lu.%06lu (%d bytes)\n", tv.tv_sec, tv.tv_usec,
+          sch->qstats.backlog);
+    */
+  } else {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+struct Qdisc_ops queue_qdisc_ops = {
+  .id          =       "queue",
+  .priv_size   =       sizeof(struct queue_sched_data),
+  .enqueue     =       queue_enqueue,
+  .dequeue     =       queue_dequeue,
+  .init                =       queue_init,
+  .change       =      queue_change,
+  .owner       =       THIS_MODULE,
+};
+
+static int __init queue_module_init(void)
+{
+  printk("loading queue\n");
+  return register_qdisc(&queue_qdisc_ops);
+}
+
+static void __exit queue_module_exit(void)
+{
+  printk("queue unloaded\n");
+  unregister_qdisc(&queue_qdisc_ops);
+}
+module_init(queue_module_init)
+module_exit(queue_module_exit)
+MODULE_LICENSE("GPL");

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel