aboutsummaryrefslogtreecommitdiffstats
path: root/tools/remus
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-11-13 15:34:46 +0000
committerKeir Fraser <keir.fraser@citrix.com>2009-11-13 15:34:46 +0000
commitb1a4f45a56d2b04d1a1fe6eeebba5e9e661256a8 (patch)
tree736d2aaea30a733e23c55d6504939c56da4e62d4 /tools/remus
parent25f52c4c7401708e7f1b71b705b65dc84bc693a7 (diff)
downloadxen-b1a4f45a56d2b04d1a1fe6eeebba5e9e661256a8.tar.gz
xen-b1a4f45a56d2b04d1a1fe6eeebba5e9e661256a8.tar.bz2
xen-b1a4f45a56d2b04d1a1fe6eeebba5e9e661256a8.zip
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@cs.ubc.ca>
Diffstat (limited to 'tools/remus')
-rw-r--r--tools/remus/Makefile13
-rw-r--r--tools/remus/imqebt/Makefile97
-rw-r--r--tools/remus/imqebt/README2
-rw-r--r--tools/remus/imqebt/communication.c762
-rw-r--r--tools/remus/imqebt/ebtables-standalone.c14
-rw-r--r--tools/remus/imqebt/ebtables.c1233
-rw-r--r--tools/remus/imqebt/extensions/Makefile29
-rw-r--r--tools/remus/imqebt/extensions/ebt_imq.c84
-rw-r--r--tools/remus/imqebt/extensions/ebt_standard.c90
-rw-r--r--tools/remus/imqebt/extensions/ebtable_filter.c35
-rw-r--r--tools/remus/imqebt/getethertype.c162
-rw-r--r--tools/remus/imqebt/include/ebtables_u.h379
-rw-r--r--tools/remus/imqebt/include/ethernetdb.h58
-rw-r--r--tools/remus/imqebt/include/linux/if_ether.h146
-rw-r--r--tools/remus/imqebt/include/linux/netfilter_bridge.h91
-rw-r--r--tools/remus/imqebt/include/linux/netfilter_bridge/ebt_imq.h8
-rw-r--r--tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h276
-rw-r--r--tools/remus/imqebt/include/linux/types.h209
-rw-r--r--tools/remus/imqebt/libebtc.c1280
-rw-r--r--tools/remus/imqebt/useful_functions.c413
-rw-r--r--tools/remus/kmod/Kbuild1
-rw-r--r--tools/remus/kmod/Makefile24
-rw-r--r--tools/remus/kmod/ebt_imq.c45
-rw-r--r--tools/remus/kmod/ebt_imq.h10
-rw-r--r--tools/remus/kmod/sch_queue.c208
25 files changed, 5661 insertions, 8 deletions
diff --git a/tools/remus/Makefile b/tools/remus/Makefile
index 17518a95f7..70e175075f 100644
--- 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
index 0000000000..fcbfb5ba11
--- /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
index 0000000000..dacb9022b0
--- /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
index 0000000000..0c58c8fbe4
--- /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
index 0000000000..eecc210d44
--- /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
index 0000000000..b0e557893d
--- /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
index 0000000000..3a07fb5ed8
--- /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
index 0000000000..e1069fdb9a
--- /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
index 0000000000..2c36cff0a3
--- /dev/null
+++ b/tools/remus/imqebt/extensions/ebt_standard.c
@@ -0,0 +1,90 @@
+/* ebt_standard
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * 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
index 0000000000..80e91c5693
--- /dev/null
+++ b/tools/remus/imqebt/extensions/ebtable_filter.c
@@ -0,0 +1,35 @@
+/* ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * 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
index 0000000000..7efc0cbdbe
--- /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@ukrsat.com>
+* Checker: Bart De Schuymer <bdschuym@pandora.be>
+* 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
index 0000000000..fad004258a
--- /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
index 0000000000..fc3458225f
--- /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
index 0000000000..1ab699484f
--- /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@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@lxorguk.ukuu.org.uk>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ *
+ * 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@cafe.net) */
+#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
index 0000000000..cd3f538ae1
--- /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
index 0000000000..30095f09df
--- /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
index 0000000000..71d39702a5
--- /dev/null
+++ b/tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,276 @@
+/*
+ * ebtables
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * ebtables.c,v 2.0, April, 2002
+ *
+ * This code is stongly inspired on the iptables code which is
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+/* 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
index 0000000000..de999aeb6a
--- /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
index 0000000000..4f99353334
--- /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
index 0000000000..e9b364e519
--- /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
index 0000000000..a76c5a4b88
--- /dev/null
+++ b/tools/remus/kmod/Kbuild
@@ -0,0 +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
index 0000000000..5780269086
--- /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
index 0000000000..7f8847cb9b
--- /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
index 0000000000..41627cfcb8
--- /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
index 0000000000..3a6a50c39a
--- /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");