more cleanup
diff --git a/communication.c b/communication.c
index e429d15..6db6858 100644
--- a/communication.c
+++ b/communication.c
@@ -369,12 +369,20 @@
 			/* Don't use this old counter */
 			old++;
 		} else {
-			if (cc->type == CNT_INCR) {
-				new->pcnt = old->pcnt + next->cnt_surplus.pcnt;
-				new->bcnt = old->bcnt + next->cnt_surplus.bcnt;
-			} else if (cc->type == CNT_DECR) {
-				new->pcnt = old->pcnt - next->cnt_surplus.pcnt;
-				new->bcnt = old->bcnt - next->cnt_surplus.bcnt;
+			if (cc->type == CNT_CHANGE) {
+				new->pcnt = old->pcnt;
+				if (cc->change % 3 == 1)
+					new->pcnt = old->pcnt + next->cnt_surplus.pcnt;
+				else if (cc->change % 3 == 2)
+					new->pcnt = old->pcnt - next->cnt_surplus.pcnt;
+				else
+					new->pcnt = next->cnt.pcnt;
+				if (cc->change / 3 == 1)
+					new->bcnt = old->bcnt + next->cnt_surplus.bcnt;
+				else if (cc->change / 3 == 2)
+					new->bcnt = old->bcnt - next->cnt_surplus.bcnt;
+				else
+					new->bcnt = next->cnt.bcnt;
 			} else
 				*new = next->cnt;
 			next->cnt = *new;
@@ -415,6 +423,7 @@
 	cc = u_repl->counterchanges;
 	for (i = 0; i < u_repl->nentries; i++) {
 		cc->type = CNT_NORM;
+		cc->change = 0;
 		cc3 = &cc->next;
 		cc = cc->next;
 	}
@@ -513,6 +522,7 @@
 		if (*totalcnt >= u_repl->nentries)
 			ebt_print_bug("*totalcnt >= u_repl->nentries");
 		new->cnt = u_repl->counters[*totalcnt];
+		new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0;
 		new->m_list = NULL;
 		new->w_list = NULL;
 		new->next = NULL;
@@ -792,6 +802,7 @@
 		if (!new_cc)
 			ebt_print_memory();
 		new_cc->type = CNT_NORM;
+		new_cc->change = 0;
 		new_cc->next = NULL;
 		*prev_cc = new_cc;
 		prev_cc = &(new_cc->next);
diff --git a/ebtables.c b/ebtables.c
index 59c1584..0cffb90 100644
--- a/ebtables.c
+++ b/ebtables.c
@@ -318,14 +318,8 @@
 
 			while (cc->type == CNT_DEL)
 				cc = cc->next;
-			/* This can only happen in daemon mode */
-			if (cc->type == CNT_INCR) {
-				pcnt += hlp->cnt_surplus.pcnt;
-				bcnt += hlp->cnt_surplus.bcnt;
-			} else if (cc->type == CNT_DECR) {
-				pcnt -= hlp->cnt_surplus.pcnt;
-				bcnt -= hlp->cnt_surplus.bcnt;
-			}
+			if (cc->change != 0) /* in daemon mode, only change==0 is allowed */
+				ebt_print_bug("cc->change != 0");
 			cc = cc->next;
 			if (replace->flags & LIST_X)
 				printf("-c %llu %llu", pcnt, bcnt);
@@ -490,13 +484,16 @@
 	return 0;
 }
 
-static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end)
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it. */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style)
 {
 	char *buffer;
+	int ret = 0;
 
 	if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
 	    (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0'  && argv[optind + 1][1] > '9')))
-		ebt_print_error2("The command -C needs at least 3 arguments");
+		ebt_print_error2("The command -C needs at least 2 arguments");
 	if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
 		if (optind + 3 != argc)
 			ebt_print_error2("No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
@@ -506,36 +503,40 @@
 	}
 
 	if (argv[optind][0] == '+') {
-		if (argv[optind + 1][0] != '+')
-			ebt_print_error2("If one counter is increased, the other should be increased too (perhaps put a '+' before '%s')", argv[optind + 1]);
-		replace->flags |= OPT_CNT_INCR;
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+			ebt_print_error2("Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 1;
 		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
-		if (*buffer != '\0')
-			goto invalid;
-		optind++;
-		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
 	} else if (argv[optind][0] == '-') {
-		if (argv[optind + 1][0] != '-')
-			ebt_print_error2("If one counter is decreased, the other should be decreased too (perhaps put a '-' before '%s')", argv[optind + 1]);
-		replace->flags |= OPT_CNT_DECR;
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+			ebt_print_error2("Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 2;
 		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
-		if (*buffer != '\0')
-			goto invalid;
-		optind++;
-		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
-	} else {
-		if (argv[optind + 1][0] == '-' || argv[optind + 1][0] == '+')
-			ebt_print_error2("If one counter is %screased, the other should be %screased too (perhaps put a '%c' before '%s')",
-			   argv[optind + 1][0] == '-' ? "de" : "in", argv[optind + 1][0] == '-' ? "de" : "in", argv[optind + 1][0], argv[optind]);
+	} else
 		new_entry->cnt_surplus.pcnt = strtoull(argv[optind], &buffer, 10);
-		optind++;
-		new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10);
-	}
 
 	if (*buffer != '\0')
 		goto invalid;
 	optind++;
-	return 0;
+	if (argv[optind][0] == '+') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_incr;
+		ret += 3;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else if (argv[optind][0] == '-') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_decr;
+		ret += 6;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10);
+
+	if (*buffer != '\0')
+		goto invalid;
+	optind++;
+	return ret;
 invalid:
 	ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
 }
