add ebtablesu scheme, along with general cleanup
diff --git a/ChangeLog b/ChangeLog
index d52160e..af1fe66 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+20051701
+	Since last entry:
+	* added ulog watcher
+	* made the ebtables code modular (make library functions).
+	* added the ebtablesd/ebtablesu scheme to allow faster
+	  addition of rules (and to test the modular code).
+	* some small fixes
 20031102
 	Since last entry:
 	* <grzes_at_gnu.univ.gda.pl> added arpreply and among modules
diff --git a/INSTALL b/INSTALL
index 2d1e42f..371cbe4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -17,9 +17,13 @@
   The Makefile will append /man8/ebtables.8.
 - ethertypes is by default placed in /etc/, if you
   want to change this, include ETHERTYPESPATH=<<path>>.
-- the userspace program ebtables is compiled and the executable is copied
-  by default to /usr/sbin/ebtables. If you want to put the executable
-  somewhere else, include BINPATH=<<path>>.
+- The pipe used for communication by ebtablesd and ebtablesu is by
+  default the file /etc/ebtables-v$(PROGVERSION)/ebtablesd_pipe. To
+  change the directory, include PIPE_DIR=<<dir>>. There is no option
+  to change the name of the pipe.
+- the userspace programs ebtables ebtablesu and ebtablesd are compiled and
+  the executables are copied by default to /usr/sbin/ebtables. If you want
+  to put the executables somewhere else, include BINPATH=<<path>>.
 
 That's all
 
diff --git a/Makefile b/Makefile
index e519c4e..671de96 100644
--- a/Makefile
+++ b/Makefile
@@ -38,15 +38,30 @@
 ETHERTYPESPATH?=$(ETCDIR)
 ETHERTYPESFILE:=$(ETHERTYPESPATH)/ethertypes
 
-BINFILE:=$(BINDIR)/ebtables
+PIPE_DIR?=.
+PIPE=$(PIPE_DIR)/ebtablesd_pipe
+EBTD_CMDLINE_MAXLN?=2048
+EBTD_ARGC_MAX?=50
+
+BINFILE_EBT:=$(BINDIR)/$(PROGNAME)
+BINFILE_EBTD:=$(BINDIR)/$(PROGNAME)d
+BINFILE_EBTU:=$(BINDIR)/$(PROGNAME)u
 
 PROGSPECS:=-DPROGVERSION=\"$(PROGVERSION)\" \
 	-DPROGNAME=\"$(PROGNAME)\" \
 	-DPROGDATE=\"$(PROGDATE)\" \
 	-D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\"
 
+PROGSPECSD:=-DPROGVERSION=\"$(PROGVERSION)\" \
+	-DPROGNAME=\"$(PROGNAME)\" \
+	-DPROGDATE=\"$(PROGDATE)\" \
+	-D_PATH_ETHERTYPES=\"$(ETHERTYPESFILE)\" \
+	-DEBTD_CMDLINE_MAXLN=$(EBTD_CMDLINE_MAXLN) \
+	-DEBTD_ARGC_MAX=$(EBTD_ARGC_MAX) \
+	-DEBTD_PIPE=\"$(PIPE)\" \
+	-DEBTD_PIPE_DIR=\"$(PIPE_DIR)\"
 
-all: ebtables
+all: ebtables daemon
 
 communication.o: communication.c include/ebtables_u.h
 	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
@@ -63,11 +78,28 @@
 ebtables.o: ebtables.c include/ebtables_u.h
 	$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< -I$(KERNEL_INCLUDES)
 
-ebtables: $(OBJECTS)
+ebtables-standalone.o: ebtables-standalone.c ebtables.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(PROGSPECS) -c $< ebtables.c -o $@ -I$(KERNEL_INCLUDES)
+
+ebtables: $(OBJECTS) ebtables-standalone.o
 	$(LD) -shared -soname libebtc.so -o libebtc.so -lc $(OBJECTS2)
-	$(CC) $(CFLAGS) -o $@ ebtables.o -I$(KERNEL_INCLUDES) -L/root/ \
+	$(CC) $(CFLAGS) -o $@ ebtables-standalone.o -I$(KERNEL_INCLUDES) -L/root/ \
 	-L. -Lextensions/ -lebtc $(EXT_LIBSI)
-	
+
+ebtablesu: ebtablesu.c
+	$(CC) $(CFLAGS) $(PROGSPECSD) $< -o $@
+
+ebtablesd.o: ebtablesd.c ebtables.c include/ebtables_u.h
+	$(CC) $(CFLAGS) $(PROGSPECSD) -c $< ebtables.c -o $@  -I$(KERNEL_INCLUDES)
+
+ebtablesd: $(OBJECTS) ebtablesd.o
+	$(LD) -shared -soname libebtc.so -o libebtc.so -lc $(OBJECTS2)
+	$(CC) $(CFLAGS) -o $@ ebtablesd.o -I$(KERNEL_INCLUDES) -L/root/ \
+	-L. -Lextensions/ -lebtc $(EXT_LIBSI)
+
+.PHONY: daemon
+daemon: ebtablesd ebtablesu
+
 $(MANDIR)/man8/ebtables.8: ebtables.8
 	mkdir -p $(@D)
 	install -m 0644 -o root -g root $< $@
@@ -77,9 +109,11 @@
 	install -m 0644 -o root -g root $< $@
 
 .PHONY: exec
-exec: ebtables
+exec: ebtables daemon
 	mkdir -p $(BINDIR)
-	install -m 0755 -o root -g root $< $(BINFILE)
+	install -m 0755 -o root -g root $(PROGNAME) $(BINFILE_EBT)
+	install -m 0755 -o root -g root $(PROGNAME)d  $(BINFILE_EBTD)
+	install -m 0755 -o root -g root $(PROGNAME)u $(BINFILE_EBTU)
 
 .PHONY: install
 install: $(MANDIR)/man8/ebtables.8 $(ETHERTYPESFILE) exec
@@ -89,7 +123,7 @@
 
 .PHONY: clean
 clean:
-	rm -f ebtables
+	rm -f ebtables ebtablesd ebtablesu
 	rm -f *.o *.c~ *.so
 	rm -f extensions/*.o extensions/*.c~ extensions/*.so
 
diff --git a/communication.c b/communication.c
index af00fa2..81fca22 100644
--- a/communication.c
+++ b/communication.c
@@ -31,15 +31,19 @@
 
 int sockfd = -1;
 
-static void get_sockfd()
+static int get_sockfd()
 {
+	int ret = 0;
 	if (sockfd == -1) {
 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
-		if (sockfd < 0)
+		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)
@@ -62,7 +66,7 @@
 	new->nentries = u_repl->nentries;
 	new->num_counters = u_repl->num_counters;
 	new->counters = sparc_cast u_repl->counters;
-	/* determine nr of udc */
+	/* Determine nr of udc */
 	i = 0;
 	cl = u_repl->udc;
 	while (cl) {
@@ -71,7 +75,7 @@
 	}
 	i += NF_BR_NUMHOOKS;
 	chain_offsets = (unsigned int *)malloc(i * sizeof(unsigned int));
-	/* determine size */
+	/* Determine size */
 	i = 0;
 	cl = u_repl->udc;
 	while (1) {
@@ -109,7 +113,7 @@
 			   sizeof(struct ebt_entry_target);
 			e = e->next;
 		}
-		/* a little sanity check */
+		/* A little sanity check */
 		if (j != entries->nentries)
 			ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j,
 			   entries->nentries, entries->name);
@@ -123,7 +127,7 @@
 	if (!p)
 		ebt_print_memory();
 
-	/* put everything in one block */
+	/* Put everything in one block */
 	new->entries = sparc_cast p;
 	i = 0;
 	cl = u_repl->udc;
@@ -147,7 +151,7 @@
 		hlp->policy = entries->policy;
 		strcpy(hlp->name, entries->name);
 		hlp->counter_offset = entries->counter_offset;
-		hlp->distinguisher = 0; /* make the kernel see the light */
+		hlp->distinguisher = 0; /* Make the kernel see the light */
 		p += sizeof(struct ebt_entries);
 		e = entries->entries;
 		while (e) {
@@ -192,7 +196,7 @@
 			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 */
+				/* Translate the jump to a udc */
 				if (st->verdict >= 0)
 					st->verdict = chain_offsets
 					   [st->verdict + NF_BR_NUMHOOKS];
@@ -207,7 +211,7 @@
 		i++;
 	}
 
-	/* sanity check */
+	/* Sanity check */
 	if (p - (char *)new->entries != new->entries_size)
 		ebt_print_bug("Entries_size bug");
 	free(chain_offsets);
@@ -220,19 +224,22 @@
 	int size;
 	FILE *file;
 
-	/* start from an empty file with right priviliges */
+	/* Start from an empty file with right priviliges */
 	command = (char *)malloc(strlen(filename) + 15);
 	if (!command)
 		ebt_print_memory();
 	strcpy(command, "cat /dev/null>");
 	strcpy(command + 14, filename);
-	if (system(command))
+	if (system(command)) {
 		ebt_print_error("Couldn't create file %s", filename);
+		goto free_command;
+	}
 	strcpy(command, "chmod 600 ");
 	strcpy(command + 10, filename);
-	if (system(command))
+	if (system(command)) {
 		ebt_print_error("Couldn't chmod file %s", filename);
-	free(command);
+		goto free_command;
+	}
 
 	size = sizeof(struct ebt_replace) + repl->entries_size +
 	   repl->nentries * sizeof(struct ebt_counter);
@@ -242,18 +249,20 @@
 	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 */
+	/* 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 (!(file = fopen(filename, "wb")))
+	if (!(file = fopen(filename, "wb"))) {
 		ebt_print_error("Couldn't open file %s", filename);
-	if (fwrite(data, sizeof(char), size, file) != size) {
-		fclose(file);
+		goto free_data;
+	} else if (fwrite(data, sizeof(char), size, file) != size)
 		ebt_print_error("Couldn't write everything to file %s",
 				filename);
-	}
 	fclose(file);
+free_data:
 	free(data);
+free_command:
+	free(command);
 }
 
 void ebt_deliver_table(struct ebt_u_replace *u_repl)
@@ -261,19 +270,20 @@
 	socklen_t optlen;
 	struct ebt_replace *repl;
 
-	/* translate the struct ebt_u_replace to a struct ebt_replace */
+	/* 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);
 		return;
 	}
-	/* give the data to the kernel */
+	/* Give the data to the kernel */
 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
-	get_sockfd();
+	if (get_sockfd())
+		return;
 	if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
 		return;
-	if (u_repl->command == 8) { /* the ebtables module may not
-	                            * yet be loaded with --atomic-commit */
+	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))
@@ -285,41 +295,46 @@
 		    " the extension");
 }
 
-static void store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+static int store_counters_in_file(char *filename, struct ebt_u_replace *repl)
 {
-	int size = repl->nentries * sizeof(struct ebt_counter);
+	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")))
+	if (!(file = fopen(filename, "r+b"))) {
 		ebt_print_error("Could not open file %s", filename);
-	/* 
-	 * find out entries_size and then set the file pointer to the
-	 * counters
-	 */
+		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)) {
-		fclose(file);
 		ebt_print_error("File %s is corrupt", filename);
+		ret = -1;
+		goto close_file;
 	}
 	if (fwrite(repl->counters, sizeof(char), size, file) != size) {
-		fclose(file);
 		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 */
-void ebt_deliver_counters(struct ebt_u_replace *u_repl)
+/* 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, int exec_style)
 {
 	struct ebt_counter *old, *new, *newcounters;
 	socklen_t optlen;
 	struct ebt_replace repl;
-	struct ebt_cntchanges *cc = u_repl->counterchanges;
+	struct ebt_cntchanges *cc = u_repl->counterchanges, *cc2, **cc3;
+	int i;
 
 	if (u_repl->nentries == 0)
 		return;
@@ -333,24 +348,22 @@
 	new = newcounters;
 	while (cc) {
 		if (cc->type == CNT_NORM) {
-			/*
-			 *'normal' rule, meaning we didn't do anything to it
-			 * So, we just copy
-			 */
+			/* 'Normal' rule, meaning we didn't do anything to it
+			 * So, we just copy */
 			new->pcnt = old->pcnt;
 			new->bcnt = old->bcnt;
-			/* we've used an old counter */
+			/* We've used an old counter */
 			old++;
-			/* we've set a new counter */
+			/* We've set a new counter */
 			new++;
 		} else if (cc->type == CNT_DEL) {
-			/* don't use this old counter */
+			/* Don't use this old counter */
 			old++;
 		} else if (cc->type == CNT_ADD) {
-			/* new counter, let it stay 0 */
+			/* New counter, let it stay 0 */
 			new++;
 		} else {
-			/* zero it (let it stay 0) */
+			/* Zero it (let it stay 0) */
 			old++;
 			new++;
 		}
@@ -366,20 +379,38 @@
 	}
 	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
 	   sizeof(struct ebt_replace);
