netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFT] Improve iptables error reporting
@ 2007-08-11 23:21 Martin Josefsson
  2007-09-11  8:42 ` Martin Josefsson
  0 siblings, 1 reply; 3+ messages in thread
From: Martin Josefsson @ 2007-08-11 23:21 UTC (permalink / raw)
  To: netfilter-devel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 2284 bytes --]

Hi

Here's a small patch that reworks the iptables/ip6tables error reporting a 
bit. The purpose of this patch is to try to provide more resonable error 
messages.

Currently a lot of functions in libiptc sets a pointer called iptc_fn to 
point to the current function, and then they set errno to an error 
code.

This has at least one major shortcoming:

int some_other_function(void) {
 	iptc_fn = some_other_function;
 	...

 	return 1;
}

void some_function(void) {
 	iptc_fn = some_function;
 	if (some_other_function())
 		errno = ENOENT;
}

Now we have iptc_fn == some_other_function but errno was meant for 
some_function, a mismatch has occured, which will cause the wrong error 
message to be printed.

What this patch does is to add TC_ERROR (iptc_error/ip6tc_error) which 
sets both iptc_errfn and iptc_errno at the same time, and it's only called 
when an error is detected. Thus we don't have any mismatches anymore.

Example of error messages:
loop:
 	old:
 		# iptables -I INPUT -j tuut
 		iptables: Too many levels of symbolic links
 	new:
 		# iptables -I INPUT -j tuut
 		iptables: Loop found in table

chain not found:
 	old:
 		# iptables -I foo -j ACCEPT
 		iptables: No chain/target/match by that name
 	new:
 		# iptables -I foo -j ACCEPT
 		iptables: No chain by that name

jump to predefined chain:
 	old:
 		# iptables -I tuut -j INPUT
 		iptables: Invalid argument
 	new:
 		# iptables -I tuut -j INPUT
 		iptables: Can't jump to a built-in target

removal of not empty chain:
 	old:
 		# iptables -X tuut
 		iptables: Directory not empty
 	new:
 		# iptables -X tuut
 		iptables: Chain is not empty

removal of chain with references:
 	old:
 		# iptables -X tuut
 		iptables: Too many links
 	new:
 		# iptables -X tuut
 		iptables: Can't delete chain with references left


Unknown errors will now be displayed like:
(this is made up)

"Unknown error, iptc_commit: 30 (foo not in phase with the moon)"

function, error code and strerror() (might give a clue).
(line number can be added if needed)

And if something signals an error (return value) but doesn't call 
iptc_error()/ip6tc_error() it looks like this:

"Something forgot to set the error reason"


Please test and report success or failure. I'm likely to have missed 
some/many errors.

/Martin

[-- Attachment #2: Type: TEXT/PLAIN, Size: 33701 bytes --]

diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/include/libiptc/libip6tc.h iptables-1.3.8-20070810/include/libiptc/libip6tc.h
--- iptables-1.3.8-20070810.orig/include/libiptc/libip6tc.h	2003-05-05 21:33:40.000000000 +0200
+++ iptables-1.3.8-20070810/include/libiptc/libip6tc.h	2007-08-12 00:06:23.000000000 +0200
@@ -17,6 +17,10 @@ typedef char ip6t_chainlabel[32];
 #define IP6TC_LABEL_QUEUE   "QUEUE"
 #define IP6TC_LABEL_RETURN "RETURN"
 
+#ifndef TC_LIBNAME
+#define TC_LIBNAME "libip6tc"
+#endif
+
 /* Transparent handle type. */
 typedef struct ip6tc_handle *ip6tc_handle_t;
 
@@ -145,8 +149,11 @@ int ip6tc_commit(ip6tc_handle_t *handle)
 /* Get raw socket. */
 int ip6tc_get_raw_socket();
 
+/* Report errors */
+void ip6tc_error(const char *, int);
+
 /* Translates errno numbers into more human-readable form than strerror. */
-const char *ip6tc_strerror(int err);
+const char *ip6tc_strerror();
 
 /* Return prefix length, or -1 if not contiguous */
 int ipv6_prefix_length(const struct in6_addr *a);
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/include/libiptc/libiptc.h iptables-1.3.8-20070810/include/libiptc/libiptc.h
--- iptables-1.3.8-20070810.orig/include/libiptc/libiptc.h	2003-05-02 17:30:11.000000000 +0200
+++ iptables-1.3.8-20070810/include/libiptc/libiptc.h	2007-08-12 00:06:31.000000000 +0200
@@ -25,6 +25,10 @@ typedef char ipt_chainlabel[32];
 #define IPTC_LABEL_QUEUE   "QUEUE"
 #define IPTC_LABEL_RETURN  "RETURN"
 
+#ifndef TC_LIBNAME
+#define TC_LIBNAME "libip4tc"
+#endif
+
 /* Transparent handle type. */
 typedef struct iptc_handle *iptc_handle_t;
 
@@ -155,8 +159,11 @@ int iptc_commit(iptc_handle_t *handle);
 /* Get raw socket. */
 int iptc_get_raw_socket();
 
+/* Report errors */
+void iptc_error(const char *, int);
+
 /* Translates errno numbers into more human-readable form than strerror. */
-const char *iptc_strerror(int err);
+const char *iptc_strerror();
 
 #ifdef __cplusplus
 }
Binary files iptables-1.3.8-20070810.orig/ip6tables and iptables-1.3.8-20070810/ip6tables differ
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/ip6tables-restore.c iptables-1.3.8-20070810/ip6tables-restore.c
--- iptables-1.3.8-20070810.orig/ip6tables-restore.c	2007-07-24 07:52:07.000000000 +0200
+++ iptables-1.3.8-20070810/ip6tables-restore.c	2007-08-11 23:39:27.000000000 +0200
@@ -240,14 +240,14 @@ int main(int argc, char *argv[])
 						exit_error(PARAMETER_PROBLEM,
 							   "error flushing chain "
 							   "'%s':%s\n", chain,
-							   strerror(errno));
+							   ip6tc_strerror());
 				} else {
 					DEBUGP("Creating new chain '%s'\n", chain);
 					if (!ip6tc_create_chain(chain, &handle))
 						exit_error(PARAMETER_PROBLEM,
 							   "error creating chain "
 							   "'%s':%s\n", chain,
-							   strerror(errno));
+							   ip6tc_strerror());
 				}
 			}
 
@@ -286,7 +286,7 @@ int main(int argc, char *argv[])
 						"Can't set policy `%s'"
 						" on `%s' line %u: %s\n",
 						chain, policy, line,
