[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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



# 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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.