-	/* now put the stuff in the kernel's 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));
 
-	get_sockfd();
+	if (get_sockfd())
+		return;
 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
 		ebt_print_bug("Couldn't update kernel counters");
+
+	if (exec_style != EXEC_STYLE_DAEMON)
+		return;
+	/* Reset the counterchanges to CNT_NORM */
+	cc = u_repl->counterchanges;
+	for (i = 0; i < u_repl->nentries; i++) {
+		cc->type = CNT_NORM;
+		cc3 = &cc->next;
+		cc = cc->next;
+	}
+	*cc3 = NULL;
+	while (cc) {
+		cc2 = cc->next;
+		free(cc);
+		cc = cc2;
+	}
 }
 
 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));
@@ -393,10 +424,12 @@
 	new->next = NULL;
 	**l = new;
 	*l = &new->next;
-	if (ebt_find_match(new->m->u.name) == NULL)
+	if (ebt_find_match(new->m->u.name) == NULL) {
 		ebt_print_error("Kernel match %s unsupported by userspace tool",
 				new->m->u.name);
-	return 0;
+		ret = -1;
+	}
+	return ret;
 }
 
 static int
@@ -404,6 +437,7 @@
    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));
@@ -417,10 +451,12 @@
 	new->next = NULL;
 	**l = new;
 	*l = &new->next;
-	if (ebt_find_watcher(new->w->u.name) == NULL)
+	if (ebt_find_watcher(new->w->u.name) == NULL) {
 		ebt_print_error("Kernel watcher %s unsupported by userspace "
 				"tool", new->w->u.name);
-	return 0;
+		ret = -1;
+	}
+	return ret;
 }
 
 static int
@@ -428,7 +464,7 @@
    int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
    unsigned int valid_hooks, char *base)
 {
-	/* an entry */
+	/* An entry */
 	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
 		struct ebt_u_entry *new;
 		struct ebt_u_match_list **m_l;
@@ -440,7 +476,7 @@
 			ebt_print_memory();
 		new->bitmask = e->bitmask;
 		/*
-		 * plain userspace code doesn't know about
+		 * Plain userspace code doesn't know about
 		 * EBT_ENTRY_OR_ENTRIES
 		 */
 		new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
@@ -467,12 +503,14 @@
 		   malloc(t->target_size + sizeof(struct ebt_entry_target));
 		if (!new->t)
 			ebt_print_memory();
-		if (ebt_find_target(t->u.name) == NULL)
+		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 */
+		/* Deal with jumps to udc */
 		if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
 			char *tmp = base;
 			int verdict = ((struct ebt_standard_target *)t)->verdict;
@@ -488,7 +526,7 @@
 					cl = cl->next;
 				}
 				if (!cl)
-					ebt_print_bug("can't find udc for "
+					ebt_print_bug("Can't find udc for "
 						      "jump");
 				((struct ebt_standard_target *)new->t)->verdict = i;
 			}
@@ -500,7 +538,7 @@
 		(*cnt)++;
 		(*totalcnt)++;
 		return 0;
-	} else { /* a new chain */
+	} else { /* A new chain */
 		int i;
 		struct ebt_entries *entries = (struct ebt_entries *)e;
 		struct ebt_u_chain_list *cl;
@@ -513,8 +551,8 @@
 			if (valid_hooks & (1 << i))
 				break;
 		*hook = i;
-		/* makes use of fact that standard chains come before udc */
-		if (i >= NF_BR_NUMHOOKS) { /* udc */
+		/* Makes use of fact that standard chains come before udc */
+		if (i >= NF_BR_NUMHOOKS) { /* Udc */
 			i -= NF_BR_NUMHOOKS;
 			cl = u_repl->udc;
 			while (i-- > 0)
@@ -526,7 +564,7 @@
 	}
 }
 
-/* initialize all chain headers */
+/* Initialize all chain headers */
 static int
 ebt_translate_chains(struct ebt_entry *e, unsigned int *hook,
    struct ebt_u_replace *u_repl, unsigned int valid_hooks)
@@ -540,10 +578,10 @@
 		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
 			if (valid_hooks & (1 << i))
 				break;
-		/* makes use of fact that standard chains come before udc */
-		if (i >= NF_BR_NUMHOOKS) { /* udc */
+		/* Makes use of fact that standard chains come before udc */
+		if (i >= NF_BR_NUMHOOKS) { /* Udc */
 			chain_list = &u_repl->udc;
-			/* add in the back */
+			/* Add in the back */
 			while (*chain_list)
 				chain_list = &((*chain_list)->next);
 			*chain_list = (struct ebt_u_chain_list *)
@@ -556,10 +594,8 @@
 			if (!((*chain_list)->udc))
 				ebt_print_memory();
 			new = (*chain_list)->udc;
-			/*
-			 * ebt_translate_entry depends on this for knowing
-			 * to which chain is being jumped
-			 */
+			/* ebt_translate_entry depends on this for knowing
+			 * to which chain is being jumped */
 			(*chain_list)->kernel_start = (char *)e;
 		} else {
 			*hook = i;
@@ -578,19 +614,19 @@
 	return 0;
 }
 
-static void retrieve_from_file(char *filename, struct ebt_replace *repl,
+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;
+	int size, ret = 0;
 
-	if (!(file = fopen(filename, "r+b")))
+	if (!(file = fopen(filename, "r+b"))) {
 		ebt_print_error("Could not open file %s", filename);
-	/*
-	 * make sure table name is right if command isn't -L or --atomic-commit
-	 */
+		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)
@@ -598,25 +634,30 @@
 		strcpy(hlp, repl->name);
 	}
 	if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
-	   != sizeof(struct ebt_replace))
+	   != 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)) {
-		fclose(file);
 		ebt_print_error("File %s contains wrong table name or is "
 				"corrupt", filename);
-		free(hlp);
+		ret = -1;
+		goto close_file;
 	} else if (!ebt_find_table(repl->name)) {
-		fclose(file);
 		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)) {
-		fclose(file);
 		ebt_print_error("File %s has wrong size", filename);
+		ret -1;
+		goto close_file;
 	}
 	entries = (char *)malloc(repl->entries_size);
 	if (!entries)
@@ -630,7 +671,7 @@
 			ebt_print_memory();
 	} else
 		repl->counters = sparc_cast NULL;
-	/* copy entries and counters */
+	/* 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 ||
@@ -639,10 +680,15 @@
 	   || fread((char *)repl->counters, sizeof(char),
 	   repl->nentries * sizeof(struct ebt_counter), file)
 	   != repl->nentries * sizeof(struct ebt_counter)) {
-		fclose(file);
 		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,
@@ -653,7 +699,8 @@
 	char *entries;
 
 	optlen = sizeof(struct ebt_replace);
-	get_sockfd();
+	if (get_sockfd())
+		return -1;
 	/* --atomic-init || --init-table */
 	if (init)
 		optname = EBT_SO_GET_INIT_INFO;
@@ -676,7 +723,7 @@
 	else
 		repl->counters = sparc_cast NULL;
 
-	/* we want to receive the counters */
+	/* We want to receive the counters */
 	repl->num_counters = repl->nentries;
 	optlen += repl->entries_size + repl->num_counters *
 	   sizeof(struct ebt_counter);
@@ -685,7 +732,7 @@
 	else
 		optname = EBT_SO_GET_ENTRIES;
 	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
-		ebt_print_bug("hmm, what is wrong??? bug#1");
+		ebt_print_bug("Hmm, what is wrong??? bug#1");
 
 	return 0;
 }
@@ -701,17 +748,16 @@
 	strcpy(repl.name, u_repl->name);
 	if (u_repl->filename != NULL) {
 		if (init)
-			ebt_print_bug("getting initial table data from a "
+			ebt_print_bug("Getting initial table data from a "
 				  "file is impossible");
-		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
-		/*
-		 * -L with a wrong table name should be dealt with silently
-		 */
+		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) == -1)
+	} else if (retrieve_from_kernel(&repl, u_repl->command, init))
 		return -1;
 
-	/* translate the struct ebt_replace to a struct ebt_u_replace */
+	/* 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;
@@ -729,14 +775,13 @@
 		prev_cc = &(new_cc->next);
 	}
 	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);
-	i = 0; /* holds the expected nr. of entries for the chain */
-	j = 0; /* holds the up to now counted entries for the chain */
-	/*
-	 * holds the total nr. of entries,
-	 * should equal u_repl->nentries afterwards
-	 */
+	i = 0; /* Holds the expected nr. of entries for the chain */
+	j = 0; /* Holds the up to now counted entries for the chain */
+	/* Holds the total nr. of entries,
+	 * should equal u_repl->nentries afterwards */
 	k = 0;
 	hook = -1;
 	EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size,
diff --git a/ebtables-standalone.c b/ebtables-standalone.c
new file mode 100644
index 0000000..d349d39
--- /dev/null
+++ b/ebtables-standalone.c
@@ -0,0 +1,14 @@
+#include <string.h>
+#include "include/ebtables_u.h"
+
+static struct ebt_u_replace replace;
+void ebt_early_init_once();
+
+int main(int argc, char *argv[])
+{
+	ebt_silent = 0;
+	ebt_early_init_once();
+	strcpy(replace.name, "filter");
+	do_command(argc, argv, EXEC_STYLE_PRG, &replace);
+	return 0;
+}
diff --git a/ebtables.8 b/ebtables.8
index 24a27a4..1dc8cd5 100644
--- a/ebtables.8
+++ b/ebtables.8
@@ -24,31 +24,43 @@
 .\"     
 .\"
 .SH NAME
-ebtables (v.2.0.7) \- Ethernet bridge frame table administration
+ebtables (v.2.0.7), ebtablesd, ebtablesu \- Ethernet bridge frame table administration
 .SH SYNOPSIS