@@ -567,6 +568,7 @@
 	char *buffer;
 	int c, i;
 	int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
+	int chcounter; /* Needed for -C */
 	int policy = 0;
 	int rule_nr = 0;
 	int rule_nr_end = 0;
@@ -686,7 +688,7 @@
 					ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]);
 				optind++;
 			} else if (c == 'C') {
-				if (parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end))
+				if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style)) == -1)
 					return -1;
 			} else if (c == 'I') {
 				if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
@@ -1224,7 +1226,7 @@
 		if (ebt_errormsg[0] != '\0')
 			return -1;
 	} else if (replace->command == 'C') {
-		ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus));
+		ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
 		if (ebt_errormsg[0] != '\0')
 			return -1;
 	}
diff --git a/libebtc.c b/libebtc.c
index 964fa8e..6760dbc 100644
--- a/libebtc.c
+++ b/libebtc.c
@@ -23,12 +23,10 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <getopt.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
-#include <netinet/ether.h>
 #include "include/ebtables_u.h"
 #include "include/ethernetdb.h"
 #include <unistd.h>
@@ -140,7 +138,7 @@
 int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
 {
 	if (!ebt_find_table(replace->name)) {
-		ebt_print_error("Bad table name");
+		ebt_print_error("Bad table name '%s'", replace->name);
 		return -1;
 	}
 	/* Get the kernel's information */
@@ -149,7 +147,7 @@
 			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);
+			ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name);
 			return -1;
 		}
 	}
@@ -212,10 +210,7 @@
 			else
 				break;
 		}
-		entries->nentries = 0;
-		entries->counter_offset = 0;
 		u_e1 = entries->entries;
-		entries->entries = NULL;
 		while (u_e1) {
 			ebt_free_u_entry(u_e1);
 			u_e2 = u_e1->next;
@@ -223,6 +218,11 @@
 			u_e1 = u_e2;
 		}
 	}
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (replace->hook_entry[i]) {
+			free(replace->hook_entry[i]);
+			replace->hook_entry[i] = NULL;
+		}
 	udc1 = replace->udc;
 	while (udc1) {
 		free(udc1->udc);
@@ -314,8 +314,6 @@
 	free(e->t);
 }
 
-/* Blatently stolen (again) from iptables.c userspace program
- * find out where the modprobe utility is located */
 static char *get_modprobe(void)
 {
 	int procfile;
@@ -327,12 +325,9 @@
 
 	ret = malloc(1024);
 	if (ret) {
-		switch (read(procfile, ret, 1024)) {
-		case -1: goto fail;
-		case 1024: goto fail; /* Partial read.  Wierd */
-		}
-		if (ret[strlen(ret)-1] == '\n')
-			ret[strlen(ret)-1] = 0;
+		if (read(procfile, ret, 1024) == -1)
+			goto fail;
+		ret[1023] = '\0';
 		close(procfile);
 		return ret;
 	}
@@ -343,7 +338,7 @@
 }
 
 char *ebt_modprobe;