-						ip6tc_strerror(errno));
+						ip6tc_strerror());
 			}
 
 			ret = 1;
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/ip6tables-save.c iptables-1.3.8-20070810/ip6tables-save.c
--- iptables-1.3.8-20070810.orig/ip6tables-save.c	2007-05-10 17:00:39.000000000 +0200
+++ iptables-1.3.8-20070810/ip6tables-save.c	2007-08-11 19:12:51.000000000 +0200
@@ -257,7 +257,7 @@ static int do_output(const char *tablena
 	h = ip6tc_init(tablename);
 	if (!h)
  		exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
-			   ip6tc_strerror(errno));
+			   ip6tc_strerror());
 
 	if (!binary) {
 		time_t now = time(NULL);
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/ip6tables-standalone.c iptables-1.3.8-20070810/ip6tables-standalone.c
--- iptables-1.3.8-20070810.orig/ip6tables-standalone.c	2004-12-27 20:49:28.000000000 +0100
+++ iptables-1.3.8-20070810/ip6tables-standalone.c	2007-08-11 19:12:51.000000000 +0200
@@ -60,7 +60,7 @@ main(int argc, char *argv[])
 
 	if (!ret)
 		fprintf(stderr, "ip6tables: %s\n",
-			ip6tc_strerror(errno));
+			ip6tc_strerror());
 
 	exit(!ret);
 }
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/ip6tables.c iptables-1.3.8-20070810/ip6tables.c
--- iptables-1.3.8-20070810.orig/ip6tables.c	2007-08-01 17:19:15.000000000 +0200
+++ iptables-1.3.8-20070810/ip6tables.c	2007-08-12 00:08:32.000000000 +0200
@@ -1330,7 +1330,9 @@ list_entries(const ip6t_chainlabel chain
 		found = 1;
 	}
 
-	errno = ENOENT;
+	if (!found)
+		ip6tc_error(__FUNCTION__, ENOENT);
+
 	return found;
 }
 
@@ -1896,7 +1898,7 @@ int do_command6(int argc, char *argv[], 
 	if (!*handle)
 		exit_error(VERSION_PROBLEM,
 			"can't initialize ip6tables table `%s': %s",
-			*table, ip6tc_strerror(errno));
+			*table, ip6tc_strerror());
 
 	if (command == CMD_APPEND
 	    || command == CMD_DELETE
Binary files iptables-1.3.8-20070810.orig/iptables and iptables-1.3.8-20070810/iptables differ
Binary files iptables-1.3.8-20070810.orig/iptables-restore and iptables-1.3.8-20070810/iptables-restore differ
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/iptables-restore.c iptables-1.3.8-20070810/iptables-restore.c
--- iptables-1.3.8-20070810.orig/iptables-restore.c	2007-07-24 07:52:07.000000000 +0200
+++ iptables-1.3.8-20070810/iptables-restore.c	2007-08-11 23:38:31.000000000 +0200
@@ -243,14 +243,14 @@ main(int argc, char *argv[])
 						exit_error(PARAMETER_PROBLEM,
 							   "error flushing chain "
 							   "'%s':%s\n", chain,
-							   strerror(errno));
+							   iptc_strerror());
 				} else {
 					DEBUGP("Creating new chain '%s'\n", chain);
 					if (!iptc_create_chain(chain, &handle))
 						exit_error(PARAMETER_PROBLEM,
 							   "error creating chain "
 							   "'%s':%s\n", chain,
-							   strerror(errno));
+							   iptc_strerror());
 				}
 			}
 
@@ -289,7 +289,7 @@ main(int argc, char *argv[])
 						"Can't set policy `%s'"
 						" on `%s' line %u: %s\n",
 						chain, policy, line,
-						iptc_strerror(errno));
+						iptc_strerror());
 			}
 
 			ret = 1;