-.BR "ebtables " [ "-t table" ] " -" [ ADI ] " chain rule specification " [ "match extensions" "] [" "watcher extensions" ] " target"
+.BR "ebtables " [ -t " table ] " - [ ADI "] chain rule specification [match extensions] [watcher extensions] target"
 .br
-.BR "ebtables " [ "-t table" ] " -P chain ACCEPT " | " DROP " | " RETURN"
+.BR "ebtables " [ -t " table ] " -P " chain " ACCEPT " | " DROP " | " RETURN
 .br
-.BR "ebtables " [ "-t table" ] " -F " [ chain ]
+.BR "ebtables " [ -t " table ] " -F " [chain]"
 .br
-.BR "ebtables " [ "-t table" ] " -Z " [ chain ]
+.BR "ebtables " [ -t " table ] " -Z " [chain]"
 .br
-.BR "ebtables " [ "-t table" ] " -L " [ -Z "] [" " chain" "] [ [ [" --Ln "] [" --Lc "] ] | [" --Lx "] ] [" --Lmac2 "]"
+.BR "ebtables " [ -t " table ] " -L " [" -Z "] [chain] [ [ [" --Ln "] [" --Lc "] ] | [" --Lx "] ] [" --Lmac2 ]
 .br
-.BR "ebtables " [ "-t table" ] " -N chain " [ "-P ACCEPT " | " DROP " | " RETURN" ]
+.BR "ebtables " [ -t " table ] " -N " chain [" "-P ACCEPT " | " DROP " | " RETURN" ]
 .br
-.BR "ebtables " [ "-t table" ] " -X " [ chain ]
+.BR "ebtables " [ -t " table ] " -X " [chain]"
 .br
-.BR "ebtables " [ "-t table" ] " -E old-chain-name new-chain-name"
+.BR "ebtables " [ -t " table ] " -E " old-chain-name new-chain-name"
 .br
-.BR "ebtables " [ "-t table" ] " --init-table"
+.BR "ebtables " [ -t " table ] " --init-table
 .br
-.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-commit"
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-commit
 .br
-.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-init"
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-init
 .br
-.BR "ebtables " [ "-t table" "] [" "--atomic-file file" ] " --atomic-save"
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-save
+.br
+.BR "ebtablesu open " table 
+.br
+.BR "ebtablesu fopen " "table file"
+.br
+.BR "ebtablesu free " table 
+.br
+.BR "ebtablesu commit " table
+.br
+.BR "ebtablesu fcommit " "table file"
+.br
+.B ebtablesu quit
 .br
 .SH DESCRIPTION
 .B ebtables
@@ -59,6 +71,52 @@
 .B iptables
 application, but less complicated, due to the fact that the Ethernet protocol
 is much simpler than the IP protocol.
+.br
+.BR ebtablesu " and " ebtablesd " can be used to speed up adding rules using a script when the"
+.B --atomic-commit
+option is not satisfactory. The
+.BR open " command makes ebtablesd open the specified kernel table for processing"
+.BR "" "in userspace (multiple tables can be open in the same time). The " fopen
+command opens the table from the specified file.
+.BR "" "The " free " command removes the specified table out of the memory of ebtablesd."
+No data is written to a file or to the kernel.
+.BR "" "The " commit " command stores the table from the memory of ebtablesd to the kernel."
+.BR "" "The " fcommit " command stores the table from the memory of ebtablesd to the specified file."
+This file can be read later, e.g. with
+.BR "ebtables --atomic-file " file " -L" .
+.BR "" "The " quit " command lets ebtablesd finish gracefully."
+All commands, options and extensions that ebtables uses can be used with ebtablesu, except for
+.BR --init-table ", " --atomic-file ", " --atomic-commit ", " --atomic-init ", " --atomic-save " and " -h .
+.br
+Example usage:
+.br
+# ebtablesd&
+.br
+# ebtablesu open filter
+.br
+# ebtablesu -A FORWARD -j DROP
+.br
+# ebtablesu commit filter
+.br
+# ebtablesu quit
+.br
+Alternatively, the commands can be echo'ed directly to the pipe used by ebtablesd,
+which has default location /tmp/ebtables-vx.y.z/ebtablesd_pipe, where
+x.y.z is the ebtables version (e.g. 2.0.7). Using echo instead of ebtablesu is
+much faster because echo is a bash built-in command. Commands like cat can be used
+too, of course.
+.br
+Example usage:
+.br
+# (./ebtablesd&) ; PIPE=/tmp/ebtables-v2.0.7/ebtablesd_pipe ; sleep 1
+.br
+# echo "ebtablesu open filter" >> $PIPE
+.br
+# echo "ebtablesu -A FORWARD -j DROP" >> $PIPE
+.br
+# echo "ebtablesu commit filter" >> $PIPE
+.br
+# echo "ebtablesu quit" >> $PIPE
 .SS CHAINS
 There are three ebtables tables with built-in chains in the
 Linux kernel. These tables are used to divide functionality into
diff --git a/ebtables.c b/ebtables.c
index 8b0dd80..2d0cd61 100644
--- a/ebtables.c
+++ b/ebtables.c
@@ -3,7 +3,7 @@
  *
  * Author: Bart De Schuymer
  *
- *  This code is stongly inspired on the iptables code which is
+ *  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
@@ -33,11 +33,8 @@
 #include <fcntl.h>
 #include <sys/wait.h>
 
-/*
- * default command line options
- * do not mess around with the already assigned numbers unless
- * you know what you are doing
- */
+/* dDfault 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' },
@@ -82,29 +79,23 @@
 
 static struct option *ebt_options = ebt_original_options;
 
-/*
- * holds all the data
- */
-static struct ebt_u_replace replace;
+/* Holds all the data */
+static struct ebt_u_replace *replace;
 
-/*
- * the chosen table
- */
+/* The chosen table */
 static struct ebt_u_table *table = NULL;
 
-/*
- * The pointers in here are special:
+/* 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;
+ * Same holds for the struct ebt_match and struct ebt_watcher pointers */
+static struct ebt_u_entry *new_entry;
 
 
-static int global_option_offset = 0;
+static int global_option_offset;
 #define OPTION_OFFSET 256
 static struct option *
 merge_options(struct option *oldopts, const struct option *newopts,
@@ -130,7 +121,7 @@
 		merge[num_old + i].val += *options_offset;
 	}
 	memset(merge + num_old + num_new, 0, sizeof(struct option));
-	/* only free dynamically allocated stuff */
+	/* Only free dynamically allocated stuff */
 	if (oldopts != ebt_original_options)
 		free(oldopts);
 
@@ -155,7 +146,7 @@
 	   (ebt_options, t->extra_ops, &(t->option_offset));
 }
 
-/* be backwards compatible, so don't use '+' in kernel */
+/* Be backwards compatible, so don't use '+' in kernel */
 #define IF_WILDCARD 1
 static void print_iface(const char *iface)
 {
@@ -168,18 +159,14 @@
 		*c = IF_WILDCARD;
 }
 
-/*
- * we use replace.flags, so we can't use the following values:
- * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
- */
+/* 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()
- */
+/* Helper function for list_rules() */
 static void list_em(struct ebt_u_entries *entries)
 {
 	int i, j, space = 0, digits;
@@ -190,13 +177,13 @@
 	struct ebt_u_watcher *w;
 	struct ebt_u_target *t;
 
-	if (replace.flags & LIST_MAC2)
+	if (replace->flags & LIST_MAC2)
 		ebt_printstyle_mac = 2;
 	hlp = entries->entries;
-	if (replace.flags & LIST_X && entries->policy != EBT_ACCEPT) {
-		printf("ebtables -t %s -P %s %s\n", replace.name,
+	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)) {
+	} 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]);
@@ -209,7 +196,7 @@
 	}
 
 	for (i = 0; i < entries->nentries; i++) {
-		if (replace.flags & LIST_N) {
+		if (replace->flags & LIST_N) {
 			digits = 0;
 			/* A little work to get nice rule numbers. */
 			j = i + 1;
@@ -221,18 +208,16 @@
 				printf(" ");
 			printf("%d. ", i + 1);
 		}
-		if (replace.flags & LIST_X)
+		if (replace->flags & LIST_X)
 			printf("ebtables -t %s -A %s ",
-			   replace.name, entries->name);
+			   replace->name, entries->name);
 
 		/* The standard target's print() uses this to find out
 		 * the name of a udc */
-		hlp->replace = &replace;
+		hlp->replace = replace;
 
-		/*
-		 * Don't print anything about the protocol if no protocol was
-		 * specified, obviously this means any protocol will do.
-		 */
+		/* 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)
@@ -311,12 +296,12 @@
 			printf("%s ", hlp->t->u.name);
 		t = ebt_find_target(hlp->t->u.name);
 		if (!t)
-			ebt_print_bug("Target not found");
+			ebt_print_bug("Target '%s' not found", hlp->t->u.name);
 		t->print(hlp, hlp->t);
-		if (replace.flags & LIST_C)
+		if (replace->flags & LIST_C)
 			printf(", pcnt = %llu -- bcnt = %llu",
-			   replace.counters[entries->counter_offset + i].pcnt,
-			   replace.counters[entries->counter_offset + i].bcnt);
+			   replace->counters[entries->counter_offset + i].pcnt,
+			   replace->counters[entries->counter_offset + i].bcnt);
 		printf("\n");
 		hlp = hlp->next;
 	}
@@ -356,10 +341,10 @@
 "--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"
+"--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"
 "--modprobe -M program         : try to insert modules using this program\n"
 "--version -V                  : print package version\n\n"
 "Environment variable:\n"
@@ -384,43 +369,39 @@
 	exit(0);
 }
 
-/*
- * execute command L
- */
+/* Execute command L */
 static void list_rules()
 {
 	int i;
 
-	if (!(replace.flags & LIST_X))
+	if (!(replace->flags & LIST_X))
 		printf("Bridge table: %s\n", table->name);
-	if (replace.selected_chain != -1) {
-		list_em(ebt_to_chain(&replace));
+	if (replace->selected_chain != -1) {
+		list_em(ebt_to_chain(replace));
 	} else {
-		struct ebt_u_chain_list *cl = replace.udc;
+		struct ebt_u_chain_list *cl = replace->udc;
 
-		/*
-		 * create new chains and rename standard chains when necessary
-		 */
-		if (replace.flags & LIST_X) {
+		/* Create new chains and rename standard chains when necessary */
+		if (replace->flags & LIST_X) {
 			while (cl) {
-				printf("ebtables -t %s -N %s\n", replace.name,
+				printf("ebtables -t %s -N %s\n", replace->name,
 				   cl->udc->name);
 				cl = cl->next;
 			}
-			cl = replace.udc;
+			cl = replace->udc;
 			for (i = 0; i < NF_BR_NUMHOOKS; i++)
-				if (replace.valid_hooks & (1 << i) &&
-				   strcmp(replace.hook_entry[i]->name,
+				if (replace->valid_hooks & (1 << i) &&
+				   strcmp(replace->hook_entry[i]->name,
 					  ebt_hooknames[i]))
 					printf("ebtables -t %s -E %s %s\n",
-					   replace.name, ebt_hooknames[i],
-					   replace.hook_entry[i]->name);
+					   replace->name, ebt_hooknames[i],
+					   replace->hook_entry[i]->name);
 		}
 		i = 0;
 		while (1) {
 			if (i < NF_BR_NUMHOOKS) {
-				if (replace.valid_hooks & (1 << i))
-					list_em(replace.hook_entry[i]);
+				if (replace->valid_hooks & (1 << i))
+					list_em(replace->hook_entry[i]);
 				i++;
 				continue;
 			} else {
@@ -440,7 +421,7 @@
 	if (colon) {
 		*colon = '\0';
 		if (*(colon + 1) == '\0')
-			*rule_nr_end = -1; /* until the last rule */
+			*rule_nr_end = -1; /* Until the last rule */
 		else {
 			*rule_nr_end = strtol(colon + 1, &buffer, 10);
 			if (*buffer != '\0' || *rule_nr_end == 0)
@@ -448,7 +429,7 @@
 		}
 	}
 	if (colon == argv)
-		*rule_nr = 1; /* beginning with the first rule */
+		*rule_nr = 1; /* Beginning with the first rule */
 	else {
 		*rule_nr = strtol(argv, &buffer, 10);
 		if (*buffer != '\0' || *rule_nr == 0)
@@ -459,7 +440,7 @@
 	return 0;
 }
 
-static void parse_iface(char *iface, char *option)
+static int parse_iface(char *iface, char *option)
 {
 	char *c;
 
@@ -467,15 +448,33 @@
 		if (*(c + 1) != '\0') {
 			ebt_print_error("Spurious characters after '+' "
 			                "wildcard for %s", option);
+			return -1;
 		} else
 			*c = IF_WILDCARD;
 	}
+	return 0;
 }
 
-#define print_if_l_error ebt_print_error("Interface name length must be less " \
-   "than %d", IFNAMSIZ)
-#define print_epoto_error(__proto) ebt_print_error("Problem with the specified"\
-   " Ethernet protocol (%s), perhaps "_PATH_ETHERTYPES " is missing", __proto);
+void ebt_early_init_once()
+{
+	ebt_iterate_matches(merge_match);
+	ebt_iterate_watchers(merge_watcher);
+	ebt_iterate_targets(merge_target);
+}
+
+#define ebt_print_error2(format, args...) {__ebt_print_error(format, ##args); \
+   return -1;}
+#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;				\
+__ret;})
+#define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->flags & OPT_ZERO)
+
 #define OPT_COMMAND	0x01
 #define OPT_TABLE	0x02
 #define OPT_IN		0x04
@@ -487,20 +486,17 @@
 #define OPT_ZERO	0x100
 #define OPT_LOGICALIN	0x200
 #define OPT_LOGICALOUT	0x400
-#define OPT_KERNELDATA	0x800 /* if set, we already have loaded the table
-			       * in userspace */
-/* the main thing */
-int main(int argc, char *argv[])
+#define OPT_KERNELDATA	0x800 /* This value is also defined in ebtablesd.c */
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_)
 {
 	char *buffer;
 	int c, i;
-	/*
-	 * this special one for the -Z option (we can have -Z <this> -L <that>)
-	 */
+	/* Needed for the -Z option (we can have -Z <this> -L <that>) */
 	int zerochain = -1;
 	int policy = 0;
-	int rule_nr = 0; /* used for -[D,I] */
-	int rule_nr_end = 0; /* used for -I */
+	int rule_nr = 0;
+	int rule_nr_end = 0;
 	struct ebt_u_target *t;
 	struct ebt_u_match *m;
 	struct ebt_u_watcher *w;
@@ -509,592 +505,476 @@
 	struct ebt_u_entries *entries;
 
 	opterr = 0;
+	ebt_modprobe = NULL;
 
-	ebt_iterate_matches(merge_match);
-	ebt_iterate_watchers(merge_watcher);
-	ebt_iterate_targets(merge_target);
+	replace = replace_;
 
-	buffer = getenv(ATOMIC_ENV_VARIABLE);
-	if (buffer) {
-		replace.filename = malloc(strlen(buffer)+1);
-		if (!replace.filename)
-			ebt_print_memory();
-		memcpy(replace.filename, buffer, strlen(buffer)+1);
-		buffer = NULL;
+	/* 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;
+		}
 	}
-	/*
-	 * initialize the table name, OPT_ flags, selected hook and command
-	 */
-	strcpy(replace.name, "filter");
-	replace.flags = 0;
-	replace.selected_chain = -1;
-	replace.command = 'h';
-	replace.counterchanges = NULL;
 
-	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
-	 */
+	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;
+	new_entry->replace = replace;
 
-	/*
-	 * The scenario induced by this loop makes that:
+	/* The scenario induced by this loop makes that:
 	 * '-t'  ,'-M' and --atomic (if specified) have to come
-	 * before '-A' and the like
-	 */
+	 * before '-A' and the like */
 
-	/*
-	 * getopt saves the day
-	 */
+	/* Getopt saves the day */
 	while ((c = getopt_long(argc, argv,
 	   "-A:D:I:N:E:X::L::Z::F::P:Vhi:o:j:p:s:d:t:M:", 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 */
-		case 'N': /* make a user defined chain */
-		case 'E': /* rename chain */
-		case 'X': /* delete chain */
+		case 'A': /* Add a rule */
+		case 'D': /* Delete a rule */
+		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--;
+			if (replace->command == 'N' && c == 'P') {
+				replace->command = c;
+				optind--; /* No table specified */
 				goto handle_P;
 			}
-			replace.command = c;
-			replace.flags |= OPT_COMMAND;
-			if (!(replace.flags & OPT_KERNELDATA)) {
-				ebt_get_kernel_table(&replace, table, 0);
-				replace.flags |= OPT_KERNELDATA;
-			}
-			if (optarg && (optarg[0] == '-' ||
-			    !strcmp(optarg, "!")))
-				ebt_print_error("No chain name specified");
-			if (c == 'N') {
-				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;
-			}
-			if (c == 'X') {
-				char *opt;
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
 
-				if (!optarg && (optind >= argc ||
-				   (argv[optind][0] == '-'
-				    && strcmp(argv[optind], "!")))) {
-					replace.selected_chain = -1;
-					ebt_delete_chain(&replace);
+			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') {
+				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 (optarg)
-					opt = optarg;
-				else {
-					opt = argv[optind];
-					optind++;
-				}
-				if ((replace.selected_chain =
-				     ebt_get_chainnr(&replace, opt)) == -1)
-					ebt_print_error("Chain %s doesn't "
-							"exist", opt);
-				ebt_delete_chain(&replace);
+
+				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_error("Chain %s doesn't exist",
-						optarg);
+			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 || argv[optind][0] == '-' ||
-				   !strcmp(argv[optind], "!"))
-					ebt_print_error("No new chain name "
-						    "specified");
+				if (optind >= argc)
+					ebt_print_error2("No new chain name specified");
+				if (optind < argc - 1)
+					ebt_print_error2("No extra options allowed with -E");
 				if (strlen(argv[optind])>=EBT_CHAIN_MAXNAMELEN)
-					ebt_print_error("Chain name len can't "
-						    "exceed %d",
-						    EBT_CHAIN_MAXNAMELEN - 1);
-				if (ebt_get_chainnr(&replace, argv[optind]) !=
-				    -1)
-					ebt_print_error("Chain %s already "
-							"exists", argv[optind]);
+					ebt_print_error2("Chain name length can't exceed %d", EBT_CHAIN_MAXNAMELEN - 1);
+				if (ebt_get_chainnr(replace, argv[optind]) != -1)
+					ebt_print_error2("Chain %s already exists", argv[optind]);
 				if (ebt_find_target(argv[optind]))
-					ebt_print_error("Target with name %s "
-							"exists", argv[optind]);
-				ebt_rename_chain(&replace, argv[optind]);
+					ebt_print_error2("Target with name %s exists", argv[optind]);
+				ebt_rename_chain(replace, argv[optind]);
 				optind++;
 				break;
-			}
-
-			if (c == 'D' && optind < argc &&
-			    (argv[optind][0] != '-' ||
-			    (argv[optind][1] >= '0' &&
-			     argv[optind][1] <= '9'))) {
-				if (parse_delete_rule(argv[optind],
-				    &rule_nr, &rule_nr_end))
-					ebt_print_error("Problem with the "
-					            "specified rule number(s)");
+			} 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_delete_rule(argv[optind], &rule_nr, &rule_nr_end))
+					ebt_print_error2("Problem with the specified rule number(s)");
 				optind++;
-			}
-			if (c == 'I') {
-				if (optind >= argc ||
-				    (argv[optind][0] == '-' &&
-				    (argv[optind][1] < '0' ||
-				    argv[optind][1] > '9')))
-					ebt_print_error("No rulenr for -I"
-					            " specified");
+			} else if (c == 'I') {
+				if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
+					ebt_print_error2("No rulenr for -I specified");
 				rule_nr = strtol(argv[optind], &buffer, 10);
 				if (*buffer != '\0')
-					ebt_print_error("Problem with the "
-					            "specified rule number");
+					ebt_print_error2("Problem with the specified rule number");
 				optind++;
-			}
-			if (c == 'P') {
+			} else if (c == 'P') {
 handle_P:
 				if (optind >= argc)
-					ebt_print_error("No policy specified");
-				policy = 0;
+					ebt_print_error2("No policy specified");
 				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
-					if (!strcmp(argv[optind],
-					   ebt_standard_targets[i])) {
+					if (!strcmp(argv[optind], ebt_standard_targets[i])) {
 						policy = -i -1;
 						if (policy == EBT_CONTINUE)
-							policy = 0;
+							ebt_print_error2("Wrong policy: '%s'", argv[optind]);
 						break;
 					}
-				if (policy == 0)
-					ebt_print_error("Wrong policy");
 				optind++;
 			}
 			break;
-
-		case 'L': /* list */
-		case 'F': /* flush */
-		case 'Z': /* zero counters */
+		case 'L': /* List */
+		case 'F': /* Flush */
+		case 'Z': /* Zero counters */
 			if (c == 'Z') {
-				if (replace.flags & OPT_ZERO)
-					ebt_print_error("Multiple commands"
-					            " not allowed");
-				if ( (replace.flags & OPT_COMMAND &&
-				   replace.command != 'L'))
-					ebt_print_error("command -Z only "
-					   "allowed together with command -L");
-				replace.flags |= OPT_ZERO;
+				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 {
-				replace.command = c;
-				if (replace.flags & OPT_COMMAND)
-					ebt_print_error("Multiple commands"
-					            " not allowed");
-				replace.flags |= OPT_COMMAND;
+				replace->command = c;
+				if (replace->flags & OPT_COMMAND)
+					ebt_print_error2("Multiple commands are not allowed");
+				replace->flags |= OPT_COMMAND;
+				if (replace->flags & OPT_ZERO)
+					goto print_zero;
 			}
-			ebt_get_kernel_table(&replace, table, 0);
+
+#ifdef SILENT_DAEMON
+			if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-L not supported in daemon mode not supported in daemon mode");
+#endif
+
+			if (!(replace->flags & OPT_KERNELDATA))
+				ebt_get_kernel_table(replace, 0);
 			i = -1;
-			if (optarg) {
-				if ( (i = ebt_get_chainnr(&replace, optarg)) ==
-				      -1 )
-					ebt_print_error("Bad chain");
-			} else
-				if (optind < argc && argv[optind][0] != '-') {
-					if ((i = ebt_get_chainnr(&replace,
-					    argv[optind])) == -1)
-						ebt_print_error("Bad chain");
-					optind++;
-				}
+			if (optind < argc && argv[optind][0] != '-') {
+				if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
+					ebt_print_error2("Bad chain");
+				optind++;
+			}
 			if (i != -1) {
 				if (c == 'Z')
 					zerochain = i;
 				else
-					replace.selected_chain = i;
+					replace->selected_chain = i;
 			}
 			break;
-
-		case 'V': /* version */
-			replace.command = 'V';
-			if (replace.flags & OPT_COMMAND)
-				ebt_print_error("Multiple commands not "
-						"allowed");
+		case 'V': /* Version */
+			replace->command = 'V';
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
 			PRINT_VERSION;
 			exit(0);
-
-		case 'M': /* modprobe */
-			if (replace.command != 'h')
-				ebt_print_error("Please put the -M option "
-						"earlier");
+		case 'M': /* Modprobe */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the -M option earlier");
 			ebt_modprobe = optarg;
 			break;
+		case 'h': /* Help */
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-h not supported in daemon mode");
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->command = 'h';
 
-		case 'h': /* help */
-			if (replace.flags & OPT_COMMAND)
-				ebt_print_error("Multiple commands not "
-						"allowed");
-			replace.command = 'h';
-			/*
-			 * All other arguments should be extension names
-			 */
+			/* 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]))
+				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_error("Extension %s "
-						   "not found", argv[optind]);
-					if (replace.flags & OPT_JUMP)
-						ebt_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;
+						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 (replace.command != 'h')
-				ebt_print_error("Please put the -t option "
-						"first");
-			ebt_check_option(&replace.flags, OPT_TABLE);
+		case 't': /* Table */
+			if (replace->command != 'h')
+				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_error("Table name too long");
-			strcpy(replace.name, optarg);
+				ebt_print_error2("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)
-				ebt_print_error("No command specified");
-			if ( replace.command != 'A' &&
-			   replace.command != 'D' && replace.command != 'I')
-				ebt_print_error("Command and option do not "
-						"match");
+		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 (!OPT_COMMANDS)
+				ebt_print_error2("No command specified");
+			if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I')
+				ebt_print_error2("Command and option do not match");
 			if (c == 'i') {
-				ebt_check_option(&replace.flags, OPT_IN);
-				if (replace.selected_chain > 2 &&
-				   replace.selected_chain < NF_BR_BROUTING)
-					ebt_print_error("Use in-interface "
-					   "only in "
-					   "INPUT, FORWARD, PREROUTING and"
-					   "BROUTING chains");
-				if (ebt_check_inverse(optarg))
+				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 (optind > argc)
-					ebt_print_error("No in-interface "
-					            "specified");
-				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
-					print_if_l_error;
-				strcpy(new_entry->in, argv[optind - 1]);
-				parse_iface(new_entry->in, "-i");
+				if (strlen(optarg) >= IFNAMSIZ)
+					ebt_print_error2("Interface name length must be less than %d", IFNAMSIZ);
+				strcpy(new_entry->in, optarg);
+				if (parse_iface(new_entry->in, "-i"))
+					return -1;
 				break;
-			}
-			if (c == 2) {
-				ebt_check_option(&replace.flags, OPT_LOGICALIN);
-				if (replace.selected_chain > 2 &&
-				   replace.selected_chain < NF_BR_BROUTING)
-					ebt_print_error("Use logical "
-					   "in-interface "
-					   "only in INPUT, FORWARD, "
-					   "PREROUTING and BROUTING chains");
-				if (ebt_check_inverse(optarg))
+			} 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 (optind > argc)
-					ebt_print_error("No logical "
-					   "in-interface specified");
-				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
-					print_if_l_error;
-				strcpy(new_entry->logical_in, argv[optind - 1]);
-				parse_iface(new_entry->logical_in,
-				            "--logical-in");
+				if (strlen(optarg) >= IFNAMSIZ)
+					ebt_print_error2("Interface name length must be less than %d", IFNAMSIZ);
+				strcpy(new_entry->logical_in, optarg);
+				if (parse_iface(new_entry->logical_in, "--logical-in"))
+					return -1;
 				break;
-			}
-			if (c == 'o') {
-				ebt_check_option(&replace.flags, OPT_OUT);
-				if (replace.selected_chain < 2)
-					ebt_print_error("Use out-interface "
-					   "only in OUTPUT, FORWARD and "
-					   "POSTROUTING chains");
-				if (ebt_check_inverse(optarg))
+			} 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 (optind > argc)
-					ebt_print_error("No out-interface "
-					            "specified");
-
-				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
-					print_if_l_error;
-				strcpy(new_entry->out, argv[optind - 1]);
-				parse_iface(new_entry->out, "-o");
+				if (strlen(optarg) >= IFNAMSIZ)
+					ebt_print_error2("Interface name length must be less than %d", IFNAMSIZ);
+				strcpy(new_entry->out, optarg);
+				if (parse_iface(new_entry->out, "-o"))
+					return -1;
 				break;
-			}
-			if (c == 3) {
-				ebt_check_option(&replace.flags,
-						 OPT_LOGICALOUT);
-				if (replace.selected_chain < 2)
-					ebt_print_error("Use logical "
-					   "out-interface "
-					   "only in OUTPUT, FORWARD and "
-					   "POSTROUTING chains");
-				if (ebt_check_inverse(optarg))
+			} else if (c == 3) {
+				ebt_check_option2(&(replace->flags), OPT_LOGICALOUT);
+				if (replace->selected_chain < 2)
+					ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
+				if (ebt_check_inverse2(optarg))
 					new_entry->invflags |= EBT_ILOGICALOUT;
 
-				if (optind > argc)
-					ebt_print_error("No logical "
-					   "out-interface specified");
-
-				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
-					print_if_l_error;
-				strcpy(new_entry->logical_out,
-				   argv[optind - 1]);
-				parse_iface(new_entry->logical_out,
-				         "--logical-out");
+				if (strlen(optarg) >= IFNAMSIZ)
+					ebt_print_error2("Interface name length must be less than %d", IFNAMSIZ);
+				strcpy(new_entry->logical_out, optarg);
+				if (parse_iface(new_entry->logical_out, "--logical-out"))
+					return -1;    
 				break;
-			}
-			if (c == 'j') {
-				ebt_check_option(&replace.flags, OPT_JUMP);
+			} 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;
+					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) {
-					if (replace.selected_chain <
-					    NF_BR_NUMHOOKS)
-						ebt_print_error("Return target"
-						" only for user defined "
-						"chains");
-				}
-				if (i != NUM_STANDARD_TARGETS)
+				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 = ebt_get_chainnr(replace, optarg)) != -1) {
 					if (i < NF_BR_NUMHOOKS)
-						ebt_print_error("don't jump"
-						  " to a standard chain");
+						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;
+					((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS;
 					break;
 				} else {
-					/*
-					 * must be an extension then
-					 */
+					/* 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_error("Illegal "
-						   "target name");
-					new_entry->t =
-					   (struct ebt_entry_target *)t;
+					/* -j standard not allowed either */
+					if (!t || t == (struct ebt_u_target *)new_entry->t)
+						ebt_print_error2("Illegal target name");
+					new_entry->t = (struct ebt_entry_target *)t;
+					ebt_find_target(EBT_STANDARD_TARGET)->used = 0;
+					t->used = 1;
 				}
 				break;
-			}
-			if (c == 's') {
-				ebt_check_option(&replace.flags, OPT_SOURCE);
-				if (ebt_check_inverse(optarg))
+			} else if (c == 's') {
+				ebt_check_option2(&(replace->flags), OPT_SOURCE);
+				if (ebt_check_inverse2(optarg))
 					new_entry->invflags |= EBT_ISOURCE;
 
-				if (optind > argc)
-					ebt_print_error("No source mac "
-					            "specified");
-				if (ebt_get_mac_and_mask(argv[optind - 1],
-				   new_entry->sourcemac, new_entry->sourcemsk))
-					ebt_print_error("Problem with "
-					   "specified source mac");
+				if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk))
+					ebt_print_error2("Problem with specified source mac");
 				new_entry->bitmask |= EBT_SOURCEMAC;
 				break;
-			}
-			if (c == 'd') {
-				ebt_check_option(&replace.flags, OPT_DEST);
-				if (ebt_check_inverse(optarg))
+			} else if (c == 'd') {
+				ebt_check_option2(&(replace->flags), OPT_DEST);
+				if (ebt_check_inverse2(optarg))
 					new_entry->invflags |= EBT_IDEST;
 
-				if (optind > argc)
-					ebt_print_error("No destination mac "
-					            "specified");
-				if (ebt_get_mac_and_mask(argv[optind - 1],
-				   new_entry->destmac, new_entry->destmsk))
-					ebt_print_error("Problem with "
-					   "specified destination mac");
+				if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk))
+					ebt_print_error2("Problem with specified destination mac");
 				new_entry->bitmask |= EBT_DESTMAC;
 				break;
 			}
-			ebt_check_option(&replace.flags, OPT_PROTOCOL);
-			if (ebt_check_inverse(optarg))
+			ebt_check_option2(&(replace->flags), OPT_PROTOCOL);
+			if (ebt_check_inverse2(optarg))
 				new_entry->invflags |= EBT_IPROTO;
 
-			if (optind > argc)
-				ebt_print_error("No protocol specified");
 			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
-			i = strtol(argv[optind - 1], &buffer, 16);
+			i = strtol(optarg, &buffer, 16);
 			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
-				ebt_print_error("Problem with the specified "
-				            "protocol");
-			new_entry->ethproto = i;
+				ebt_print_error2("Problem with the specified protocol");
 			if (*buffer != '\0') {
 				struct ethertypeent *ent;
 
-				if (!strcasecmp(argv[optind - 1], "LENGTH")) {
+				if (!strcasecmp(optarg, "LENGTH")) {
 					new_entry->bitmask |= EBT_802_3;
 					break;
 				}
-				ent = getethertypebyname(argv[optind - 1]);
+				ent = getethertypebyname(optarg);
 				if (!ent)
-					print_epoto_error(argv[optind - 1]);
+					ebt_print_error2("Problem with the specified Ethernet protocol (%s), perhaps "_PATH_ETHERTYPES " is missing", optarg)
 				new_entry->ethproto = ent->e_ethertype;
-			}
-			if (new_entry->ethproto < 1536 &&
-			   !(new_entry->bitmask & EBT_802_3))
-				ebt_print_error("Sorry, protocols have values "
-						"above or equal to 0x0600");
-			break;
+			} 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 */
-			ebt_check_option(&replace.flags, LIST_C);
-			if (replace.command != 'L')
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lc is not supported in daemon mode");
+			ebt_check_option2(&(replace->flags), LIST_C);
+			if (replace->command != 'L')
 				ebt_print_error("Use --Lc with -L");
-			if (replace.flags & LIST_X)
-				ebt_print_error("--Lx not compatible with "
-						"--Lc");
-			replace.flags |= LIST_C;
+			if (replace->flags & LIST_X)
+				ebt_print_error2("--Lx is not compatible with --Lc");
+			replace->flags |= LIST_C;
 			break;
 		case 5  : /* Ln */
-			ebt_check_option(&replace.flags, LIST_N);
-			if (replace.command != 'L')
-				ebt_print_error("Use --Ln with -L");
-			if (replace.flags & LIST_X)
-				ebt_print_error("--Lx not compatible with "
-						"--Ln");
-			replace.flags |= LIST_N;
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Ln is not supported in daemon mode");
+			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 */
-			ebt_check_option(&replace.flags, LIST_X);
-			if (replace.command != 'L')
-				ebt_print_error("Use --Lx with -L");
-			if (replace.flags & LIST_C)
-				ebt_print_error("--Lx not compatible with "
-						"--Lc");
-			if (replace.flags & LIST_N)
-				ebt_print_error("--Lx not compatible with "
-						"--Ln");
-			replace.flags |= LIST_X;
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lx is not supported in daemon mode");
+			ebt_check_option2(&(replace->flags), LIST_X);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Lx with -L");
+			if (replace->flags & LIST_C)
+				ebt_print_error2("--Lx is not compatible with --Lc");
+			if (replace->flags & LIST_N)
+				ebt_print_error2("--Lx is not compatible with --Ln");
+			replace->flags |= LIST_X;
 			break;
 		case 12 : /* Lmac2 */
-			ebt_check_option(&replace.flags, LIST_MAC2);
-			if (replace.command != 'L')
-				ebt_print_error("Use --Lmac2 with -L");
-			replace.flags |= LIST_MAC2;
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error("--Lmac2 is not supported in daemon mode");
+			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 */
-			replace.command = c;
-			if (replace.flags & OPT_COMMAND)
-				ebt_print_error("Multiple commands not "
-						"allowed");
-			replace.flags |= OPT_COMMAND;
-			if (!replace.filename)
-				ebt_print_error("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,
+			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;
+			 * 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 */
-			replace.command = c;
-			if (replace.flags & OPT_COMMAND)
-				ebt_print_error("Multiple commands not "
-						"allowed");
-			if (c != 11 && !replace.filename)
-				ebt_print_error("No atomic file specified");
-			replace.flags |= OPT_COMMAND;
+			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;
+				char *tmp = replace->filename;
 				int init = 1;
 
 				if (c == 10)
 					init = 0;
-				tmp = replace.filename;
-				/* get the kernel table */
-				replace.filename = NULL;
-				ebt_get_kernel_table(&replace, table, init);
-				replace.filename = tmp;
+				/* Get the kernel table */
+				replace->filename = NULL;
+				ebt_get_kernel_table(replace, init);
+				replace->filename = tmp;
 			}
 			break;
 		case 9 : /* atomic */
-			if (replace.flags & OPT_COMMAND)
-				ebt_print_error("--atomic has to come before"
-						" the command");
-			/* another possible memory leak here */
-			replace.filename = (char *)malloc(strlen(optarg) + 1);
-			strcpy(replace.filename, optarg);
+			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_inverse(optarg);
+				ebt_check_inverse2(optarg);
 			else
-				ebt_print_error("Bad argument : %s", optarg);
-			/*
-			 * ebt_check_inverse() did optind++
-			 */
+				ebt_print_error2("Bad argument : %s", optarg);
+			/* ebt_check_inverse() did optind++ */
 			optind--;
 			continue;
 		default:
-			/*
-			 * is it a target option?
-			 */
+			/* 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 ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t)))
 				goto check_extension;
 
-			/*
-			 * is it a match_option?
-			 */
+			/* 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))
+				if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
 					break;
 
 			if (m != NULL) {
@@ -1105,109 +985,107 @@
 				goto check_extension;
 			}
 
-			/*
-			 * is it a watcher option?
-			 */
+			/* 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))
+				if (w->parse(c-w->option_offset, argv, argc, new_entry, &w->flags, &w->w))
 					break;
 
 			if (w == NULL)
-				ebt_print_error("Unknown argument");
+				ebt_print_error2("Unknown argument: '%s', %c, '%c'", argv[optind - 1], (char)optopt, (char)c);
 			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')
-				ebt_print_error("Extensions only for -A, "
-						"-I and -D");
+			if (replace->command != 'A' && replace->command != 'I' && replace->command != 'D')
+				ebt_print_error2("Extensions only for -A, -I and -D");
 		}
 		ebt_invert = 0;
 	}
 
-	if ( !table && !(table = ebt_find_table(replace.name)) )
-		ebt_print_error("Bad table name");
+	/* Just in case we didn't catch an error */
+	if (ebt_errormsg[0] != '\0')
+		return -1;
 
-	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
-	   replace.flags & OPT_ZERO )
-		ebt_print_error("Command -Z only allowed together with "
-				"command -L");
+	if (!(table = ebt_find_table(replace->name)))
+		ebt_print_error2("Bad table name");
 
-	/*
-	 * do this after parsing everything, so we can print specific info
-	 */
-	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+	if (replace->command == 'h' && !(replace->flags & OPT_ZERO))
 		print_help();
 
-	/*
-	 * do the final checks
-	 */
-	if (replace.command == 'A' || replace.command == 'I' ||
-	   replace.command == 'D') {
-		/*
-		 * this will put the hook_mask right for the chains
-		 */
-		ebt_check_for_loops(&replace);
-		entries = ebt_to_chain(&replace);
+	/* Do the final checks */
+	if (replace->command == 'A' || replace->command == 'I' ||
+	   replace->command == 'D') {
+		/* 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,
+			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,
+			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,
+		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
-	 */
+	/* 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_error("Policy RETURN only allowed for user "
-					"defined chains");
-		ebt_change_policy(&replace, policy);
-	} else if (replace.command == 'L') {
+	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) {
-			replace.selected_chain = zerochain;
-			ebt_zero_counters(&replace);
-		} else
+		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);
-		ebt_check_for_loops(&replace);
-		/*
-		 * do the final_check(), for all entries
-		 * needed when adding a rule that has a chain target
-		 */
+	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;
+
+		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 (1) {
 			struct ebt_u_entry *e;
 
 			i++;
-			entries = ebt_nr_to_chain(&replace, i);
+			entries = ebt_nr_to_chain(replace, i);
 			if (!entries) {
 				if (i < NF_BR_NUMHOOKS)
 					continue;
@@ -1216,28 +1094,33 @@
 			}
 			e = entries->entries;
 			while (e) {
-				/*
-				 * userspace extensions use host endian
-				 */
+				/* Userspace extensions use host endian */
 				e->ethproto = ntohs(e->ethproto);
-				ebt_do_final_checks(&replace, e, entries);
+				ebt_do_final_checks(replace, e, entries);
+				if (ebt_errormsg[0] != '\0')
+					goto delete_the_rule;
 				e->ethproto = htons(e->ethproto);
 				e = e->next;
 			}
 		}
-	} else if (replace.command == 'D')
-		ebt_delete_rule(&replace, new_entry, rule_nr, rule_nr_end);
-	/*
-	 * commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
-	 * --init-table fall through
-	 */
+		/* Don't reuse the added rule */
+		new_entry = NULL;
+	} else if (replace->command == 'D')
+delete_the_rule: /* This needs to be followed by a check on ebt_errormsg[0] */
+		ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end);
+	/* 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);
+		table->check(replace);
 
-	ebt_deliver_table(&replace);
+	if (exec_style == EXEC_STYLE_PRG) {/* Implies ebt_errormsg[0] == '\0' */
+		ebt_deliver_table(replace);
 
-	if (replace.counterchanges)
-		ebt_deliver_counters(&replace);
+		if (replace->counterchanges)
+			ebt_deliver_counters(replace, exec_style);
+	}
 	return 0;
 }
diff --git a/ebtablesd.c b/ebtablesd.c
new file mode 100644
index 0000000..1f60791
--- /dev/null
+++ b/ebtablesd.c
@@ -0,0 +1,367 @@
+/*
+ * ebtablesd.c, January 2005
+ *
+ * Author: Bart De Schuymer
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "include/ebtables_u.h"
+
+#define OPT_KERNELDATA	0x800 /* Also defined in ebtables.c */
+
+static struct ebt_u_replace replace[3];
+#define OPEN_METHOD_FILE 1
+#define OPEN_METHOD_KERNEL 2
+static int open_method[3];
+void ebt_early_init_once();
+
+static void sigpipe_handler(int sig)
+{
+}
+
+int main(int argc_, char *argv_[])
+{
+	char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir",
+	     mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR,
+	     cmdline[EBTD_CMDLINE_MAXLN];
+	int readfd, stop = 0, base = 0, offset = 0, n = 0, ret = 0;
+
+	/* Make sure the pipe directory exists */
+	args[0] = name;
+	args[1] = mkdir_option;
+	args[2] = mkdir_dir;
+	args[3] = NULL;
+	switch (fork()) {
+	case 0:
+		execvp(args[0], args);
+
+		/* Not usually reached */
+		exit(0);
+	case -1:
+		return -1;
+
+	default: /* Parent */
+		wait(NULL);
+	}
+
+	if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) {
+		printf("Error creating FIFO " EBTD_PIPE "\n");
+		ret = -1;
+		goto do_exit;
+	}
+
+	if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) {
+		perror("open");
+		ret = -1;
+		goto do_exit;
+	}
+
+	if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) {
+		perror("signal");
+		ret = -1;
+		goto do_exit;
+	}
+
+	ebt_silent = 1;
+
+	strcpy(replace[0].name, "filter");
+	strcpy(replace[1].name, "nat");
+	strcpy(replace[2].name, "broute");
+
+	ebt_early_init_once();
+
+	while (!stop) {
+		int n2, i, argc, table_nr, ntot;
+
+		/* base == 0 */
+		ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1);
+		if (ntot <= 0)
+			continue;
+		ntot += offset;
+continue_read:
+		/* Change all ' ' into '\0'. This implies that the user is not
+		 * allowed to use spaces (that don't distinguish options or
+		 * commands) in her rules. This comes down to not allowing spaces
+		 * in options like the string of --ulog-prefix (use '_' instead).
+		 */
+		for (; offset < ntot; n++, offset++) {
+			if (cmdline[offset] == ' ')
+				cmdline[offset] = '\0';
+			if (cmdline[offset] == '\n') {
+				cmdline[offset] = '\0';
+				break;
+			}
+		}
+		if (n == 0) {
+			if (offset == ntot) {
+				/* The ntot bytes were parsed and ended with '\n' */
+				base = 0;
+				offset = 0;
+				continue;
+			}
+			offset++;
+			base = offset;
+			n = 0;
+			goto continue_read;
+		}
+		if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */
+			if (base == 0) {
+				ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1);
+				goto write_msg;
+			}
+			memmove(cmdline, cmdline+base+offset, ntot-offset);
+			offset -= base;
+			offset++;
+			base = 0;
+			continue;
+		}
+
+		table_nr = 0;
+		n2 = 0;
+		argc = 0;
+		while (n2 < n && argc < EBTD_ARGC_MAX) {
+			argv[argc++] = cmdline + base + n2;
+			n2 += strlen(cmdline + base + n2) + 1;
+		}
+		offset++; /* Move past the '\n' */
+		base = offset;
+
+		if (argc > EBTD_ARGC_MAX) {
+			ebt_print_error("ebtablesd: maximum %d arguments "
+			                "allowed", EBTD_ARGC_MAX - 1);
+			goto write_msg;
+		}
+		if (argc == 1) {
+			ebt_print_error("ebtablesd: no arguments");
+			goto write_msg;
+		}
+
+		/* Parse the options */
+		if (!strcmp(argv[1], "-t")) {
+			if (argc < 3) {
+				ebt_print_error("ebtablesd: -t but no table");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			table_nr = i;
+		} else if (!strcmp(argv[1], "free")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command free "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			ebt_cleanup_replace(&replace[i]);
+			replace[i].flags &= ~OPT_KERNELDATA;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "open")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command open "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			if (!ebt_get_kernel_table(&replace[i], 0)) {
+				replace[i].flags |= OPT_KERNELDATA;
+				open_method[i] = OPEN_METHOD_KERNEL;
+			}
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fopen")) {
+			struct ebt_u_replace tmp;
+
+			memset(&tmp, 0, sizeof(tmp));
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command fopen "
+				                "needs exactly two arguments");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			tmp.filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!tmp.filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(tmp.filename, argv[3]);
+			strcpy(tmp.name, "filter");
+			tmp.command = 'L'; /* Make sure retrieve_from_file()
+			                    * doesn't complain about wrong
+			                    * table name */
+
+			ebt_get_kernel_table(&tmp, 0);
+			free(tmp.filename);
+			tmp.filename = NULL;
+			if (ebt_errormsg[0] != '\0')
+				goto write_msg;
+
+			if (strcmp(tmp.name, argv[2])) {
+				ebt_print_error("ebtablesd: opened file with "
+				                "wrong table name '%s'", tmp.name);
+				ebt_cleanup_replace(&tmp);
+				goto write_msg;
+			}
+			replace[i] = tmp;
+			replace[i].command = '\0';
+			replace[i].flags |= OPT_KERNELDATA;
+			open_method[i] = OPEN_METHOD_FILE;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "commit")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			/* The counters from the kernel are useless if we 
+			 * didn't start from a kernel table */
+			if (open_method[i] == OPEN_METHOD_FILE)
+				replace[i].num_counters = 0;
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i], EXEC_STYLE_DAEMON);
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fcommit")) {
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly two argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			replace[i].filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!replace[i].filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(replace[i].filename, argv[3]);
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i], EXEC_STYLE_DAEMON);
+			free(replace[i].filename);
+			replace[i].filename = NULL;
+			goto write_msg;
+		}else if (!strcmp(argv[1], "quit")) {
+			if (argc != 2) {
+				ebt_print_error("ebtablesd: command quit does "
+				                "not take any arguments");
+				goto write_msg;
+			}
+			stop = 1;
+			goto write_msg;
+		}
+		if (!(replace[table_nr].flags & OPT_KERNELDATA)) {
+			ebt_print_error("ebtablesd: table %s has not been "
+			                "opened", replace[table_nr].name);
+			goto write_msg;
+		}
+		optind = 1;
+printf("jak: ntot = %d, offset = %d, base = %d\n", ntot, offset, base);
+		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
+		ebt_reinit_extensions();
+write_msg:
+#ifndef SILENT_DAEMON
+		if (ebt_errormsg[0] != '\0')
+			printf("%s\n", ebt_errormsg);
+#endif
+		ebt_errormsg[0]= '\0';
+		n = 0;
+		goto continue_read;
+	}
+do_exit:
+	unlink(EBTD_PIPE);
+	
+	return 0;
+}
diff --git a/ebtablesu.c b/ebtablesu.c
new file mode 100644
index 0000000..234d4f6
--- /dev/null
+++ b/ebtablesu.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int main(int argc, char *argv[])
+{
+	char *arguments, *pos;
+	int i, writefd, len = 0;
+
+	if ((writefd = open(EBTD_PIPE, O_WRONLY, 0)) == -1) {
+		perror("open");
+		return -1;
+	}
+
+	if (argc > EBTD_ARGC_MAX) {
+		printf("ebtablesd accepts at most %d arguments, %d arguments "
+		       "were specified. If you need this many arguments, "
+		       "recompile this tool with a higher value for "
+		       "EBTD_ARGC_MAX.\n", EBTD_ARGC_MAX - 1, argc - 1);
+		return -1;
+	} else if (argc == 1) {
+		printf("At least one argument is needed.\n");
+		return -1;
+	}
+
+	for (i = 0; i < argc; i++)
+		len += strlen(argv[i]);
+	/* Don't forget '\0' */
+	len += argc;
+	if (len > EBTD_CMDLINE_MAXLN) {
+		printf("ebtablesd has a maximum command line argument length "
+		       "of %d, an argument length of %d was received. If a "
+		       "smaller length is unfeasible, recompile this tool "
+		       "with a higher value for EBTD_CMDLINE_MAXLN.\n",
+		       EBTD_CMDLINE_MAXLN, len);
+		return -1;
+	}
+
+	if (!(arguments = (char *)malloc(len))) {
+		printf("ebtablesu: out of memory.\n");
+		return -1;
+	}
+
+	pos = arguments;
+	for (i = 0; i < argc; i++) {
+		strcpy(pos, argv[i]);
+		pos += strlen(argv[i]) + 1;
+	}
+
+	*(pos-1) = '\n';
+	if (write(writefd, arguments, len) == -1) {
+		perror("write");
+		return -1;
+	}
+	return 0;
+}
diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
index 6d9233e..c81e687 100644
--- a/extensions/ebt_ip.c
+++ b/extensions/ebt_ip.c
@@ -209,7 +209,7 @@
 			ipinfo->invflags |= EBT_IP_PROTO;
 		if (optind > argc)
 			ebt_print_error("Missing IP protocol argument");