-/* Try to load the kernel module */
+/* Try to load the kernel module, analogous to ip_tables.c */
 int ebtables_insmod(const char *modname)
 {
 	char *buf = NULL;
@@ -380,8 +375,7 @@
 /* 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. */
-struct ebt_u_entries *ebt_nr_to_chain(const struct ebt_u_replace *replace,
-				       int nr)
+struct ebt_u_entries *ebt_nr_to_chain(const struct ebt_u_replace *replace, int nr)
 {
 	if (nr == -1)
 		return NULL;
@@ -418,7 +412,7 @@
 	struct ebt_u_chain_list *cl = replace->udc;
 
 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-		if (!(replace->valid_hooks & (1 << i)))
+		if (!replace->hook_entry[i])
 			continue;
 		if (!strcmp(arg, replace->hook_entry[i]->name))
 			return replace->hook_entry[i];
@@ -439,7 +433,7 @@
 	struct ebt_u_chain_list *cl = replace->udc;
 
 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-		if (!(replace->valid_hooks & (1 << i)))
+		if (!replace->hook_entry[i])
 			continue;
 		if (!strcmp(arg, replace->hook_entry[i]->name))
 			return i;
@@ -473,7 +467,7 @@
 }
 
 /* Flush one chain or the complete table
- * If selected_chain == -1: flush the complete table */
+ * If selected_chain == -1 then flush the complete table */
 void ebt_flush_chains(struct ebt_u_replace *replace)
 {
 	int i, j, numdel;
@@ -690,8 +684,8 @@
  * This function expects the ebt_{match,watcher,target} members of new_entry
  * to contain pointers to ebt_u_{match,watcher,target} and updates these
  * pointers so that they point to ebt_{match,watcher,target}, before adding
- * the rule to the chain. Don't free() the ebt_{match,watcher,target} after a
- * successful call to ebt_add_rule() */
+ * the rule to the chain. Don't free() the ebt_{match,watcher,target} and
+ * don't reuse the new_entry after a successful call to ebt_add_rule() */
 void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry,
 		  int rule_nr)
 {
@@ -717,7 +711,7 @@
 
 	/* Handle counter stuff */
 	for (i = 0; i < replace->selected_chain; i++) {
-		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
+		if (i < NF_BR_NUMHOOKS && !(replace->hook_entry[i]))
 			continue;
 		j = ebt_nr_to_chain(replace, i)->nentries;
 		while (j) {
@@ -737,11 +731,11 @@
 	if (cc && cc->type == CNT_DEL)
 		cc->type = CNT_OWRITE;
 	else {
-		new_cc = (struct ebt_cntchanges *)
-			 malloc(sizeof(struct ebt_cntchanges));
+		new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
 		if (!new_cc)
 			ebt_print_memory();
 		new_cc->type = CNT_ADD;
+		new_cc->change = 0;
 		new_cc->next = cc;
 		*prev_cc = new_cc;
 	}
@@ -780,6 +774,8 @@
 	}
 }
 
+/* If *begin==*end==0 then find the rule corresponding to new_entry,
+ * else make the rule numbers positive and check for bad rule numbers. */
 static int check_and_change_rule_number(struct ebt_u_replace *replace,
    struct ebt_u_entry *new_entry, int *begin, int *end)
 {
@@ -837,7 +833,7 @@
 
 	/* Handle counter stuff */
 	for (i = 0; i < replace->selected_chain; i++) {
-		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
+		if (i < NF_BR_NUMHOOKS && !(replace->hook_entry[i]))
 			continue;
 		j = ebt_nr_to_chain(replace, i)->nentries;
 		while (j) {
@@ -899,17 +895,19 @@
 	}
 }
 
-#define OPT_CNT_INCR	0x2000 /* This value is also defined in ebtables.c */
-#define OPT_CNT_DECR	0x4000 /* This value is also defined in ebtables.c */
 /* Change the counters of a rule or rules
  * begin == end == 0: change counters of the rule corresponding to new_entry
  *
  * The first rule has rule nr 1, the last rule has rule nr -1, etc.
  * This function expects the ebt_{match,watcher,target} members of new_entry
- * to contain pointers to ebt_u_{match,watcher,target}. */
+ * to contain pointers to ebt_u_{match,watcher,target}.
+ * The mask denotes the following:
+ *    pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement
+ *    bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment
+ * In daemon mode, mask==0 must hold */
 void ebt_change_counters(struct ebt_u_replace *replace,
 		     struct ebt_u_entry *new_entry, int begin, int end,
-		     struct ebt_counter *cnt)
+		     struct ebt_counter *cnt, int mask)
 {
 	int i, j;
 	struct ebt_u_entry *u_e;
@@ -920,7 +918,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->hook_entry[i]))
 			continue;
 		j = ebt_nr_to_chain(replace, i)->nentries;
 		while (j) {
@@ -942,40 +940,34 @@
 	while (i) {
 		if (cc->type != CNT_DEL) {
 			i--;
-			if (cc->type != CNT_ADD && cc->type != CNT_OWRITE) {
-				/* In daemon mode, we want to make sure we don't lose
-				 * any counted packets/bytes (the kernel is still counting
-				 * while we are changing things in userspace). Therefore,
-				 * we need to add the amount to the counters in the kernel,
-				 * not the counters copied earlier from the kernel. */
-				u_e->cnt_surplus = *cnt;
-				if (replace->flags & OPT_CNT_INCR) {
-					cc->type = CNT_INCR;
-				} else if (replace->flags & OPT_CNT_DECR)
-					cc->type = CNT_DECR;
-				else {
-					cc->type = CNT_CHANGE;
-					u_e->cnt = *cnt;
-					u_e->cnt_surplus.pcnt = u_e->cnt_surplus.bcnt = 0;
-				}
-			} else { /* A -C after a -A remains a -A */
-				if (replace->flags & OPT_CNT_INCR) {
-					u_e->cnt.pcnt += cnt->pcnt;
-					u_e->cnt.bcnt += cnt->bcnt;
-				} else if (replace->flags & OPT_CNT_DECR) {
-					u_e->cnt.pcnt -= cnt->pcnt;
-					u_e->cnt.bcnt -= cnt->bcnt;
-				} else
-					u_e->cnt = *cnt;
+			if (mask % 3 == 0) {
+				u_e->cnt.pcnt = (*cnt).pcnt;
+				u_e->cnt_surplus.pcnt = 0;
+			} else {
+				if (cc->type != CNT_NORM)
+					ebt_print_bug("cc->type != CNT_NORM");
+				u_e->cnt_surplus.pcnt = (*cnt).pcnt;
 			}
+
+			if (mask / 3 == 0) {
+				u_e->cnt.bcnt = (*cnt).bcnt;
+				u_e->cnt_surplus.bcnt = 0;
+			} else {
+				if (cc->type != CNT_NORM)
+					ebt_print_bug("cc->type != CNT_NORM");
+				u_e->cnt_surplus.bcnt = (*cnt).bcnt;
+			}
+			if (cc->type == CNT_NORM || cc->type == CNT_ZERO)
+				cc->type = CNT_CHANGE;
+			cc->change = mask;
 			u_e = u_e->next;
 		}
 		cc = cc->next;
 	}
 }
 
-/* Selected_chain == -1 : zero all counters
- * Otherwise, zero the counters of selected_chain */
+/* If selected_chain == -1 then zero all counters,
+ * otherwise, zero the counters of selected_chain */
 void ebt_zero_counters(struct ebt_u_replace *replace)
 {
 	struct ebt_u_entries *entries = ebt_to_chain(replace);
@@ -992,7 +984,7 @@
 		i = -1;
 		while (1) {
 			i++;
-			if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
+			if (i < NF_BR_NUMHOOKS && !(replace->hook_entry[i]))
 				continue;
 			entries = ebt_nr_to_chain(replace, i);
 			if (!entries) {
@@ -1006,14 +998,13 @@
 				next = next->next;
 			}
 		}
-			
 	} else {
 		next = entries->entries;
 		if (entries->nentries == 0)
 			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->hook_entry[i]))
 				continue;
 			j = ebt_nr_to_chain(replace, i)->nentries;
 			while (j) {
@@ -1059,8 +1050,7 @@
 	if (!cl)
 		ebt_print_memory();
 	cl->next = NULL;
-	cl->udc = (struct ebt_u_entries *)
-	   malloc(sizeof(struct ebt_u_entries));
+	cl->udc = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
 	if (!cl->udc)
 		ebt_print_memory();
 	cl->udc->nentries = 0;
@@ -1217,7 +1207,7 @@
 	/* Initialize hook_mask to 0 */
 	while (1) {
 		i++;
-		if (i < NF_BR_NUMHOOKS && !(replace->valid_hooks & (1 << i)))
+		if (i < NF_BR_NUMHOOKS && !(replace->hook_entry[i]))
 			continue;
 		entries = ebt_nr_to_chain(replace, i);
 		if (!entries)
@@ -1225,15 +1215,14 @@
 		entries->hook_mask = 0;
 	}
 	if (i > NF_BR_NUMHOOKS) {
-		stack = (struct ebt_u_stack *)malloc((i - NF_BR_NUMHOOKS) *
-		   sizeof(struct ebt_u_stack));
+		stack = (struct ebt_u_stack *)malloc((i - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack));
 		if (!stack)
 			ebt_print_memory();
 	}
 
 	/* Check for loops, starting from every base chain */
 	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-		if (!(replace->valid_hooks & (1 << i)))
+		if (!(replace->hook_entry[i]))
 			continue;
 		entries = ebt_nr_to_chain(replace, i);
 		/* (1 << NF_BR_NUMHOOKS) implies it's a standard chain