From b1a4f45a56d2b04d1a1fe6eeebba5e9e661256a8 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Fri, 13 Nov 2009 15:34:46 +0000 Subject: 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 --- tools/remus/Makefile | 13 +- tools/remus/imqebt/Makefile | 97 ++ tools/remus/imqebt/README | 2 + tools/remus/imqebt/communication.c | 762 ++++++++++++ tools/remus/imqebt/ebtables-standalone.c | 14 + tools/remus/imqebt/ebtables.c | 1233 +++++++++++++++++++ tools/remus/imqebt/extensions/Makefile | 29 + tools/remus/imqebt/extensions/ebt_imq.c | 84 ++ tools/remus/imqebt/extensions/ebt_standard.c | 90 ++ tools/remus/imqebt/extensions/ebtable_filter.c | 35 + tools/remus/imqebt/getethertype.c | 162 +++ tools/remus/imqebt/include/ebtables_u.h | 379 ++++++ tools/remus/imqebt/include/ethernetdb.h | 58 + tools/remus/imqebt/include/linux/if_ether.h | 146 +++ .../remus/imqebt/include/linux/netfilter_bridge.h | 91 ++ .../include/linux/netfilter_bridge/ebt_imq.h | 8 + .../include/linux/netfilter_bridge/ebtables.h | 276 +++++ tools/remus/imqebt/include/linux/types.h | 209 ++++ tools/remus/imqebt/libebtc.c | 1280 ++++++++++++++++++++ tools/remus/imqebt/useful_functions.c | 413 +++++++ tools/remus/kmod/Kbuild | 1 + tools/remus/kmod/Makefile | 24 + tools/remus/kmod/ebt_imq.c | 45 + tools/remus/kmod/ebt_imq.h | 10 + tools/remus/kmod/sch_queue.c | 208 ++++ 25 files changed, 5661 insertions(+), 8 deletions(-) create mode 100644 tools/remus/imqebt/Makefile create mode 100644 tools/remus/imqebt/README create mode 100644 tools/remus/imqebt/communication.c create mode 100644 tools/remus/imqebt/ebtables-standalone.c create mode 100644 tools/remus/imqebt/ebtables.c create mode 100644 tools/remus/imqebt/extensions/Makefile create mode 100644 tools/remus/imqebt/extensions/ebt_imq.c create mode 100644 tools/remus/imqebt/extensions/ebt_standard.c create mode 100644 tools/remus/imqebt/extensions/ebtable_filter.c create mode 100644 tools/remus/imqebt/getethertype.c create mode 100644 tools/remus/imqebt/include/ebtables_u.h create mode 100644 tools/remus/imqebt/include/ethernetdb.h create mode 100644 tools/remus/imqebt/include/linux/if_ether.h create mode 100644 tools/remus/imqebt/include/linux/netfilter_bridge.h create mode 100644 tools/remus/imqebt/include/linux/netfilter_bridge/ebt_imq.h create mode 100644 tools/remus/imqebt/include/linux/netfilter_bridge/ebtables.h create mode 100644 tools/remus/imqebt/include/linux/types.h create mode 100644 tools/remus/imqebt/libebtc.c create mode 100644 tools/remus/imqebt/useful_functions.c create mode 100644 tools/remus/kmod/Kbuild create mode 100644 tools/remus/kmod/Makefile create mode 100644 tools/remus/kmod/ebt_imq.c create mode 100644 tools/remus/kmod/ebt_imq.h create mode 100644 tools/remus/kmod/sch_queue.c (limited to 'tools/remus') 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 +#include +#include +#include +#include +#include +#include +#include +#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 +#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 +#include +#include +#include +#include +#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 \n" +"--atomic-init : put the initial kernel table into \n" +"--atomic-save : put the current kernel table into \n" +"--atomic-file file : set 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 (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 -L ) */ + 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 +#include +#include +#include "../include/ebtables_u.h" +#include + +#define IMQ_TODEV '1' + +static struct option opts[] = +{ + { "todev" , required_argument, 0, IMQ_TODEV }, + { 0 } +}; + +static void help(void) +{ + printf( + "IMQ options:\n" + " --todev enqueue to imq, 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 + * + * April, 2002 + */ + +#include +#include +#include +#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 + * + * April, 2002 + */ + +#include +#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 +* Checker: Bart De Schuymer +* Origin: uClibc-0.9.16/libc/inet/getproto.c +* Created at: Mon Nov 11 12:20:11 EET 2002 +********************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 < ðertype_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 +#include +#include + +#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 +#include +#include + +/* 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, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * + * 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 + +/* + * 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 + +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 +#include +#include +#include + +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 + * + * 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 +#include +#include + +#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 + +#ifndef __ASSEMBLY__ +#ifdef __KERNEL__ + +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +#endif + +#include + +#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 +#include +#include +#include +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" +#include +#include +#include + +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<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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 ≈ + 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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include + +/* 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"); -- cgit v1.2.3