-		(unsigned char) i = strtoul(argv[optind - 1], &end, 10);
+		i = strtoul(argv[optind - 1], &end, 10);
 		if (*end != '\0') {
 			struct protoent *pe;
 
diff --git a/extensions/ebt_limit.c b/extensions/ebt_limit.c
index 1c5c32b..6dcd50d 100644
--- a/extensions/ebt_limit.c
+++ b/extensions/ebt_limit.c
@@ -203,15 +203,15 @@
 
 static struct ebt_u_match limit_match =
 {
-	.name		EBT_LIMIT_MATCH,
-	.size		sizeof(struct ebt_limit_info),
-	.help		print_help,
-	.init		init,
-	.parse		parse,
-	.final_check	final_check,
-	.print		print,
-	.compare	compare,
-	.extra_ops	opts,
+	.name		= EBT_LIMIT_MATCH,
+	.size		= sizeof(struct ebt_limit_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
 };
 
 void _init(void)
diff --git a/extensions/ebt_vlan.c b/extensions/ebt_vlan.c
index b4e1d38..0c94a66 100644
--- a/extensions/ebt_vlan.c
+++ b/extensions/ebt_vlan.c
@@ -141,8 +141,7 @@
 		ebt_check_option(flags, OPT_VLAN_ID);
 		CHECK_INV_FLAG(EBT_VLAN_ID);
 		CHECK_IF_MISSING_VALUE;
-		(unsigned short) local.id =
-		    strtoul(argv[optind - 1], &end, 10);
+		local.id = strtoul(argv[optind - 1], &end, 10);
 		CHECK_RANGE(local.id > 4094 || *end != '\0');
 		vlaninfo->id = local.id;
 		SET_BITMASK(EBT_VLAN_ID);
@@ -152,8 +151,7 @@
 		ebt_check_option(flags, OPT_VLAN_PRIO);
 		CHECK_INV_FLAG(EBT_VLAN_PRIO);
 		CHECK_IF_MISSING_VALUE;
-		(unsigned char) local.prio =
-		    strtoul(argv[optind - 1], &end, 10);
+		local.prio = strtoul(argv[optind - 1], &end, 10);
 		CHECK_RANGE(local.prio >= 8 || *end != '\0');
 		vlaninfo->prio = local.prio;
 		SET_BITMASK(EBT_VLAN_PRIO);
@@ -163,8 +161,7 @@
 		ebt_check_option(flags, OPT_VLAN_ENCAP);
 		CHECK_INV_FLAG(EBT_VLAN_ENCAP);
 		CHECK_IF_MISSING_VALUE;
-		(unsigned short) local.encap =
-		    strtoul(argv[optind - 1], &end, 16);
+		local.encap = strtoul(argv[optind - 1], &end, 16);
 		if (*end != '\0') {
 			ethent = getethertypebyname(argv[optind - 1]);
 			if (ethent == NULL)
diff --git a/include/ebtables_u.h b/include/ebtables_u.h
index 5b040d2..d5264e3 100644
--- a/include/ebtables_u.h
+++ b/include/ebtables_u.h
@@ -26,6 +26,9 @@
 #include <netinet/in.h>
 #include <linux/netfilter_bridge/ebtables.h>
 
+#define EXEC_STYLE_PRG		0
+#define EXEC_STYLE_DAEMON	1
+
 #ifndef EBT_MIN_ALIGN
 #define EBT_MIN_ALIGN (__alignof__(struct ebt_entry_target))
 #endif
@@ -223,8 +226,7 @@
 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);
-void ebt_get_kernel_table(struct ebt_u_replace *replace,
-			  struct ebt_u_table *table, int init);
+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);
@@ -232,6 +234,8 @@
 int ebtables_insmod(const char *modname);
 void ebt_list_extensions();
 void ebt_initialize_entry(struct ebt_u_entry *e);
+void ebt_cleanup_replace(struct ebt_u_replace *replace);
+void ebt_reinit_extensions();
 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);
