Initial revision
diff --git a/ebtables.c b/ebtables.c
new file mode 100644
index 0000000..e28fd96
--- /dev/null
+++ b/ebtables.c
@@ -0,0 +1,1655 @@
+/*
+ * ebtables.c, v2.0 April 2002
+ *
+ * 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 <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h> // the database
+#include <netinet/in.h>
+#include <asm/types.h>
+#include "include/ebtables_u.h"
+
+// here are the number-name correspondences kept for the ethernet
+// frame type field
+#define PROTOCOLFILE "/etc/ethertypes"
+
+#define DATABASEHOOKNR NF_BR_NUMHOOKS
+#define DATABASEHOOKNAME "DB"
+
+static char *prog_name = PROGNAME;
+static char *prog_version = PROGVERSION;
+char* 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"
+};
+
+// default command line options
+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' },
+	{ "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' },
+	{ "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' },
+	{ 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+// yup, all the possible target names
+char* standard_targets[NUM_STANDARD_TARGETS] = {
+	"ACCEPT",
+	"DROP",
+	"CONTINUE",
+};
+
+unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+
+// tells what happened to the old rules
+static unsigned short *counterchanges;
+// holds all the data
+static struct ebt_u_replace replace;
+
+// the chosen table
+static struct ebt_u_table *table = NULL;
+// the lists of supported tables, matches, watchers and targets
+static struct ebt_u_table *tables = NULL;
+static struct ebt_u_match *matches = NULL;
+static struct ebt_u_watcher *watchers = NULL;
+static struct ebt_u_target *targets = NULL;
+
+struct ebt_u_target *find_target(const char *name)
+{
+	struct ebt_u_target *t = targets;
+
+	while(t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+struct ebt_u_match *find_match(const char *name)
+{
+	struct ebt_u_match *m = matches;
+
+	while(m && strcmp(m->name, name))
+		m = m->next;
+	return m;
+}
+
+struct ebt_u_watcher *find_watcher(const char *name)
+{
+	struct ebt_u_watcher *w = watchers;
+
+	while(w && strcmp(w->name, name))
+		w = w->next;
+	return w;
+}
+
+struct ebt_u_table *find_table(char *name)
+{
+	struct ebt_u_table *t = tables;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+// The pointers in here are special:
+// The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
+// instead of making yet a few other structs, we just do a cast.
+// 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.
+// Same holds for the struct ebt_match and struct ebt_watcher pointers
+struct ebt_u_entry *new_entry;
+
+void 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;
+	// the init function of the standard target should have put the verdict
+	// on CONTINUE
+	e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+	if (!e->t)
+		print_bug("Couldn't load standard target\n");
+}
+
+// this doesn't free e, becoz the calling function might need e->next
+void 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);
+}
+
+// the user will use the match, so put it in new_entry
+static void add_match(struct ebt_u_match *m)
+{
+	struct ebt_u_match_list **m_list, *new;
+
+	m->used = 1;
+	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)
+		print_memory();
+	*m_list = new;
+	new->next = NULL;
+	new->m = (struct ebt_entry_match *)m;
+}
+
+static void add_watcher(struct ebt_u_watcher *w)
+{
+	struct ebt_u_watcher_list **w_list;
+	struct ebt_u_watcher_list *new;
+
+	w->used = 1;
+	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)
+		print_memory();
+	*w_list = new;
+	new->next = NULL;
+	new->w = (struct ebt_entry_watcher *)w;
+}
+
+static int global_option_offset = 0;
+#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)
+		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)
+		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;
+}
+
+void register_match(struct ebt_u_match *m)
+{
+	int size = m->size + sizeof(struct ebt_entry_match);
+	struct ebt_u_match **i;
+
+	m->m = (struct ebt_entry_match *)malloc(size);
+	if (!m->m)
+		print_memory();
+	strcpy(m->m->u.name, m->name);
+	m->m->match_size = m->size;
+	ebt_options = merge_options
+	   (ebt_options, m->extra_ops, &(m->option_offset));
+	m->init(m->m);
+
+	for (i = &matches; *i; i = &((*i)->next));
+	m->next = NULL;
+	*i = m;
+}
+
+void register_watcher(struct ebt_u_watcher *w)
+{
+	int size = w->size + sizeof(struct ebt_entry_watcher);
+	struct ebt_u_watcher **i;
+
+	w->w = (struct ebt_entry_watcher *)malloc(size);
+	if (!w->w)
+		print_memory();
+	strcpy(w->w->u.name, w->name);
+	w->w->watcher_size = w->size;
+	ebt_options = merge_options
+	   (ebt_options, w->extra_ops, &(w->option_offset));
+	w->init(w->w);
+
+	for (i = &watchers; *i; i = &((*i)->next));
+	w->next = NULL;
+	*i = w;
+}
+
+void register_target(struct ebt_u_target *t)
+{
+	int size = t->size + sizeof(struct ebt_entry_target);
+	struct ebt_u_target **i;
+
+	t->t = (struct ebt_entry_target *)malloc(size);
+	if (!t->t)
+		print_memory();
+	strcpy(t->t->u.name, t->name);
+	t->t->target_size = t->size;
+	ebt_options = merge_options
+	   (ebt_options, t->extra_ops, &(t->option_offset));
+	t->init(t->t);
+	for (i = &targets; *i; i = &((*i)->next));
+	t->next = NULL;
+	*i = t;
+}
+
+void register_table(struct ebt_u_table *t)
+{
+	t->next = tables;
+	tables = t;
+}
+
+// used to parse /etc/etherproto
+int disregard_whitespace(char *buffer, FILE *ifp)
+{
+	int hlp;
+	buffer[0] = '\t';
+	while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+		hlp = fscanf(ifp, "%c", buffer);
+		if (hlp == EOF || hlp == 0) return -1;
+	}
+	return 0;
+}
+
+// used to parse /etc/etherproto
+int disregard_tabspace(char *buffer, FILE *ifp)
+{
+	int hlp;
+	buffer[0] = '\t';
+	while (buffer[0] == '\t' || buffer[0] == ' ') {
+		hlp = fscanf(ifp, "%c", buffer);
+		if (hlp == EOF || hlp == 0) return -1;
+	}
+	return 0;
+}
+
+// helper function: processes a line of data from the file /etc/etherproto
+int get_a_line(char *buffer, char *value, FILE *ifp)
+{
+	int i, hlp;
+	char anotherhlp;
+
+	/* discard comment lines && whitespace*/
+	while (1) {
+		if (disregard_whitespace(buffer, ifp)) return -1;
+		if (buffer[0] == '#')
+			while (1) {
+				hlp = fscanf(ifp, "%c", &anotherhlp);
+				if (!hlp || hlp == EOF)
+					return -1;
+				if (anotherhlp == '\n')
+					break;
+			}
+		else break;
+	}
+
+	// buffer[0] already contains the first letter
+	for (i = 1; i < 21; i++) {
+		hlp = fscanf(ifp, "%c", buffer + i);
+		if (hlp == EOF || hlp == 0) return -1;
+		if (buffer[i] == '\t' || buffer[i] == ' ')
+			break;
+	}
+	if (i == 21) return -1;
+	buffer[i] = '\0';
+	if (disregard_tabspace(value, ifp))
+		return -1;
+	// maybe I should allow 0x0800 instead of 0800, but I'm feeling lazy
+	// buffer[0] already contains the first letter
+	for (i = 1; i < 5; i++) {
+		hlp = fscanf(ifp, "%c", value+i);
+		if (value[i] == '\n' || value[i] == '\t' ||
+		   value[i] == ' ' || hlp == EOF)
+			break;
+	}
+	if (i == 5) return -1;
+	/* discard comments at the end of a line */
+	if (value[i] == '\t' || value[i] == ' ')
+		while (1) {
+			hlp = fscanf(ifp, "%c", &anotherhlp);
+			if (!hlp || hlp == EOF || anotherhlp == '\n')
+				break;
+		}
+	value[i] = '\0';
+	return 0;
+}
+
+// helper function for list_em()
+int number_to_name(unsigned short proto, char *name)
+{
+	FILE *ifp;
+	char buffer[21], value[5], *bfr;
+	unsigned short i;
+
+	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+		return -1;
+	while (1) {
+		if (get_a_line(buffer, value, ifp)) {
+			fclose(ifp);
+			return -1;
+		}
+		i = (unsigned short) strtol(value, &bfr, 16);
+		if (*bfr != '\0' || i != proto)
+			continue;
+		strcpy(name, buffer);
+		fclose(ifp);
+		return 0;
+	}
+}
+
+// helper function for list_rules()
+static void list_em(int hooknr)
+{
+	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;
+	char name[21];
+
+	hlp = replace.hook_entry[hooknr]->entries;
+	printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
+	   standard_targets[replace.hook_entry[hooknr]->policy]);
+	printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
+
+	i = replace.hook_entry[hooknr]->nentries;
+	while (i >9) {
+		space++;
+		i /= 10;
+	}
+
+	for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
+		digits = 0;
+		// A little work to get nice rule numbers.
+		while (j > 9) {
+			digits++;
+			j /= 10;
+		}
+		for (j = 0; j < space - digits; j++)
+			printf(" ");
+		printf("%d. ", i + 1);
+
+		// 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("eth proto: ");
+			if (hlp->invflags & EBT_IPROTO)
+				printf("! ");
+			if (hlp->bitmask & EBT_802_3)
+				printf("Length, ");
+			else {
+				if (number_to_name(ntohs(hlp->ethproto), name))
+					printf("0x%x, ", ntohs(hlp->ethproto));
+				else
+					printf("%s, ", name);
+			}
+		}
+		if (hlp->bitmask & EBT_SOURCEMAC) {
+			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+			printf("source mac: ");
+			if (hlp->invflags & EBT_ISOURCE)
+				printf("! ");
+			if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_unicast, 6)) {
+				printf("Unicast");
+				goto endsrc;
+			}
+			if (!memcmp(hlp->sourcemac, mac_type_multicast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_multicast, 6)) {
+				printf("Multicast");
+				goto endsrc;
+			}
+			if (!memcmp(hlp->sourcemac, mac_type_broadcast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_broadcast, 6)) {
+				printf("Broadcast");
+				goto endsrc;
+			}
+			for (j = 0; j < ETH_ALEN; j++)
+				printf("%02x%s", hlp->sourcemac[j],
+				   (j == ETH_ALEN - 1) ? "" : ":");
+			if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
+				printf("/");
+				for (j = 0; j < ETH_ALEN; j++)
+					printf("%02x%s", hlp->sourcemsk[j],
+					   (j == ETH_ALEN - 1) ? "" : ":");
+			}
+endsrc:
+			printf(", ");
+		}
+		if (hlp->bitmask & EBT_DESTMAC) {
+			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+			printf("dest mac: ");
+			if (hlp->invflags & EBT_IDEST)
+				printf("! ");
+			if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_unicast, 6)) {
+				printf("Unicast");
+				goto enddst;
+			}
+			if (!memcmp(hlp->destmac, mac_type_multicast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_multicast, 6)) {
+				printf("Multicast");
+				goto enddst;
+			}
+			if (!memcmp(hlp->destmac, mac_type_broadcast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_broadcast, 6)) {
+				printf("Broadcast");
+				goto enddst;
+			}
+			for (j = 0; j < ETH_ALEN; j++)
+				printf("%02x%s", hlp->destmac[j],
+				   (j == ETH_ALEN - 1) ? "" : ":");
+			if (memcmp(hlp->destmsk, hlpmsk, 6)) {
+				printf("/");
+				for (j = 0; j < ETH_ALEN; j++)
+					printf("%02x%s", hlp->destmsk[j],
+					   (j == ETH_ALEN - 1) ? "" : ":");
+			}
+enddst:
+			printf(", ");
+		}
+		if (hlp->in[0] != '\0') {
+			if (hlp->invflags & EBT_IIN)
+				printf("! ");
+			printf("in-if: %s, ", hlp->in);
+		}
+		if (hlp->logical_in[0] != '\0') {
+			if (hlp->invflags & EBT_ILOGICALIN)
+				printf("! ");
+			printf("logical in-if: %s, ", hlp->logical_in);
+		}
+		if (hlp->logical_out[0] != '\0') {
+			if (hlp->invflags & EBT_ILOGICALOUT)
+				printf("! ");
+			printf("logical out-if: %s, ", hlp->logical_out);
+		}
+		if (hlp->out[0] != '\0') {
+			if (hlp->invflags & EBT_IOUT)
+				printf("! ");
+			printf("out-if: %s, ", hlp->out);
+		}
+
+		m_l = hlp->m_list;
+		while (m_l) {
+			m = find_match(m_l->m->u.name);
+			if (!m)
+				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 = find_watcher(w_l->w->u.name);
+			if (!w)
+				print_bug("Watcher not found");
+			w->print(hlp, w_l->w);
+			w_l = w_l->next;
+		}
+
+		printf("target: ");
+		t = find_target(hlp->t->u.name);
+		if (!t)
+			print_bug("Target not found");
+		t->print(hlp, hlp->t);
+		printf(", count = %llu",
+		   replace.counters[replace.counter_entry[hooknr] + i].pcnt);
+		printf("\n");
+		hlp = hlp->next;
+	}
+}
+
+// parse the chain name and return the corresponding nr
+int get_hooknr(char* arg)
+{
+	int i;
+
+	// database is special case (not really a chain)
+	if (!strcmp(arg, DATABASEHOOKNAME))
+		return DATABASEHOOKNR;
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (!strcmp(arg, hooknames[i]))
+			return i;
+	return -1;
+}
+
+// yup, print out help
+void print_help()
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	printf(
+"%s v%s\n"
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[b] [y,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"
+"--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"
+"--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
+"--flush  -F [chain]           : Delete all rules in chain or in all chains\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"
+"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"
+"--version -V                  : print package version\n"
+"\n" ,
+	prog_name,
+	prog_version);
+
+	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(hooknames);
+	exit(0);
+}
+
+// execute command L
+static void list_rules()
+{
+	int i;
+
+	printf("Bridge table: %s\n", table->name);
+	if (replace.selected_hook != -1) list_em(replace.selected_hook);
+	else
+		for (i = 0; i < NF_BR_NUMHOOKS; i++)
+			if (replace.valid_hooks & (1 << i))
+				list_em(i);
+	return;
+}
+
+// execute command P
+static void change_policy(int policy)
+{
+	int i;
+
+	// don't do anything if the policy is the same
+	if (replace.hook_entry[replace.selected_hook]->policy != policy) {
+		replace.hook_entry[replace.selected_hook]->policy = policy;
+		replace.num_counters = replace.nentries;
+		if (replace.nentries) {
+			// '+ 1' for the CNT_END
+			if (!(counterchanges = (unsigned short *) malloc(
+			   (replace.nentries + 1) * sizeof(unsigned short))))
+				print_memory();
+			// done nothing special to the rules
+			for (i = 0; i < replace.nentries; i++)
+				counterchanges[i] = CNT_NORM;
+			counterchanges[replace.nentries] = CNT_END;
+		}
+		else
+			counterchanges = NULL;
+	}
+	else
+		exit(0);
+}
+
+// flush one chain or the complete table
+static void flush_chains()
+{
+	int i, j, oldnentries;
+	unsigned short *cnt;
+	struct ebt_u_entry *u_e, *tmp;
+
+	// flush whole table
+	if (replace.selected_hook == -1) {
+		if (replace.nentries == 0)
+			exit(0);
+		replace.nentries = 0;
+		// no need for the kernel to give us counters back
+		replace.num_counters = 0;
+		// free everything and zero (n)entries
+		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			replace.hook_entry[i]->nentries = 0;
+			u_e = replace.hook_entry[i]->entries;
+			while (u_e) {
+				free_u_entry(u_e);
+				tmp = u_e->next;
+				free(u_e);
+				u_e = tmp;
+			}
+			replace.hook_entry[i]->entries = NULL;
+		}
+		return;
+	}
+
+	if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+		exit(0);
+	oldnentries = replace.nentries;
+	replace.nentries = replace.nentries -
+	   replace.hook_entry[replace.selected_hook]->nentries;
+
+	// delete the counters belonging to the specified chain
+	if (replace.nentries) {
+		// +1 for CNT_END
+		if ( !(counterchanges = (unsigned short *)
+		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+			print_memory();
+		cnt = counterchanges;
+		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+				if (i != replace.selected_hook)
+					*cnt = CNT_NORM;
+				else
+					*cnt = CNT_DEL;
+				cnt++;
+			}
+		}
+		*cnt = CNT_END;
+		replace.num_counters = oldnentries;
+	}
+	else
+		replace.num_counters = 0;
+
+	replace.hook_entry[replace.selected_hook]->nentries = 0;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	while (u_e) {
+		free_u_entry(u_e);
+		tmp = u_e->next;
+		free(u_e);
+		u_e = tmp;
+	}
+	replace.hook_entry[replace.selected_hook]->entries = NULL;
+}	
+
+// -1 == no match
+static int check_rule_exists(int rule_nr)
+{
+	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;
+	int i, j, k;
+
+	// handle '-D chain rulenr' command
+	if (rule_nr != -1) {
+		if (rule_nr >
+		   replace.hook_entry[replace.selected_hook]->nentries)
+			return 0;
+		// user starts counting from 1
+		return rule_nr - 1;
+	}
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	// check for an existing rule (if there are duplicate rules,
+	// take the first occurance)
+	for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
+	   i++, u_e = u_e->next) {
+		if (!u_e)
+			print_bug("Hmm, trouble");
+		if ( u_e->ethproto == new_entry->ethproto
+		   && !strcmp(u_e->in, new_entry->in)
+		   && !strcmp(u_e->out, new_entry->out)
+		   && u_e->bitmask == new_entry->bitmask) {
+			if (new_entry->bitmask & EBT_SOURCEMAC &&
+			   strcmp(u_e->sourcemac, new_entry->sourcemac))
+				continue;
+			if (new_entry->bitmask & EBT_DESTMAC &&
+			   strcmp(u_e->destmac, new_entry->destmac))
+				continue;
+			if (new_entry->bitmask != u_e->bitmask ||
+			   new_entry->invflags != u_e->invflags)
+				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;
+}
+
+// execute command A
+static void add_rule(int rule_nr)
+{
+	int i, j;
+	struct ebt_u_entry *u_e, *u_e2;
+	unsigned short *cnt;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	if (rule_nr != -1) { // command -I
+		if (--rule_nr >
+		   replace.hook_entry[replace.selected_hook]->nentries)
+			print_error("rule nr too high: %d > %d", rule_nr,
+			   replace.hook_entry[replace.selected_hook]->nentries);
+	} else
+		rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
+	// we're adding one rule
+	replace.num_counters = replace.nentries;
+	replace.nentries++;
+	replace.hook_entry[replace.selected_hook]->nentries++;
+
+	// handle counter stuff
+	// +1 for CNT_END
+	if ( !(counterchanges = (unsigned short *)
+	   malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+		print_memory();
+	cnt = counterchanges;
+	for (i = 0; i < replace.selected_hook; i++) {
+		if (!(replace.valid_hooks & (1 << i)))
+			continue;
+		for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+	}
+	for (i = 0; i < rule_nr; i++) {
+		*cnt = CNT_NORM;
+		cnt++;
+	}
+	*cnt = CNT_ADD;
+	cnt++;
+	while (cnt != counterchanges + replace.nentries) {
+		*cnt = CNT_NORM;
+		cnt++;
+	}
+	*cnt = CNT_END;
+
+	// go to the right position in the chain
+	u_e2 = NULL;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	for (i = 0; i < rule_nr; i++) {
+		u_e2 = u_e;
+		u_e = u_e->next;
+	}
+	// insert the rule
+	if (u_e2)
+		u_e2->next = new_entry;
+	else
+		replace.hook_entry[replace.selected_hook]->entries = new_entry;
+	new_entry->next = u_e;
+
+	// 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;
+}
+
+// execute command D
+static void delete_rule(int rule_nr)
+{
+	int i, j, lentmp = 0;
+	unsigned short *cnt;
+	struct ebt_u_entry *u_e, *u_e2;
+
+	if ( (i = check_rule_exists(rule_nr)) == -1 )
+		print_error("Sorry, rule does not exists");
+
+	// we're deleting a rule
+	replace.num_counters = replace.nentries;
+	replace.nentries--;
+
+	if (replace.nentries) {
+		for (j = 0; j < replace.selected_hook; j++) {
+			if (!(replace.valid_hooks & (1 << j)))
+				continue;
+			lentmp += replace.hook_entry[j]->nentries;
+		}
+		lentmp += i;
+		// +1 for CNT_END
+		if ( !(counterchanges = (unsigned short *)malloc(
+		   (replace.num_counters + 1) * sizeof(unsigned short))) )
+			print_memory();
+		cnt = counterchanges;
+		for (j = 0; j < lentmp; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_DEL;
+		cnt++;
+		for (j = 0; j < replace.num_counters - lentmp; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_END;
+	}
+	else
+		replace.num_counters = 0;
+
+	// go to the right position in the chain
+	u_e2 = NULL;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	for (j = 0; j < i; j++) {
+		u_e2 = u_e;
+		u_e = u_e->next;
+	}
+
+	// remove from the chain
+	if (u_e2)
+		u_e2->next = u_e->next;
+	else
+		replace.hook_entry[replace.selected_hook]->entries = u_e->next;
+
+	replace.hook_entry[replace.selected_hook]->nentries--;
+	// free everything
+	free_u_entry(u_e);
+	free(u_e);
+}
+
+// execute command Z
+void zero_counters(int zerochain)
+{
+
+	if (zerochain == -1) {
+		// tell main() we don't update the counters
+		// this results in tricking the kernel to zero his counters,
+		// naively expecting userspace to update its counters. Muahahaha
+		counterchanges = NULL;
+		replace.num_counters = 0;
+	} else {
+		int i, j;
+		unsigned short *cnt;
+
+		if (replace.hook_entry[zerochain]->nentries == 0)
+			exit(0);
+		counterchanges = (unsigned short *)
+		   malloc((replace.nentries + 1) * sizeof(unsigned short));
+		if (!counterchanges)
+			print_memory();
+		cnt = counterchanges;
+		for (i = 0; i < zerochain; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+				*cnt = CNT_NORM;
+				cnt++;
+			}
+		}
+		for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
+			*cnt = CNT_ZERO;
+			cnt++;
+		}
+		while (cnt != counterchanges + replace.nentries) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_END;
+	}
+}
+
+// list the database (optionally compiled into the kernel)
+static void list_db()
+{
+	struct brdb_dbinfo nr;
+	struct brdb_dbentry *db;
+	char name[21];
+	int i;
+
+	get_dbinfo(&nr);
+
+	// 0 : database disabled (-db n)
+	if (!(nr.nentries))
+		print_error("Database not present"
+		            " (disabled), try ebtables --db y");
+	nr.nentries--;
+	if (!nr.nentries) print_error("Database empty");
+	if ( !(db = (struct brdb_dbentry *)
+	   malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
+		print_memory();
+
+	get_db(nr.nentries, db);
+	printf("number of entries: %d\n", nr.nentries);
+	for (i = 0; i < nr.nentries; i++) {
+		printf(
+		"%d:\n"
+		"hook    : %s\n"
+		"in-if   : %s\n"
+		"out-if  : %s\n"
+		"protocol: ", i + 1, hooknames[db->hook], db->in, db->out);
+		if (db->ethproto == IDENTIFY802_3)
+			printf("802.2/802.3 STYLE LENGTH FIELD\n");
+		else {
+			if (number_to_name(ntohs(db->ethproto), name))
+				printf("%x\n",ntohs(db->ethproto));
+			else
+				printf("%s\n", name);
+		}
+		db++;
+	}
+	exit(0);
+}
+
+// handle db [dis,en]abling
+static void allowdb(char yorn)
+{
+	__u16 decision;
+
+	if (yorn != 'y' && yorn != 'n')
+		print_error("Option [y] or [n] needed");
+
+	if (yorn == 'y')
+		decision = BRDB_DB;
+	else
+		decision = BRDB_NODB;
+
+	deliver_allowdb(&decision);
+
+	exit(0);
+}
+
+// set ethproto
+int name_to_protocol(char *name)
+{
+	FILE *ifp;
+	char buffer[21], value[5], *bfr;
+	unsigned short i;
+
+	if (!strcasecmp("LENGTH", name)) {
+		new_entry->ethproto = 0;
+		new_entry->bitmask |= EBT_802_3;
+		return 1;
+	}
+	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+		return -1;
+	while (1) {
+		if (get_a_line(buffer, value, ifp)) return -1;
+		if (strcasecmp(buffer, name))
+			continue;
+		i = (unsigned short) strtol(value, &bfr, 16);
+		if (*bfr != '\0')
+			return -1;
+		new_entry->ethproto = i;
+		fclose(ifp);
+		return 0;
+	}
+	return -1;
+}
+
+// put the mac address into 6 (ETH_ALEN) bytes
+int getmac(char *from, char *to)
+{
+	int i, tmp;
+	char *buffer;
+
+	if (strlen(from) != 3 * ETH_ALEN - 1)
+		return -1;
+	for (i = 1; i < ETH_ALEN; i++) {
+		if (from[i*3 - 1] != ':')
+			return -1;
+		from[i*3 - 1] = '\0';
+	}
+	for (i = 0; i < ETH_ALEN; i++) {
+		tmp = strtol(from + i*3, &buffer, 16);
+		if (*buffer != '\0' || tmp > 255 || tmp < 0)
+			return -1;
+		to[i] = (unsigned char) tmp;
+	}
+	return 0;
+}
+
+int getmac_and_mask(char *from, char *to, char *mask)
+{
+	char *p;
+	int i;
+
+	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 ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		if (getmac(p + 1, mask))
+			return -1;
+	} else
+		memset(mask, 0xff, ETH_ALEN);
+	if (getmac(from, to))
+		return -1;
+	for (i = 0; i < ETH_ALEN; i++)
+		to[i] &= mask[i];
+	return 0;
+}
+
+int check_inverse(const char option[])
+{
+	if (strcmp(option, "!") == 0) {
+		optind++;
+		return 1;
+	}
+	return 0;
+}
+
+void check_option(unsigned int *flags, unsigned int mask)
+{
+	if (*flags & mask)
+		print_error("Multiple use of same option not allowed");
+	*flags |= mask;
+}
+
+#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
+// the main thing
+int main(int argc, char *argv[])
+{
+	char *buffer, allowbc = 'n';
+	int c, i;
+	// this special one for the -Z option (we can have -Z <this> -L <that>)
+	int zerochain = -1;
+	int policy = -1;
+	int rule_nr = -1;// used for -D chain number
+	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;
+
+	// initialize the table name, OPT_ flags, selected hook and command
+	strcpy(replace.name, "filter");
+	replace.flags = 0;
+	replace.selected_hook = -1;
+	replace.command = 'h';
+
+	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+	if (!new_entry)
+		print_memory();
+	// put some sane values in our new entry
+	initialize_entry(new_entry);
+
+	// getopt saves the day
+	while ((c = getopt_long(argc, argv,
+	   "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
+		switch (c) {
+
+		case 'A': // add a rule
+		case 'D': // delete a rule
+		case 'P': // define policy
+		case 'I': // insert a rule
+			replace.command = c;
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.flags |= OPT_COMMAND;
+			if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+				print_error("Bad chain");
+			if (c == 'D' && optind < argc &&
+			   argv[optind][0] != '-') {
+				rule_nr = strtol(argv[optind], &buffer, 10);
+				if (*buffer != '\0' || rule_nr < 0)
+					print_error("Problem with the "
+					            "specified rule number");
+				optind++;
+			}
+			if (c == 'P') {
+				if (optind >= argc)
+					print_error("No policy specified");
+				for (i = 0; i < 2; i++)
+					if (!strcmp(argv[optind],
+					   standard_targets[i])) {
+						policy = i;
+						break;
+					}
+				if (policy == -1)
+					print_error("Wrong policy");
+				optind++;
+			}
+			if (c == 'I') {
+				if (optind >= argc)
+					print_error("No rulenr for -I"
+					            " specified");
+				rule_nr = strtol(argv[optind], &buffer, 10);
+				if (*buffer != '\0' || rule_nr < 0)
+					print_error("Problem with the specified"
+					            " rule number");
+				optind++;
+			}
+			break;
+
+		case 'L': // list
+		case 'F': // flush
+		case 'Z': // zero counters
+			if (c == 'Z') {
+				if (replace.flags & OPT_ZERO)
+					print_error("Multiple commands"
+					            " not allowed");
+				if ( (replace.flags & OPT_COMMAND &&
+				   replace.command != 'L'))
+					print_error("command -Z only allowed "
+					            "together with command -L");
+				replace.flags |= OPT_ZERO;
+			} else {
+				replace.command = c;
+				if (replace.flags & OPT_COMMAND)
+					print_error("Multiple commands"
+					            " not allowed");
+				replace.flags |= OPT_COMMAND;
+			}
+			i = -1;
+			if (optarg) {
+				if ( (i = get_hooknr(optarg)) == -1 )
+					print_error("Bad chain");
+			} else
+				if (optind < argc && argv[optind][0] != '-') {
+					if ((i = get_hooknr(argv[optind]))
+					   == -1)
+						print_error("Bad chain");
+					optind++;
+				}
+			if (i != -1) {
+				if (c == 'Z')
+					zerochain = i;
+				else
+					replace.selected_hook = i;
+			}
+			break;
+
+		case 'V': // version
+			replace.command = 'V';
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			printf("%s, %s\n", prog_name, prog_version);
+			exit(0);
+
+		case 'h': // help
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands 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 ((m = find_match(argv[optind])))
+					add_match(m);
+				else if ((w = find_watcher(argv[optind])))
+					add_watcher(w);
+				else {
+					if (!(t = find_target(argv[optind])))
+						print_error("Extension %s "
+						   "not found", argv[optind]);
+					if (replace.flags & OPT_JUMP)
+						print_error("Sorry, you can "
+						 "only see help for one "
+						 "target extension each time");
+					replace.flags |= OPT_JUMP;
+					new_entry->t =
+					   (struct ebt_entry_target *)t;
+				}
+				optind++;
+			}
+			break;
+
+		case 't': // table
+			check_option(&replace.flags, OPT_TABLE);
+			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
+				print_error("Table name too long");
+			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
+			if ((replace.flags & OPT_COMMAND) == 0)
+				print_error("No command specified");
+			if ( replace.command != 'A' &&
+			   replace.command != 'D' && replace.command != 'I')
+				print_error("Command and option do not match");
+			if (c == 'i') {
+				check_option(&replace.flags, OPT_IN);
+				if (replace.selected_hook > 2 &&
+				   replace.selected_hook < NF_BR_BROUTING)
+					print_error("Use in-interface only in "
+					   "INPUT, FORWARD, PREROUTING and"
+					   "BROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IIN;
+
+				if (optind > argc)
+					print_error("No in-interface "
+					            "specified");
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interfacelength");
+				strcpy(new_entry->in, argv[optind - 1]);
+				break;
+			}
+			if (c == 2) {
+				check_option(&replace.flags, OPT_LOGICALIN);
+				if (replace.selected_hook > 2 &&
+				   replace.selected_hook < NF_BR_BROUTING)
+					print_error("Use logical in-interface "
+					   "only in INPUT, FORWARD, "
+					   "PREROUTING and BROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ILOGICALIN;
+
+				if (optind > argc)
+					print_error("No logical in-interface "
+					            "specified");
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interfacelength");
+				strcpy(new_entry->logical_in, argv[optind - 1]);
+				break;
+			}
+			if (c == 'o') {
+				check_option(&replace.flags, OPT_OUT);
+				if (replace.selected_hook < 2)
+					print_error("Use out-interface only"
+					   " in OUTPUT, FORWARD and "
+					   "POSTROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IOUT;
+
+				if (optind > argc)
+					print_error("No out-interface "
+					            "specified");
+
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interface "
+					            "length");
+				strcpy(new_entry->out, argv[optind - 1]);
+				break;
+			}
+			if (c == 3) {
+				check_option(&replace.flags, OPT_LOGICALOUT);
+				if (replace.selected_hook < 2)
+					print_error("Use logical out-interface "
+					   "only in OUTPUT, FORWARD and "
+					   "POSTROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ILOGICALOUT;
+
+				if (optind > argc)
+					print_error("No logical out-interface "
+					            "specified");
+
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interface "
+					            "length");
+				strcpy(new_entry->logical_out,
+				   argv[optind - 1]);
+				break;
+			}
+			if (c == 'j') {
+
+				check_option(&replace.flags, OPT_JUMP);
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(optarg,
+					   standard_targets[i])) {
+						t = find_target(
+						   EBT_STANDARD_TARGET);
+						((struct ebt_standard_target *)
+						   t->t)->verdict = i;
+						break;
+					}
+				// must be an extension then
+				if (i == NUM_STANDARD_TARGETS) {
+					struct ebt_u_target *t;
+					t = find_target(optarg);
+					// -j standard not allowed either
+					if (!t || t ==
+					   (struct ebt_u_target *)new_entry->t)
+						print_error("Illegal target "
+						            "name");
+					new_entry->t =
+					   (struct ebt_entry_target *)t;
+				}
+				break;
+			}
+			if (c == 's') {
+				check_option(&replace.flags, OPT_SOURCE);
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ISOURCE;
+
+				if (optind > argc)
+					print_error("No source mac "
+					            "specified");
+				if (getmac_and_mask(argv[optind - 1],
+				   new_entry->sourcemac, new_entry->sourcemsk))
+					print_error("Problem with specified "
+					            "source mac");
+				new_entry->bitmask |= EBT_SOURCEMAC;
+				break;
+			}
+			if (c == 'd') {
+				check_option(&replace.flags, OPT_DEST);
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IDEST;
+
+				if (optind > argc)
+					print_error("No destination mac "
+					            "specified");
+				if (getmac_and_mask(argv[optind - 1],
+				   new_entry->destmac, new_entry->destmsk))
+					print_error("Problem with specified "
+					            "destination mac");
+				new_entry->bitmask |= EBT_DESTMAC;
+				break;
+			}
+			check_option(&replace.flags, OPT_PROTOCOL);
+			if (check_inverse(optarg))
+				new_entry->invflags |= EBT_IPROTO;
+
+			if (optind > argc)
+				print_error("No protocol specified");
+			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+			i = strtol(argv[optind - 1], &buffer, 16);
+			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+				print_error("Problem with the specified "
+				            "protocol");
+			new_entry->ethproto = i;
+			if (*buffer != '\0')
+				if (name_to_protocol(argv[optind - 1]) == -1)
+					print_error("Problem with the specified"
+					            " protocol");
+			if (new_entry->ethproto < 1536 &&
+			   !(new_entry->bitmask & EBT_802_3))
+				print_error("Sorry, protocols have values above"
+				            " or equal to 1536 (0x0600)");
+			break;
+
+		case 'b': // allow database?
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.command = c;
+			allowbc = *optarg;
+			break;
+
+		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)))
+				continue;
+
+			// is it a match_option?
+			for (m = 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 (m->used == 0)
+					add_match(m);
+				continue;
+			}
+
+			// is it a watcher option?
+			for (w = watchers; w; w = w->next)
+				if (w->parse(c-w->option_offset, argv,
+				   argc, new_entry, &w->flags, &w->w))
+					break;
+
+			if (w == NULL)
+				print_error("Unknown argument");
+			if (w->used == 0)
+				add_watcher(w);
+		}
+	}
+
+	// database stuff before ebtables stuff
+	if (replace.command == 'b')
+		allowdb(allowbc);
+	if (replace.command == 'L' && replace.selected_hook == DATABASEHOOKNR)
+		list_db();
+
+	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
+	   replace.flags & OPT_ZERO )
+		print_error("Command -Z only allowed together with command -L");
+
+	if (replace.command == 'A' || replace.command == 'I' ||
+	   replace.command == 'D') {
+		if (replace.selected_hook == -1)
+			print_error("Not enough information");
+	}
+
+	if ( !(table = find_table(replace.name)) )
+		print_error("Bad table name");
+
+	// do this after parsing everything, so we can print specific info
+	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+		print_help();
+
+	// do the final checks
+	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,
+		   replace.selected_hook);
+		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,
+		   replace.selected_hook);
+		w_l = w_l->next;
+	}
+	t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+	
+	// so, the extensions can work with the host endian
+	// the kernel does not have to do this ofcourse
+	new_entry->ethproto = htons(new_entry->ethproto);
+
+	// get the kernel's information
+	get_table(&replace);
+	// check if selected_hook is a valid_hook
+	if (replace.selected_hook >= 0 &&
+	   !(replace.valid_hooks & (1 << replace.selected_hook)))
+		print_error("Bad chain name");
+	if (replace.command == 'P')
+		change_policy(policy);
+	else if (replace.command == 'L') {
+		list_rules();
+		if (replace.flags & OPT_ZERO)
+			zero_counters(zerochain);
+		else
+			exit(0);
+	}
+	if (replace.flags & OPT_ZERO)
+		zero_counters(zerochain);
+	else if (replace.command == 'F')
+		flush_chains();
+	else if (replace.command == 'A' || replace.command == 'I')
+		add_rule(rule_nr);
+	else if (replace.command == 'D')
+		delete_rule(rule_nr);
+
+	if (table->check)
+		table->check(&replace);
+
+	deliver_table(&replace);
+
+	if (counterchanges)
+		deliver_counters(&replace, counterchanges);
+	return 0;
+}