Binary files iptables-1.3.8-20070810.orig/iptables-save and iptables-1.3.8-20070810/iptables-save differ
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/iptables-save.c iptables-1.3.8-20070810/iptables-save.c
--- iptables-1.3.8-20070810.orig/iptables-save.c	2007-05-10 17:00:39.000000000 +0200
+++ iptables-1.3.8-20070810/iptables-save.c	2007-08-11 19:12:51.000000000 +0200
@@ -268,7 +268,7 @@ static int do_output(const char *tablena
 	h = iptc_init(tablename);
 	if (!h)
  		exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
-			   iptc_strerror(errno));
+			   iptc_strerror());
 
 	if (!binary) {
 		time_t now = time(NULL);
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/iptables-standalone.c iptables-1.3.8-20070810/iptables-standalone.c
--- iptables-1.3.8-20070810.orig/iptables-standalone.c	2006-06-19 18:53:03.000000000 +0200
+++ iptables-1.3.8-20070810/iptables-standalone.c	2007-08-11 19:12:51.000000000 +0200
@@ -66,7 +66,7 @@ main(int argc, char *argv[])
 
 	if (!ret) {
 		fprintf(stderr, "iptables: %s\n",
-			iptc_strerror(errno));
+			iptc_strerror());
 		if (errno == EAGAIN) {
 			exit(RESOURCE_PROBLEM);
 		}
Binary files iptables-1.3.8-20070810.orig/iptables-xml and iptables-1.3.8-20070810/iptables-xml differ
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/iptables.c iptables-1.3.8-20070810/iptables.c
--- iptables-1.3.8-20070810.orig/iptables.c	2007-08-01 17:19:15.000000000 +0200
+++ iptables-1.3.8-20070810/iptables.c	2007-08-12 00:08:15.000000000 +0200
@@ -1371,7 +1371,9 @@ list_entries(const ipt_chainlabel chain,
 		found = 1;
 	}
 
-	errno = ENOENT;
+	if (!found)
+		iptc_error(__FUNCTION__, ENOENT);
+
 	return found;
 }
 
@@ -1961,7 +1963,7 @@ int do_command(int argc, char *argv[], c
 	if (!*handle)
 		exit_error(VERSION_PROBLEM,
 			   "can't initialize iptables table `%s': %s",
-			   *table, iptc_strerror(errno));
+			   *table, iptc_strerror());
 
 	if (command == CMD_APPEND
 	    || command == CMD_DELETE
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/libiptc/libip4tc.c iptables-1.3.8-20070810/libiptc/libip4tc.c
--- iptables-1.3.8-20070810.orig/libiptc/libip4tc.c	2006-07-05 11:31:45.000000000 +0200
+++ iptables-1.3.8-20070810/libiptc/libip4tc.c	2007-08-12 00:11:55.000000000 +0200
@@ -93,6 +93,7 @@ typedef unsigned int socklen_t;
 #define TC_INIT			iptc_init
 #define TC_FREE			iptc_free
 #define TC_COMMIT		iptc_commit
+#define TC_ERROR		iptc_error
 #define TC_STRERROR		iptc_strerror
 #define TC_NUM_RULES		iptc_num_rules
 #define TC_GET_RULE		iptc_get_rule
@@ -115,6 +116,8 @@ typedef unsigned int socklen_t;
 #define ALIGN			IPT_ALIGN
 #define RETURN			IPT_RETURN
 
+#define TC_ERR(x)		TC_ERROR(__FUNCTION__, (x))
+
 #include "libiptc.c"
 
 #define IP_PARTS_NATIVE(n)			\
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/libiptc/libip6tc.c iptables-1.3.8-20070810/libiptc/libip6tc.c
--- iptables-1.3.8-20070810.orig/libiptc/libip6tc.c	2006-07-05 11:31:45.000000000 +0200
+++ iptables-1.3.8-20070810/libiptc/libip6tc.c	2007-08-12 00:12:33.000000000 +0200
@@ -88,6 +88,7 @@ typedef unsigned int socklen_t;
 #define TC_INIT			ip6tc_init
 #define TC_FREE			ip6tc_free
 #define TC_COMMIT		ip6tc_commit
+#define TC_ERROR		ip6tc_error
 #define TC_STRERROR		ip6tc_strerror
 #define TC_NUM_RULES		ip6tc_num_rules
 #define TC_GET_RULE		ip6tc_get_rule
@@ -110,6 +111,8 @@ typedef unsigned int socklen_t;
 #define ALIGN			IP6T_ALIGN
 #define RETURN			IP6T_RETURN
 
+#define TC_ERR(x)		TC_ERROR(__FUNCTION__, (x))
+
 #include "libiptc.c"
 
 #define BIT6(a, l) \
diff -X /home/gandalf/dontdiff.ny -x '*.d' -x '*.man' -x '*.8' -urNp iptables-1.3.8-20070810.orig/libiptc/libiptc.c iptables-1.3.8-20070810/libiptc/libiptc.c
--- iptables-1.3.8-20070810.orig/libiptc/libiptc.c	2007-06-30 12:47:57.000000000 +0200
+++ iptables-1.3.8-20070810/libiptc/libiptc.c	2007-08-12 00:13:40.000000000 +0200
@@ -46,7 +46,13 @@
 
 static int sockfd = -1;
 static int sockfd_use = 0;
-static void *iptc_fn = NULL;
+
+/* Silly games to get cpp to perform recursive macro expansion when quoting */
+#define QUOTE(x) #x
+#define QF(x) QUOTE(x)
+
+static int iptc_errno = 0;
+static char iptc_errfn[64];
 
 static const char *hooknames[]
 = { [HOOK_PRE_ROUTING]  "PREROUTING",
@@ -456,7 +462,7 @@ static int cache_add_entry(STRUCT_ENTRY 
 		DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, 
 			(char *)c->name, c);
 		if (!c) {
-			errno = -ENOMEM;
+			TC_ERR(ENOMEM);
 			return -1;
 		}
 
@@ -469,7 +475,7 @@ static int cache_add_entry(STRUCT_ENTRY 
 		DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", 
 			*num, offset, c, &c->rules);
 		if (!c) {
-			errno = -ENOMEM;
+			TC_ERR(ENOMEM);
 			return -1;
 		}
 
@@ -486,9 +492,10 @@ new_rule:
 
 		if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, 
 					   e->next_offset))) {
-			errno = ENOMEM;
+			TC_ERR(ENOMEM);
 			return -1;
 		}