@@ -257,8 +261,9 @@
 /**/
 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 ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr);
+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);
@@ -271,7 +276,7 @@
 /* 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_counters(struct ebt_u_replace *repl, int exec_style);
 void ebt_deliver_table(struct ebt_u_replace *repl);
 
 /* useful_functions.c */
@@ -285,6 +290,9 @@
 void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
 char *ebt_mask_to_dotted(uint32_t mask);
 
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_);
+
 struct ethertypeent *parseethertypebynumber(int type);
 
 #define ebt_print_bug(format, args...) \
diff --git a/libebtc.c b/libebtc.c
index dfc277b..0fef235 100644
--- a/libebtc.c
+++ b/libebtc.c
@@ -40,9 +40,7 @@
 static void remove_udc(struct ebt_u_replace *replace);
 static int iterate_entries(struct ebt_u_replace *replace, int type);
 
-/*
- * The standard names
- */
+/* The standard names */
 const char *ebt_hooknames[NF_BR_NUMHOOKS] =
 {
 	[NF_BR_PRE_ROUTING]"PREROUTING",
@@ -53,9 +51,7 @@
 	[NF_BR_BROUTING]"BROUTING"
 };
 
-/*
- * The four target names
- */
+/* The four target names */
 const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
 {
 	"ACCEPT",
@@ -64,22 +60,18 @@
 	"RETURN",
 };
 
-/*
- * The lists of supported tables, matches, watchers and targets
- */
+/* 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
- */
+/* 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))
+	while (t && strcmp(t->name, name))
 		t = t->next;
 	return t;
 }
@@ -88,7 +80,7 @@
 {
 	struct ebt_u_match *m = ebt_matches;
 
-	while(m && strcmp(m->name, name))
+	while (m && strcmp(m->name, name))
 		m = m->next;
 	return m;
 }
@@ -97,7 +89,7 @@
 {
 	struct ebt_u_watcher *w = ebt_watchers;
 
-	while(w && strcmp(w->name, name))
+	while (w && strcmp(w->name, name))
 		w = w->next;
 	return w;
 }
@@ -111,9 +103,7 @@
 	return t;
 }
 
-/*
- * Prints all registered extensions
- */
+/* Prints all registered extensions */
 void ebt_list_extensions()
 {
 	struct ebt_u_table *tbl = ebt_tables;
@@ -123,59 +113,51 @@
 
 	PRINT_VERSION;
 	printf("Supported userspace extensions:\n\nSupported tables:\n");
-        while(tbl) {
+        while (tbl) {
 		printf("%s\n", tbl->name);
                 tbl = tbl->next;
 	}
 	printf("\nSupported targets:\n");
-        while(t) {
+        while (t) {
 		printf("%s\n", t->name);
                 t = t->next;
 	}
 	printf("\nSupported matches:\n");
-        while(m) {
+        while (m) {
 		printf("%s\n", m->name);
                 m = m->next;
 	}
 	printf("\nSupported watchers:\n");
-        while(w) {
+        while (w) {
 		printf("%s\n", w->name);
                 w = w->next;
 	}
-	exit(0);
 }
 
-/*
- * Get the table from the kernel or from a binary file
+/* 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
- */
-void ebt_get_kernel_table(struct ebt_u_replace *replace,
-			  struct ebt_u_table *table, int init)
+ *       0 = get the current data in the table */
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
 {
-	if ( !(table = ebt_find_table(replace->name)) )
+	if (!ebt_find_table(replace->name)) {
 		ebt_print_error("Bad table name");
-	/*
-	 * get the kernel's information
-	 */
-	if (ebt_get_table(replace, init)) {
-		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;
 	}
-	/*
-	 * when listing a table contained in a file, we don't demand that
-	 * the user knows the table's name, so we update table if necessary
-	 */
-	if ( !(table = ebt_find_table(replace->name)) )
-		ebt_print_error("Bad table name");
+	/* 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
- */
+/* Put sane values into a new entry */
 void ebt_initialize_entry(struct ebt_u_entry *e)
 {
 	e->bitmask = EBT_NOPROTO;
@@ -188,16 +170,14 @@
 	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;
 
 	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;
+	((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE;
 }
 
-/*
- * replace is reborn, i.e. rebirth of replace
- */
+/* 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;
@@ -245,6 +225,7 @@
 	}
 	udc1 = replace->udc;
 	while (udc1) {
+		free(udc1->udc);
 		udc2 = udc1->next;
 		free(udc1);
 		udc1 = udc2;
@@ -259,10 +240,8 @@
 	replace->counterchanges = NULL;
 }
 
-/*
- * Should be called between 2 rule adds, f.e.
- */
-void ebt_reinit_extensions(struct ebt_u_replace *replace)
+/* Should be called, e.g., between 2 rule adds */
+void ebt_reinit_extensions()
 {
 	struct ebt_u_match *m;
 	struct ebt_u_watcher *w;
@@ -273,42 +252,46 @@
 	 * called for the first time or not (when necessary). */
 	for (m = ebt_matches; m; m = m->next) {
 		size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
-		if (m->m)
-			free(m->m);
-		m->m = (struct ebt_entry_match *)malloc(size);
-		if (!m->m)
-			ebt_print_memory();
-		m->used = 0;
-		m->flags = 0;
+		if (m->used) {
+			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;
+		}
 		m->init(m->m);
 	}
 	for (w = ebt_watchers; w; w = w->next) {
 		size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
-		if (w->w)
-			free(w->w);
-		w->w = (struct ebt_entry_watcher *)malloc(size);
-		if (!w->w)
-			ebt_print_memory();
-		w->used = 0;
-		w->flags = 0;
+		if (w->used) {
+			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; m; t = t->next) {
+	for (t = ebt_targets; t; t = t->next) {
 		size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
-		if (t->t)
-			free(t->t);
-		t->t = (struct ebt_entry_target *)malloc(size);
-		if (!t->t)
-			ebt_print_memory();
-		t->used = 0;
-		t->flags = 0;
+		if (t->used) {
+			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, becoz the calling function might need e->next
- */
+/* 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;
@@ -331,10 +314,8 @@
 	free(e->t);
 }
 
-/*
- * Blatently stolen (again) from iptables.c userspace program
- * find out where the modprobe utility is located
- */
+/* Blatently stolen (again) from iptables.c userspace program
+ * find out where the modprobe utility is located */
 static char *get_modprobe(void)
 {
 	int procfile;
@@ -350,8 +331,8 @@
 		case -1: goto fail;
 		case 1024: goto fail; /* Partial read.  Wierd */
 		}
-		if (ret[strlen(ret)-1]=='\n')
-			ret[strlen(ret)-1]=0;
+		if (ret[strlen(ret)-1] == '\n')
+			ret[strlen(ret)-1] = 0;
 		close(procfile);
 		return ret;
 	}
@@ -362,9 +343,7 @@
 }
 
 char *ebt_modprobe;
-/*
- * Try to load the kernel module
- */
+/* Try to load the kernel module */
 int ebtables_insmod(const char *modname)
 {
 	char *buf = NULL;
@@ -385,12 +364,12 @@
 		argv[2] = NULL;
 		execv(argv[0], argv);
 
-		/* not usually reached */
+		/* Not usually reached */
 		exit(0);
 	case -1:
 		return -1;
 
-	default: /* parent */
+	default: /* Parent */
 		wait(NULL);
 	}
 
@@ -398,11 +377,9 @@
 	return 0;
 }
 
-/*
- * Gives back a pointer to the chain base, based on nr.
+/* Gives back a pointer to the chain base, based on nr.
  * If nr >= NF_BR_NUMHOOKS you'll get back a user-defined chain.
- * Returns NULL on failure.
- */
+ * Returns NULL on failure. */
 struct ebt_u_entries *ebt_nr_to_chain(const struct ebt_u_replace *replace,
 				       int nr)
 {
@@ -426,20 +403,16 @@
 	}
 }
 
-/*
- * Gives back a pointer to the chain base of selected_chain
- */
+/* Gives back a pointer to the chain base of selected_chain */
 struct ebt_u_entries *ebt_to_chain(const struct ebt_u_replace *replace)
 {
 	return ebt_nr_to_chain(replace, replace->selected_chain);
 }
 
-/*
- * Parse the chain name and return a pointer to the chain base.
- * Returns NULL on failure.
- */
+/* 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)
+					const char* arg)
 {
 	int i;
 	struct ebt_u_chain_list *cl = replace->udc;
@@ -458,10 +431,8 @@
 	return NULL;
 }
 
-/*
- * Parse the chain name and return the corresponding chain nr
- * returns -1 on failure
- */
+/* 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;
@@ -490,23 +461,19 @@
 ************
      */
 
-/*
- * Chainge the policy of selected_chain.
- * No sanity checks are done.
- */
+/* 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);
+		ebt_print_bug("Wrong policy: %d", policy);
 	entries->policy = policy;
 }
 
-/*
- * Flush one chain or the complete table
- * If selected_chain == -1: flush the complete table
- */
+/* Flush one chain or the complete table
+ * If selected_chain == -1: flush the complete table */
 void ebt_flush_chains(struct ebt_u_replace *replace)
 {
 	int i, j, numdel;
@@ -515,17 +482,13 @@
 	struct ebt_cntchanges *cc = replace->counterchanges;
 	struct ebt_cntchanges **prev_cc =  &(replace->counterchanges);
 
-	/*
-	 * flush whole table
-	 */
+	/* Flush whole table */
 	if (!entries) {
 		if (replace->nentries == 0)
 			return;
 		replace->nentries = 0;
 
-		/*
-		 * free everything and zero (n)entries
-		 */
+		/* Free everything and zero (n)entries */
 		i = -1;
 		while (1) {
 			i++;
@@ -547,9 +510,7 @@
 				u_e = tmp;
 			}
 		}
-		/*
-		 * update the counters
-		 */
+		/* Update the counters */
 		while (cc) {
 			if (cc->type == CNT_ADD) {
 				*prev_cc = cc->next;
@@ -569,10 +530,8 @@
 	replace->nentries -= entries->nentries;
 	numdel = entries->nentries;
 
-	/*
-	 * delete the counters belonging to the specified chain,
-	 * update counter_offset
-	 */
+	/* Delete the counters belonging to the specified chain,
+	 * update counter_offset */
 	i = -1;
 	while (1) {
 		i++;
@@ -589,7 +548,7 @@
 		}
 		j = entries->nentries;
 		while (j) {
-			/* don't count deleted entries */
+			/* Don't count deleted entries */
 			if (cc->type == CNT_DEL)
 				goto letscontinue;
 			if (i == replace->selected_chain) {
@@ -619,15 +578,12 @@
 		u_e = tmp;
 	}
 	entries->entries = NULL;
-	return;
 }
 
-/*
- * returns the rule number on success (starting from 0), -1 on failure
+/* 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}.
- */
+ * 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)
 {
@@ -641,10 +597,8 @@
 	int i, j, k;
 
 	u_e = entries->entries;
-	/*
-	 * check for an existing rule (if there are duplicate rules,
-	 * take the first occurance)
-	 */
+	/* 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)
 			ebt_print_bug("Hmm, trouble");
@@ -659,17 +613,15 @@
 		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))
+		    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))
+		    memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
 			continue;
 		if (new_entry->bitmask != u_e->bitmask ||
-		   new_entry->invflags != u_e->invflags)
+		    new_entry->invflags != u_e->invflags)
 			continue;
-		/*
-		 * compare all matches
-		 */
+		/* Compare all matches */
 		m_l = new_entry->m_list;
 		j = 0;
 		while (m_l) {
@@ -682,9 +634,7 @@
 			j++;
 			m_l = m_l->next;
 		}
-		/*
-		 * now be sure they have the same nr of matches
-		 */
+		/* Now be sure they have the same nr of matches */
 		k = 0;
 		m_l = u_e->m_list;
 		while (m_l) {
@@ -694,9 +644,7 @@
 		if (j != k)
 			continue;
 
-		/*
-		 * compare all watchers
-		 */
+		/* Compare all watchers */
 		w_l = new_entry->w_list;
 		j = 0;
 		while (w_l) {
@@ -732,13 +680,14 @@
  * 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 rule in the chain
+ *               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 before adding the rule to the chain.
- */
+ * 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} 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)
 {
@@ -754,17 +703,15 @@
 		rule_nr += entries->nentries;
 	else
 		rule_nr--;
-	if (rule_nr > entries->nentries || rule_nr < 0)
+	if (rule_nr > entries->nentries || rule_nr < 0) {
 		ebt_print_error("The specified rule number is incorrect");
-	/*
-	 * we're adding one rule
-	 */
+		return;
+	}
+	/* We're adding one rule */
 	replace->nentries++;
 	entries->nentries++;
 
-	/*
-	 * handle counter stuff
-	 */
+	/* Handle counter stuff */
 	for (i = 0; i < replace->selected_chain; i++) {
 		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
 			continue;
@@ -783,32 +730,26 @@
 		prev_cc = &(cc->next);
 		cc = cc->next;
 	}
-	if (cc && cc->type == CNT_DEL) /* The add is victorious and conquers
-					* the delete */
+	if (cc && cc->type == CNT_DEL)
 		cc->type = CNT_OWRITE;
 	else {
 		new_cc = (struct ebt_cntchanges *)
 			 malloc(sizeof(struct ebt_cntchanges));
+		if (!new_cc)
+			ebt_print_memory();
 		new_cc->type = CNT_ADD;
 		new_cc->next = cc;
 		*prev_cc = new_cc;
 	}
-
-	/*
-	 * go to the right position in the chain
-	 */
+	/* Go to the right position in the chain */
 	u_e = &entries->entries;
 	for (i = 0; i < rule_nr; i++)
 		u_e = &(*u_e)->next;
-	/*
-	 * insert the rule
-	 */
+	/* Insert the rule */
 	new_entry->next = *u_e;
 	*u_e = new_entry;
 
-	/*
-	 * put the ebt_[match, watcher, target] pointers in place
-	 */
+	/* 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;
@@ -820,10 +761,7 @@
 		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
-	 */
+	/* Update the counter_offset of chains behind this one */
 	i = replace->selected_chain;
 	while (1) {
 		i++;
@@ -838,14 +776,12 @@
 	}
 }
 
-/*
- * Delete a rule or rules
+/* 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.
+ * 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}.
- */
+ * 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)
 {
@@ -860,8 +796,10 @@
 	if (end < 0)
 		end += entries->nentries + 1;
 
-	if (begin < 0 || begin > end || end > entries->nentries)
+	if (begin < 0 || begin > end || end > entries->nentries) {
 		ebt_print_error("Sorry, wrong rule numbers");
+		return;
+	}
 
 	if ((begin * end == 0) && (begin + end != 0))
 		ebt_print_bug("begin and end should be either both zero, "
@@ -872,20 +810,18 @@
 	} else {
 		begin = ebt_check_rule_exists(replace, new_entry);
 		end = begin;
-		if (begin == -1)
+		if (begin == -1) {
 			ebt_print_error("Sorry, rule does not exist");
+			return;
+		}
 	}
 
-	/*
-	 * we're deleting rules
-	 */
+	/* We're deleting rules */
 	nr_deletes = end - begin + 1;
 	replace->nentries -= nr_deletes;
 	entries->nentries -= nr_deletes;
 
-	/*
-	 * handle counter stuff
-	 */
+	/* Handle counter stuff */
 	for (i = 0; i < replace->selected_chain; i++) {
 		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
 			continue;
@@ -920,27 +856,21 @@
 		cc = cc->next;
 	}
 
-	/*
-	 * go to the right position in the chain
-	 */
+	/* Go to the right position in the chain */
 	u_e = &entries->entries;
 	for (j = 0; j < begin; j++)
 		u_e = &(*u_e)->next;
-	/*
-	 * remove the rules
-	 */
+	/* Remove the rules */
 	j = nr_deletes;
 	while(j--) {
 		u_e2 = *u_e;
 		*u_e = (*u_e)->next;
-		/* free everything */
+		/* Free everything */
 		ebt_free_u_entry(u_e2);
 		free(u_e2);
 	}
 
-	/*
-	 * update the counter_offset of chains behind this one
-	 */
+	/* Update the counter_offset of chains behind this one */
 	j = replace->selected_chain;
 	while (1) {
 		j++;
@@ -955,10 +885,8 @@
 	}
 }
 
-/*
- * Selected_chain == -1 : zero all counters
- * else, zero the counters of selected_chain
- */
+/* Selected_chain == -1 : 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);
@@ -976,8 +904,7 @@
 			return;
 
 		for (i = 0; i < replace->selected_chain; i++) {
-			if (i < NF_BR_NUMHOOKS &&
-			    !(replace->valid_hooks & (1 << i)))
+			if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
 				continue;
 			j = ebt_nr_to_chain(replace, i)->nentries;
 			while (j) {
@@ -998,20 +925,22 @@
 	}
 }
 
-/*
- * Add a new chain and specify its policy
- */
+/* 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_chain_list *cl, **cl2;
 
-	if (ebt_get_chainnr(replace, name) != -1)
+	if (ebt_get_chainnr(replace, name) != -1) {
 		ebt_print_error("Chain %s already exists", optarg);
-	if (ebt_find_target(name))
+		return;
+	} else if (ebt_find_target(name)) {
 		ebt_print_error("Target with name %s exists", optarg);
-	if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
+		return;
+	} else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN) {
 		ebt_print_error("Chain name length can't exceed %d",
 				EBT_CHAIN_MAXNAMELEN - 1);
+		return;
+	}
 	cl = (struct ebt_u_chain_list *)
 	     malloc(sizeof(struct ebt_u_chain_list));
 	if (!cl)
@@ -1028,39 +957,37 @@
 	strcpy(cl->udc->name, name);
 	cl->udc->entries = NULL;
 	cl->kernel_start = NULL;
-	/*
-	 * put the new chain at the end
-	 */
+	/* Put the new chain at the end */
 	cl2 = &(replace->udc);
 	while (*cl2)
 		cl2 = &((*cl2)->next);
 	*cl2 = cl;
 }
 
-/*
- * Selected_chain == -1: delete all non-referenced udc
- * selected_chain < NF_BR_NUMHOOKS is illegal
- */
+/* Selected_chain == -1: delete all non-referenced udc
+ * selected_chain < NF_BR_NUMHOOKS is illegal */
 void ebt_delete_chain(struct ebt_u_replace *replace)
 {
-	int chain_nr = replace->selected_chain;
+	int chain_nr = replace->selected_chain, print_error = 1;
 
 	if (chain_nr != -1 && chain_nr < NF_BR_NUMHOOKS)
 		ebt_print_bug("You can't remove a standard chain");
-	if (chain_nr == -1)
+	if (chain_nr == -1) {
+		print_error = 0;
 		replace->selected_chain = NF_BR_NUMHOOKS;
+	}
 	do {
 		if (ebt_to_chain(replace) == NULL) {
 			if (chain_nr == -1)
 				break;
 			ebt_print_bug("udc nr %d doesn't exist", chain_nr);
 		}
-		/*
-		 * if the chain is referenced, don't delete it,
+		/* If the chain is referenced, don't delete it,
 		 * also decrement jumps to a chain behind the
-		 * one we're deleting
-		 */
-		if (ebt_check_for_references(replace)) {
+		 * one we're deleting */
+		if (ebt_check_for_references(replace, print_error)) {
+			if (chain_nr != -1) 
+				break;
 			replace->selected_chain++;
 			continue;
 		}
@@ -1068,12 +995,10 @@
 		ebt_flush_chains(replace);
 		remove_udc(replace);
 	} while (chain_nr == -1);
-	replace->selected_chain = chain_nr;
+	replace->selected_chain = chain_nr; /* Put back to -1 */
 }
 
-/*
- * Rename an existing chain.
- */
+/* 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);
@@ -1093,11 +1018,10 @@
             */
 
 
-/*
- * executes the final_check() function for all extensions used by the rule
+/* 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.
- */
+ * 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)
 {
@@ -1113,12 +1037,16 @@
 		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);
@@ -1126,26 +1054,29 @@
 	   entries->hook_mask, 1);
 }
 
-/*
- * returns 1 when the chain is referenced,
- * 0 when it isn't.
- */
-int ebt_check_for_references(struct ebt_u_replace *replace)
+/* 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)
 {
-	return iterate_entries(replace, 1);
+	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 when the chain is referenced,
- * 0 when it isn't.
- */
-int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr)
+/* 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;
-	ret = iterate_entries(replace, 1);
+	if (print_err)
+		ret = iterate_entries(replace, 1);
+	else
+		ret = iterate_entries(replace, 2);
 	replace->selected_chain = tmp;
 	return ret;
 }
@@ -1158,12 +1089,10 @@
 	struct ebt_u_entries *entries;
 };
 
-/*
- * Checks for loops
+/* 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.
- */
+ * 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;
@@ -1172,9 +1101,7 @@
 	struct ebt_u_entry *e;
 
 	i = -1;
-	/*
-	 * initialize hook_mask to 0
-	 */
+	/* Initialize hook_mask to 0 */
 	while (1) {
 		i++;
 		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
@@ -1191,17 +1118,13 @@
 			ebt_print_memory();
 	}
 
-	/*
-	 * check for loops, starting from every base chain
-	 */
+	/* Check for loops, starting from every base chain */
 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
 		if (!(replace->valid_hooks & (1 << i)))
 			continue;
 		entries = ebt_nr_to_chain(replace, i);
-		/*
-		 * (1 << NF_BR_NUMHOOKS) implies it's a standard chain
-		 * (usefull in the final_check() funtions)
-		 */
+		/* (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);
 		chain_nr = i;
 
@@ -1212,20 +1135,17 @@
 			verdict = ((struct ebt_standard_target *)(e->t))->verdict;
 			if (verdict < 0)
 				goto letscontinue;
-			entries2 = ebt_nr_to_chain(replace,
-						   verdict + NF_BR_NUMHOOKS);
+			entries2 = ebt_nr_to_chain(replace, verdict + NF_BR_NUMHOOKS);
 			entries2->hook_mask |= entries->hook_mask;
-			/*
-			 * now see if we've been here before
-			 */
+			/* 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",
+				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) {
+					ebt_print_error("Loop from chain '%s' to chain '%s'",
 					   ebt_nr_to_chain(replace, chain_nr)->name,
 					   ebt_nr_to_chain(replace, stack[k].chain_nr)->name);
-			/*
-			 * jump to the chain, make sure we know how to get back
-			 */
+					goto free_stack;
+				}
+			/* 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;
@@ -1239,14 +1159,10 @@
 letscontinue:
 			e = e->next;
 		}
-		/*
-		 * we are at the end of a standard chain
-		 */
+		/* We are at the end of a standard chain */
 		if (sp == 0)
 			continue;
-		/*
-		 * go back to the chain one level higher
-		 */
+		/* Go back to the chain one level higher */
 		sp--;
 		j = stack[sp].n;
 		chain_nr = stack[sp].chain_nr;
@@ -1254,13 +1170,16 @@
 		entries = stack[sp].entries;
 		goto letscontinue;
 	}
+free_stack:
 	free(stack);
 	return;
 }
 
-/*
- * the user will use the match, so put it in new_entry
- */
+/* 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;
@@ -1300,13 +1219,12 @@
          */
 
 
-/*
- * type = 0 => update chain jumps
- * type = 1 => check for reference
+/* 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
- */
+ * 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 = -1, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS;
@@ -1338,19 +1256,21 @@
 				    verdict;
 			switch (type) {
 			case 1:
+			case 2:
 			if (chain_jmp == chain_nr) {
-				ebt_print_error("Can't delete the chain, it's "
-				   "referenced in chain %s, rule %d",
-				   entries->name, j);
+				if (type == 2)
+					return 1;
+				ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d",
+				                ebt_nr_to_chain(replace, chain_nr + NF_BR_NUMHOOKS)->name, entries->name, j);
 				return 1;
 			}
 			break;
 			case 0:
-			/* adjust the chain jumps when necessary */
+			/* Adjust the chain jumps when necessary */
 			if (chain_jmp > chain_nr)
 				((struct ebt_standard_target *)e->t)->verdict--;
 			break;
-			} /* end switch */
+			} /* End switch */
 			e = e->next;
 		}
 	}