+
 		DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
 
 		r->index = *num;
@@ -504,7 +511,7 @@ new_rule:
 			t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
 			if (t->target.u.target_size
 			    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
-				errno = EINVAL;
+				TC_ERR(EINVAL);
 				return -1;
 			}
 
@@ -770,7 +777,7 @@ alloc_handle(const char *tablename, unsi
 
 	h = malloc(sizeof(STRUCT_TC_HANDLE));
 	if (!h) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return NULL;
 	}
 	memset(h, 0, sizeof(*h));
@@ -778,8 +785,10 @@ alloc_handle(const char *tablename, unsi
 	strcpy(h->info.name, tablename);
 
 	h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
-	if (!h->entries)
+	if (!h->entries) {
+		TC_ERR(ENOMEM);
 		goto out_free_handle;
+	}
 
 	strcpy(h->entries->name, tablename);
 	h->entries->size = size;
@@ -801,17 +810,21 @@ TC_INIT(const char *tablename)
 	unsigned int tmp;
 	socklen_t s;
 
-	iptc_fn = TC_INIT;
+	/* ugly hack to avoid warnings if called multiple times */
+	memset(iptc_errfn, 0, 64);
+	iptc_errno = 0;
 
 	if (strlen(tablename) >= TABLE_MAXNAMELEN) {
-		errno = EINVAL;
+		TC_ERR(E2BIG);
 		return NULL;
 	}
 	
 	if (sockfd_use == 0) {
 		sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
-		if (sockfd < 0)
+		if (sockfd < 0) {
+			TC_ERR(errno);
 			return NULL;
+		}
 	}
 	sockfd_use++;
 
@@ -823,6 +836,7 @@ TC_INIT(const char *tablename)
 			close(sockfd);
 			sockfd = -1;
 		}
+		TC_ERR(errno);
 		return NULL;
 	}
 
@@ -835,6 +849,7 @@ TC_INIT(const char *tablename)
 			close(sockfd);
 			sockfd = -1;
 		}
+		TC_ERR(ENOMEM);
 		return NULL;
 	}
 
@@ -846,8 +861,10 @@ TC_INIT(const char *tablename)
 	tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
 
 	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
-		       &tmp) < 0)
+		       &tmp) < 0) {
+		TC_ERR(errno);
 		goto error;
+	}
 
 #ifdef IPTC_DEBUG2
 	{
@@ -879,7 +896,6 @@ TC_FREE(TC_HANDLE_T *h)
 {
 	struct chain_head *c, *tmp;
 
-	iptc_fn = TC_FREE;
 	if (--sockfd_use == 0) {
 		close(sockfd);
 		sockfd = -1;
@@ -913,7 +929,6 @@ static int dump_entry(STRUCT_ENTRY *e, c
 void
 TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
 {
-	iptc_fn = TC_DUMP_ENTRIES;
 	CHECK(handle);
 #if 0
 	printf("libiptc v%s. %u bytes.\n",
@@ -940,7 +955,6 @@ TC_DUMP_ENTRIES(const TC_HANDLE_T handle
 /* Does this chain exist? */
 int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
 {
-	iptc_fn = TC_IS_CHAIN;
 	return iptcc_find_label(chain, handle) != NULL;
 }
 
@@ -962,11 +976,9 @@ TC_FIRST_CHAIN(TC_HANDLE_T *handle)
 	struct chain_head *c = list_entry((*handle)->chains.next,
 					  struct chain_head, list);
 
-	iptc_fn = TC_FIRST_CHAIN;
-
-
 	if (list_empty(&(*handle)->chains)) {
 		DEBUGP(": no chains\n");
+		TC_ERR(ENOENT);
 		return NULL;
 	}
 
@@ -983,8 +995,6 @@ TC_NEXT_CHAIN(TC_HANDLE_T *handle)
 {
 	struct chain_head *c = (*handle)->chain_iterator_cur;
 
-	iptc_fn = TC_NEXT_CHAIN;
-
 	if (!c) {
 		DEBUGP(": no more chains\n");
 		return NULL;
@@ -1003,13 +1013,11 @@ TC_FIRST_RULE(const char *chain, TC_HAND
 	struct chain_head *c;
 	struct rule_head *r;
 
-	iptc_fn = TC_FIRST_RULE;
-
 	DEBUGP("first rule(%s): ", chain);
 
 	c = iptcc_find_label(chain, *handle);
 	if (!c) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return NULL;
 	}
 
@@ -1032,7 +1040,6 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, T
 {
 	struct rule_head *r;
 
-	iptc_fn = TC_NEXT_RULE;
 	DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
 
 	if (!(*handle)->rule_iterator_cur) {
@@ -1043,8 +1050,6 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, T
 	r = list_entry((*handle)->rule_iterator_cur->list.next, 
 			struct rule_head, list);
 
-	iptc_fn = TC_NEXT_RULE;
-
 	DEBUGP_C("next=%p, head=%p...", &r->list, 
 		&(*handle)->rule_iterator_cur->chain->rules);
 
@@ -1066,12 +1071,11 @@ unsigned int
 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
 {
 	struct chain_head *c;
-	iptc_fn = TC_NUM_RULES;
 	CHECK(*handle);
 
 	c = iptcc_find_label(chain, *handle);
 	if (!c) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return (unsigned int)-1;
 	}
 	
@@ -1085,13 +1089,11 @@ const STRUCT_ENTRY *TC_GET_RULE(const ch
 	struct chain_head *c;
 	struct rule_head *r;
 	
-	iptc_fn = TC_GET_RULE;
-
 	CHECK(*handle);
 
 	c = iptcc_find_label(chain, *handle);
 	if (!c) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return NULL;
 	}
 
@@ -1134,8 +1136,6 @@ const char *TC_GET_TARGET(const STRUCT_E
 	STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
 	struct rule_head *r = container_of(e, struct rule_head, entry[0]);
 
-	iptc_fn = TC_GET_TARGET;
-
 	switch(r->type) {
 		int spos;
 		case IPTCC_R_FALLTHROUGH:
@@ -1162,13 +1162,9 @@ TC_BUILTIN(const char *chain, const TC_H
 {
 	struct chain_head *c;
 	
-	iptc_fn = TC_BUILTIN;
-
 	c = iptcc_find_label(chain, handle);
-	if (!c) {
-		errno = ENOENT;
+	if (!c)
 		return 0;
-	}
 
 	return iptcc_is_builtin(c);
 }
@@ -1181,13 +1177,11 @@ TC_GET_POLICY(const char *chain,
 {
 	struct chain_head *c;
 
-	iptc_fn = TC_GET_POLICY;
-
 	DEBUGP("called for chain %s\n", chain);
 
 	c = iptcc_find_label(chain, *handle);
 	if (!c) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return NULL;
 	}
 
@@ -1209,7 +1203,7 @@ iptcc_standard_map(struct rule_head *r, 
 
 	if (t->target.u.target_size
 	    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
-		errno = EINVAL;
+		TC_ERR(EINVAL);
 		return 0;
 	}
 	/* memset for memcmp convenience on delete/replace */
@@ -1245,7 +1239,7 @@ iptcc_map_target(const TC_HANDLE_T handl
 		return iptcc_standard_map(r, RETURN);
 	else if (TC_BUILTIN(t->u.user.name, handle)) {
 		/* Can't jump to builtins. */
-		errno = EINVAL;
+		TC_ERR(EINVAL);
 		return 0;
 	} else {
 		/* Maybe it's an existing chain name. */
@@ -1284,17 +1278,15 @@ TC_INSERT_ENTRY(const IPT_CHAINLABEL cha
 	struct rule_head *r;
 	struct list_head *prev;
 
-	iptc_fn = TC_INSERT_ENTRY;
-
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	/* first rulenum index = 0
 	   first c->num_rules index = 1 */
 	if (rulenum > c->num_rules) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
@@ -1312,7 +1304,7 @@ TC_INSERT_ENTRY(const IPT_CHAINLABEL cha
 	}
 
 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return 0;
 	}
 
@@ -1342,15 +1334,13 @@ TC_REPLACE_ENTRY(const IPT_CHAINLABEL ch
 	struct chain_head *c;
 	struct rule_head *r, *old;
 
-	iptc_fn = TC_REPLACE_ENTRY;
-
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (rulenum >= c->num_rules) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
@@ -1362,7 +1352,7 @@ TC_REPLACE_ENTRY(const IPT_CHAINLABEL ch
 	}
 
 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return 0;
 	}
 
@@ -1392,16 +1382,15 @@ TC_APPEND_ENTRY(const IPT_CHAINLABEL cha
 	struct chain_head *c;
 	struct rule_head *r;
 
-	iptc_fn = TC_APPEND_ENTRY;
 	if (!(c = iptcc_find_label(chain, *handle))) {
 		DEBUGP("unable to find chain `%s'\n", chain);
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
 		DEBUGP("unable to allocate rule for chain `%s'\n", chain);
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return 0;
 	}
 
@@ -1500,16 +1489,15 @@ TC_DELETE_ENTRY(const IPT_CHAINLABEL cha
 	struct chain_head *c;
 	struct rule_head *r, *i;
 
-	iptc_fn = TC_DELETE_ENTRY;
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	/* Create a rule_head from origfw. */
 	r = iptcc_alloc_rule(c, origfw->next_offset);
 	if (!r) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return 0;
 	}
 
@@ -1557,7 +1545,7 @@ TC_DELETE_ENTRY(const IPT_CHAINLABEL cha
 	}
 
 	free(r);
-	errno = ENOENT;
+	TC_ERR(EINVAL);
 	return 0;
 }
 
@@ -1571,15 +1559,13 @@ TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL
 	struct chain_head *c;
 	struct rule_head *r;
 
-	iptc_fn = TC_DELETE_NUM_ENTRY;
-
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (rulenum >= c->num_rules) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
@@ -1614,8 +1600,7 @@ TC_CHECK_PACKET(const IPT_CHAINLABEL cha
 		STRUCT_ENTRY *entry,
 		TC_HANDLE_T *handle)
 {
-	iptc_fn = TC_CHECK_PACKET;
-	errno = ENOSYS;
+	TC_ERR(ENOSYS);
 	return NULL;
 }
 
@@ -1626,9 +1611,8 @@ TC_FLUSH_ENTRIES(const IPT_CHAINLABEL ch
 	struct chain_head *c;
 	struct rule_head *r, *tmp;
 
-	iptc_fn = TC_FLUSH_ENTRIES;
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
@@ -1650,9 +1634,8 @@ TC_ZERO_ENTRIES(const IPT_CHAINLABEL cha
 	struct chain_head *c;
 	struct rule_head *r;
 
-	iptc_fn = TC_ZERO_ENTRIES;
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
@@ -1677,16 +1660,15 @@ TC_READ_COUNTER(const IPT_CHAINLABEL cha
 	struct chain_head *c;
 	struct rule_head *r;
 
-	iptc_fn = TC_READ_COUNTER;
 	CHECK(*handle);
 
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return NULL;
 	}
 
 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return NULL;
 	}
 
@@ -1701,16 +1683,15 @@ TC_ZERO_COUNTER(const IPT_CHAINLABEL cha
 	struct chain_head *c;
 	struct rule_head *r;
 	
-	iptc_fn = TC_ZERO_COUNTER;
 	CHECK(*handle);
 
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
@@ -1732,16 +1713,15 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chai
 	struct rule_head *r;
 	STRUCT_ENTRY *e;
 
-	iptc_fn = TC_SET_COUNTER;
 	CHECK(*handle);
 
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
-		errno = E2BIG;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
@@ -1763,8 +1743,6 @@ TC_CREATE_CHAIN(const IPT_CHAINLABEL cha
 {
 	static struct chain_head *c;
 
-	iptc_fn = TC_CREATE_CHAIN;
-
 	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
 	if (iptcc_find_label(chain, *handle)
@@ -1773,20 +1751,20 @@ TC_CREATE_CHAIN(const IPT_CHAINLABEL cha
 	    || strcmp(chain, LABEL_QUEUE) == 0
 	    || strcmp(chain, LABEL_RETURN) == 0) {
 		DEBUGP("Chain `%s' already exists\n", chain);
-		errno = EEXIST;
+		TC_ERR(EEXIST);
 		return 0;
 	}
 
 	if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
 		DEBUGP("Chain name `%s' too long\n", chain);
-		errno = EINVAL;
+		TC_ERR(E2BIG);
 		return 0;
 	}
 
 	c = iptcc_alloc_chain_head(chain, 0);
 	if (!c) {
 		DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		return 0;
 
 	}
@@ -1806,9 +1784,8 @@ TC_GET_REFERENCES(unsigned int *ref, con
 {
 	struct chain_head *c;
 
-	iptc_fn = TC_GET_REFERENCES;
 	if (!(c = iptcc_find_label(chain, *handle))) {
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
@@ -1824,34 +1801,33 @@ TC_DELETE_CHAIN(const IPT_CHAINLABEL cha
 	unsigned int references;
 	struct chain_head *c;
 
-	iptc_fn = TC_DELETE_CHAIN;
-
 	if (!(c = iptcc_find_label(chain, *handle))) {
 		DEBUGP("cannot find chain `%s'\n", chain);
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (TC_BUILTIN(chain, *handle)) {
 		DEBUGP("cannot remove builtin chain `%s'\n", chain);
-		errno = EINVAL;
+		TC_ERR(EINVAL);
 		return 0;
 	}
 
 	if (!TC_GET_REFERENCES(&references, chain, handle)) {
 		DEBUGP("cannot get references on chain `%s'\n", chain);
+		TC_ERR(ENOSYS);
 		return 0;
 	}
 
 	if (references > 0) {
 		DEBUGP("chain `%s' still has references\n", chain);
-		errno = EMLINK;
+		TC_ERR(EMLINK);
 		return 0;
 	}
 
 	if (c->num_rules) {
 		DEBUGP("chain `%s' is not empty\n", chain);
-		errno = ENOTEMPTY;
+		TC_ERR(ENOTEMPTY);
 		return 0;
 	}
 
@@ -1876,7 +1852,17 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL
 		    TC_HANDLE_T *handle)
 {
 	struct chain_head *c;
-	iptc_fn = TC_RENAME_CHAIN;
+
+	if (!(c = iptcc_find_label(oldname, *handle))
+	    || TC_BUILTIN(oldname, *handle)) {
+		TC_ERR(ENOENT);
+		return 0;
+	}
+
+	if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+		TC_ERR(E2BIG);
+		return 0;
+	}
 
 	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
            QUEUE, RETURN. */
@@ -1885,18 +1871,7 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL
 	    || strcmp(newname, LABEL_ACCEPT) == 0
 	    || strcmp(newname, LABEL_QUEUE) == 0
 	    || strcmp(newname, LABEL_RETURN) == 0) {
-		errno = EEXIST;
-		return 0;
-	}
-
-	if (!(c = iptcc_find_label(oldname, *handle))
-	    || TC_BUILTIN(oldname, *handle)) {
-		errno = ENOENT;
-		return 0;
-	}
-
-	if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
-		errno = EINVAL;
+		TC_ERR(EEXIST);
 		return 0;
 	}
 
@@ -1916,17 +1891,15 @@ TC_SET_POLICY(const IPT_CHAINLABEL chain
 {
 	struct chain_head *c;
 
-	iptc_fn = TC_SET_POLICY;
-
 	if (!(c = iptcc_find_label(chain, *handle))) {
 		DEBUGP("cannot find chain `%s'\n", chain);
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
 	if (!iptcc_is_builtin(c)) {
 		DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
-		errno = ENOENT;
+		TC_ERR(ENOENT);
 		return 0;
 	}
 
@@ -1935,7 +1908,7 @@ TC_SET_POLICY(const IPT_CHAINLABEL chain
 	else if (strcmp(policy, LABEL_DROP) == 0)
 		c->verdict = -NF_DROP - 1;
 	else {
-		errno = EINVAL;
+		TC_ERR(EINVAL);
 		return 0;
 	}
 
@@ -2035,7 +2008,6 @@ TC_COMMIT(TC_HANDLE_T *handle)
 	int new_number;
 	unsigned int new_size;
 
-	iptc_fn = TC_COMMIT;
 	CHECK(*handle);
 
 	/* Don't commit if nothing changed. */
@@ -2044,13 +2016,13 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
 	new_number = iptcc_compile_table_prep(*handle, &new_size);
 	if (new_number < 0) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		goto out_zero;
 	}
 
 	repl = malloc(sizeof(*repl) + new_size);
 	if (!repl) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		goto out_zero;
 	}
 	memset(repl, 0, sizeof(*repl) + new_size);
@@ -2066,13 +2038,13 @@ TC_COMMIT(TC_HANDLE_T *handle)
 	repl->counters = malloc(sizeof(STRUCT_COUNTERS)
 				* (*handle)->info.num_entries);
 	if (!repl->counters) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		goto out_free_repl;
 	}
 	/* These are the counters we're going to put back, later. */
 	newcounters = malloc(counterlen);
 	if (!newcounters) {
-		errno = ENOMEM;
+		TC_ERR(ENOMEM);
 		goto out_free_repl_counters;
 	}
 	memset(newcounters, 0, counterlen);
@@ -2089,7 +2061,7 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
 	ret = iptcc_compile_table(*handle, repl);
 	if (ret < 0) {
-		errno = ret;
+		TC_ERR(EINVAL);
 		goto out_free_newcounters;
 	}
 
@@ -2107,8 +2079,10 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
 	ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
 			 sizeof(*repl) + repl->size);
-	if (ret < 0)
+	if (ret < 0) {
+		TC_ERR(errno);
 		goto out_free_newcounters;
+	}
 
 	/* Put counters back. */
 	strcpy(newcounters->name, (*handle)->info.name);
@@ -2183,8 +2157,10 @@ TC_COMMIT(TC_HANDLE_T *handle)
 
 	ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
 			 newcounters, counterlen);
-	if (ret < 0)
+	if (ret < 0) {
+		TC_ERR(errno);
 		goto out_free_newcounters;
+	}
 
 	free(repl->counters);
 	free(repl);
@@ -2211,57 +2187,98 @@ TC_GET_RAW_SOCKET()
 	return sockfd;
 }
 
+void
+TC_ERROR(const char *function, int error)
+{
+	if (strlen(iptc_errfn))
+		printf("%s: %s\n", TC_LIBNAME, TC_STRERROR());
+	strncpy(iptc_errfn, function, 63);
+	iptc_errno = error;
+}
+
 /* Translates errno numbers into more human-readable form than strerror. */
 const char *
-TC_STRERROR(int err)
+TC_STRERROR()
 {
-	unsigned int i;
+	unsigned int i, length;
+	static char strbuf[256];
+
 	struct table_struct {
-		void *fn;
+		const char *fn;
 		int err;
 		const char *message;
 	} table [] =
-	  { { TC_INIT, EPERM, "Permission denied (you must be root)" },
-	    { TC_INIT, EINVAL, "Module is wrong version" },
-	    { TC_INIT, ENOENT, 
-		    "Table does not exist (do you need to insmod?)" },
-	    { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
-	    { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
-	    { TC_DELETE_CHAIN, EMLINK,
-	      "Can't delete chain with references left" },
-	    { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
-	    { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
-	    { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
-	    { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
-	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
-	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
-	    { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
-	    { TC_INSERT_ENTRY, EINVAL, "Target problem" },
-	    /* EINVAL for CHECK probably means bad interface. */
-	    { TC_CHECK_PACKET, EINVAL,
-	      "Bad arguments (does that interface exist?)" },
-	    { TC_CHECK_PACKET, ENOSYS,
+	  { { QF(TC_APPEND_ENTRY), ENOENT, "No chain by that name" },
+	    { QF(TC_CHECK_PACKET), ENOSYS,
 	      "Checking will most likely never get implemented" },
-	    /* ENOENT for DELETE probably means no matching rule */
-	    { TC_DELETE_ENTRY, ENOENT,
+	    { QF(TC_COMMIT), EINVAL, "Table compilation failed" },
+	    { QF(TC_COMMIT), ELOOP, "Loop found in table" },
+	    { QF(TC_COMMIT), EPERM, "Permission denied (you must be root)" },
+	    { QF(TC_CREATE_CHAIN), EEXIST, "Chain already exists" },
+	    { QF(TC_CREATE_CHAIN), E2BIG, "Chain name too long" },
+	    { QF(TC_DELETE_CHAIN), ENOTEMPTY, "Chain is not empty" },
+	    { QF(TC_DELETE_CHAIN), EINVAL, "Can't delete built-in chain" },
+	    { QF(TC_DELETE_CHAIN), EMLINK,
+	      "Can't delete chain with references left" },
+	    { QF(TC_DELETE_CHAIN), ENOENT, "No chain by that name" },
+	    { QF(TC_DELETE_CHAIN), ENOSYS, "Couldn't get reference list for chain" },
+	    { QF(TC_DELETE_ENTRY), ENOENT, "No chain by that name" },
+	    { QF(TC_DELETE_ENTRY), EINVAL,
 	      "Bad rule (does a matching rule exist in that chain?)" },
-	    { TC_SET_POLICY, ENOENT,
-	      "Bad built-in chain name" },
-	    { TC_SET_POLICY, EINVAL,
-	      "Bad policy name" },
+	    { QF(TC_DELETE_NUM_ENTRY), ENOENT, "No chain by that name" },
+	    { QF(TC_DELETE_NUM_ENTRY), E2BIG, "Index of deletion too big" },
+	    { QF(TC_FIRST_CHAIN), ENOENT, "No chains to be found" },
+	    { QF(TC_FIRST_RULE), ENOENT, "No chain by that name" },
+	    { QF(TC_FLUSH_ENTRIES), ENOENT, "No chain by that name" },
+	    { QF(TC_GET_POLICY), ENOENT, "No chain by that name" },
+	    { QF(TC_GET_REFERENCES), ENOENT, "No chain by that name" },
+	    { QF(TC_GET_RULE), ENOENT, "No chain by that name" },
+	    { QF(TC_INIT), EPERM, "Permission denied (you must be root)" },
+	    { QF(TC_INIT), EINVAL, "Module is wrong version" },
+	    { QF(TC_INIT), E2BIG, "Table name too long" },
+	    { QF(TC_INIT), ENOENT, 
+		    "Table does not exist (do you need to insmod?)" },
+	    { QF(TC_INSERT_ENTRY), E2BIG, "Index of insertion too big" },
+	    { QF(TC_INSERT_ENTRY), ENOENT, "No chain by that name" },
+	    { QF(TC_NUM_RULES), ENOENT, "No chain by that name" },
+	    { QF(TC_READ_COUNTER), E2BIG, "Index of counter read too big" },
+	    { QF(TC_READ_COUNTER), ENOENT, "No chain by that name" },
+	    { QF(TC_RENAME_CHAIN), E2BIG, "Chain name too long" },
+	    { QF(TC_RENAME_CHAIN), ENOENT, "No chain by that name" },
+	    { QF(TC_RENAME_CHAIN), EEXIST, "Chain already exists" },
+	    { QF(TC_REPLACE_ENTRY), E2BIG, "Index of replacement too big" },
+	    { QF(TC_REPLACE_ENTRY), ENOENT, "No chain by that name" },
+	    { QF(TC_SET_COUNTER), E2BIG, "Index of counter too big" },
+	    { QF(TC_SET_COUNTER), ENOENT, "No chain by that name" },
+	    { QF(TC_SET_POLICY), ENOENT, "Bad built-in chain name" },
+	    { QF(TC_SET_POLICY), EINVAL, "Bad policy name" },
+	    { QF(TC_ZERO_COUNTER), E2BIG, "Index of counter too big" },
+	    { QF(TC_ZERO_COUNTER), ENOENT, "No chain by that name" },
+	    { QF(TC_ZERO_ENTRIES), ENOENT, "No chain by that name" },
+
+	    { QF(cache_add_entry), EINVAL, "Target problem" },
+	    { QF(iptcc_map_target), EINVAL, "Can't jump to a built-in target" },
+	    { QF(iptcc_standard_map), EINVAL, "Target problem" },
+	    { QF(list_entries), ENOENT, "No chain by that name" },
 
-	    { NULL, 0, "Incompatible with this kernel" },
 	    { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
-	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
 	    { NULL, ENOMEM, "Memory allocation problem" },
-	    { NULL, ENOENT, "No chain/target/match by that name" },
 	  };
+	  
+	if (!strlen(iptc_errfn))
+		return "Something forgot to set the error reason";
 
 	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
-		if ((!table[i].fn || table[i].fn == iptc_fn)
-		    && table[i].err == err)
+		if ((!table[i].fn || !strcmp(table[i].fn, iptc_errfn))
+		    && table[i].err == iptc_errno)
 			return table[i].message;
 	}
 
-	return strerror(err);
+	length = snprintf(strbuf, 256, "Unknown error, %s: %i", iptc_errfn, iptc_errno);
+
+	/* Avoid printing "(Success)" */
+	if (iptc_errno)
+		snprintf(strbuf + length, 256 - length, " (%s)", strerror(iptc_errno));
+
+	return strbuf;
 }

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-09-19 12:56 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-08-11 23:21 [PATCH RFT] Improve iptables error reporting Martin Josefsson
2007-09-11  8:42 ` Martin Josefsson
2007-09-19 12:56   ` Patrick McHardy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).