@@ -1362,9 +1282,7 @@
 	iterate_entries(replace, 0);
 }
 
-/*
- * selected_chain >= NF_BR_NUMHOOKS
- */
+/* Selected_chain >= NF_BR_NUMHOOKS */
 static void remove_udc(struct ebt_u_replace *replace)
 {
 	struct ebt_u_chain_list *cl, **cl2;
@@ -1375,7 +1293,7 @@
 	if (chain_nr < NF_BR_NUMHOOKS)
 		ebt_print_bug("remove_udc: chain_nr = %d < %d", chain_nr,
 			      NF_BR_NUMHOOKS);
-	/* first free the rules */
+	/* First free the rules */
 	entries = ebt_nr_to_chain(replace, chain_nr);
 	u_e = entries->entries;
 	while (u_e) {
@@ -1395,10 +1313,7 @@
 	free(cl);
 }
 
-
-/*
- * used in initialization code of modules
- */
+/* 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);
@@ -1480,9 +1395,7 @@
 		f(i);
 }
 
-/*
- * Don't use this function, use ebt_print_bug()
- */
+/* Don't use this function, use ebt_print_bug() */
 void __ebt_print_bug(char *file, int line, char *format, ...)
 {
 	va_list l;
@@ -1495,26 +1408,20 @@
 	exit (-1);
 }
 
-/*
- * The error messages are put in here when ebt_silent == 1
- * ebt_errormsg[0] == '\0' implies there was no error
- */
+/* 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.
- */
+/* 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()
- */
+/* 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) {
-		snprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
+	if (ebt_silent && ebt_errormsg[0] == '\0') {
+		vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
 		va_end(l);
 	} else {
 		vprintf(format, l);