Introduces xtables match/target registration

- moves lib_dir to xtables.c
- introduces struct pfinfo which has protocol family dependent infomations.
- unifies load_ip[6]tables_ko() and moves them as load_xtables_ko()
- introduces xt_{match,match_rule,target,tryload} and replaces
  ip[6]t_* with them
- unifies following functions and move them to xtables.c
        - find_{match,find_target}
        - compatible_revision, compatible_{match,target}_revision
- introduces xtables_register_{match,target} and make
  register_{match,target}[6] call them. xtables_register_* register ONLY
  matches/targets matched protocol family

Some concepts:
- source compatibility for libip[6]t_xxx.c with warning on compilation
  not binary compatibility.
- binary compatibility between 2.4/2.6 kernel and iptables/ip6tables,
  of cause.
- xtables is enough to support only one address family at runtime.
  Then xtables keeps infomations of only the focused address famiy
  in struct afinfo.
diff --git a/iptables.c b/iptables.c
index 39b8e01..47de864 100644
--- a/iptables.c
+++ b/iptables.c
@@ -187,15 +187,9 @@
 
 const char *program_version;
 const char *program_name;
-char *lib_dir;
 
 int kernel_version;
 
-/* Keeping track of external matches and targets: linked lists.  */
-struct iptables_match *iptables_matches = NULL;
-struct iptables_target *iptables_targets = NULL;
-
-/* Extra debugging from libiptc */
 extern void dump_entries(const iptc_handle_t handle);
 
 /* A few hardcoded protocols for 'all' and in case the user has no
@@ -205,6 +199,15 @@
 	u_int8_t num;
 };
 
+struct afinfo afinfo = {
+	.family		= AF_INET,
+	.libprefix	= "libipt_",
+	.ipproto	= IPPROTO_IP,
+	.kmod		= "ip_tables",
+	.so_rev_match	= IPT_SO_GET_REVISION_MATCH,
+	.so_rev_target	= IPT_SO_GET_REVISION_TARGET,
+};
+
 /* Primitive headers... */
 /* defined in netinet/in.h */
 #if 0
@@ -472,7 +475,7 @@
 	/* Print out any special helps. A user might like to be able
 	   to add a --help to the commandline, and see expected
 	   results. So we call help for all specified matches & targets */
-	for (t = iptables_targets; t ;t = t->next) {
+	for (t = xtables_targets; t ;t = t->next) {
 		if (t->used) {
 			printf("\n");
 			t->help();
@@ -698,82 +701,6 @@
 	}
 }
 
-struct iptables_match *
-find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches)
-{
-	struct iptables_match *ptr;
-
-	for (ptr = iptables_matches; ptr; ptr = ptr->next) {
-		if (strcmp(name, ptr->name) == 0) {
-			struct iptables_match *clone;
-			
-			/* First match of this type: */
-			if (ptr->m == NULL)
-				break;
-
-			/* Second and subsequent clones */
-			clone = fw_malloc(sizeof(struct iptables_match));
-			memcpy(clone, ptr, sizeof(struct iptables_match));
-			clone->mflags = 0;
-			/* This is a clone: */
-			clone->next = clone;
-
-			ptr = clone;
-			break;
-		}
-	}		
-
-#ifndef NO_SHARED_LIBS
-	if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
-		char path[strlen(lib_dir) + sizeof("/libipt_.so")
-			 + strlen(name)];
-		sprintf(path, "%s/libipt_%s.so", lib_dir, name);
-		if (dlopen(path, RTLD_NOW)) {
-			/* Found library.  If it didn't register itself,
-			   maybe they specified target as match. */
-			ptr = find_match(name, DONT_LOAD, NULL);
-
-			if (!ptr)
-				exit_error(PARAMETER_PROBLEM,
-					   "Couldn't load match `%s'\n",
-					   name);
-		} else if (tryload == LOAD_MUST_SUCCEED)
-			exit_error(PARAMETER_PROBLEM,
-				   "Couldn't load match `%s':%s\n",
-				   name, dlerror());
-	}
-#else
-	if (ptr && !ptr->loaded) {
-		if (tryload != DONT_LOAD)
-			ptr->loaded = 1;
-		else
-			ptr = NULL;
-	}
-	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
-		exit_error(PARAMETER_PROBLEM,
-			   "Couldn't find match `%s'\n", name);
-	}
-#endif
-
-	if (ptr && matches) {
-		struct iptables_rule_match **i;
-		struct iptables_rule_match *newentry;
-
-		newentry = fw_malloc(sizeof(struct iptables_rule_match));
-
-		for (i = matches; *i; i = &(*i)->next) {
-			if (strcmp(name, (*i)->match->name) == 0)
-				(*i)->completed = 1;
-		}
-		newentry->match = ptr;
-		newentry->completed = 0;
-		newentry->next = NULL;
-		*i = newentry;
-	}
-
-	return ptr;
-}
-
 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
 static struct iptables_match *
 find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches)
@@ -1025,61 +952,6 @@
 	}
 }
 
-struct iptables_target *
-find_target(const char *name, enum ipt_tryload tryload)
-{
-	struct iptables_target *ptr;
-
-	/* Standard target? */
-	if (strcmp(name, "") == 0
-	    || strcmp(name, IPTC_LABEL_ACCEPT) == 0
-	    || strcmp(name, IPTC_LABEL_DROP) == 0
-	    || strcmp(name, IPTC_LABEL_QUEUE) == 0
-	    || strcmp(name, IPTC_LABEL_RETURN) == 0)
-		name = "standard";
-
-	for (ptr = iptables_targets; ptr; ptr = ptr->next) {
-		if (strcmp(name, ptr->name) == 0)
-			break;
-	}
-
-#ifndef NO_SHARED_LIBS
-	if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
-		char path[strlen(lib_dir) + sizeof("/libipt_.so")
-			 + strlen(name)];
-		sprintf(path, "%s/libipt_%s.so", lib_dir, name);
-		if (dlopen(path, RTLD_NOW)) {
-			/* Found library.  If it didn't register itself,
-			   maybe they specified match as a target. */
-			ptr = find_target(name, DONT_LOAD);
-			if (!ptr)
-				exit_error(PARAMETER_PROBLEM,
-					   "Couldn't load target `%s'\n",
-					   name);
-		} else if (tryload == LOAD_MUST_SUCCEED)
-			exit_error(PARAMETER_PROBLEM,
-				   "Couldn't load target `%s':%s\n",
-				   name, dlerror());
-	}
-#else
-	if (ptr && !ptr->loaded) {
-		if (tryload != DONT_LOAD)
-			ptr->loaded = 1;
-		else
-			ptr = NULL;
-	}
-	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
-		exit_error(PARAMETER_PROBLEM,
-			   "Couldn't find target `%s'\n", name);
-	}
-#endif
-
-	if (ptr)
-		ptr->used = 1;
-
-	return ptr;
-}
-
 static struct option *
 merge_options(struct option *oldopts, const struct option *newopts,
 	      unsigned int *option_offset)
@@ -1105,164 +977,16 @@
 	return merge;
 }
 
-static int compatible_revision(const char *name, u_int8_t revision, int opt)
+void register_match(struct iptables_match *me)
 {
-	struct ipt_get_revision rev;
-	socklen_t s = sizeof(rev);
-	int max_rev, sockfd;
-
-	sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
-	if (sockfd < 0) {
-		fprintf(stderr, "Could not open socket to kernel: %s\n",
-			strerror(errno));
-		exit(1);
-	}
-
-	load_iptables_ko(modprobe, 1);
-
-	strcpy(rev.name, name);
-	rev.revision = revision;
-
-	max_rev = getsockopt(sockfd, IPPROTO_IP, opt, &rev, &s);
-	if (max_rev < 0) {
-		/* Definitely don't support this? */
-		if (errno == ENOENT || errno == EPROTONOSUPPORT) {
-			close(sockfd);
-			return 0;
-		} else if (errno == ENOPROTOOPT) {
-			close(sockfd);
-			/* Assume only revision 0 support (old kernel) */
-			return (revision == 0);
-		} else {
-			fprintf(stderr, "getsockopt failed strangely: %s\n",
-				strerror(errno));
-			exit(1);
-		}
-	}
-	close(sockfd);
-	return 1;
+	me->family = PF_INET;
+	xtables_register_match(me);
 }
 
-static int compatible_match_revision(const char *name, u_int8_t revision)
+void register_target(struct iptables_target *me)
 {
-	return compatible_revision(name, revision, IPT_SO_GET_REVISION_MATCH);
-}
-
-static int compatible_target_revision(const char *name, u_int8_t revision)
-{
-	return compatible_revision(name, revision, IPT_SO_GET_REVISION_TARGET);
-}
-
-void
-register_match(struct iptables_match *me)
-{
-	struct iptables_match **i, *old;
-
-	if (strcmp(me->version, program_version) != 0) {
-		fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
-			program_name, me->name, me->version, program_version);
-		exit(1);
-	}
-
-	/* Revision field stole a char from name. */
-	if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
-		fprintf(stderr, "%s: target `%s' has invalid name\n",
-			program_name, me->name);
-		exit(1);
-	}
-
-	old = find_match(me->name, DURING_LOAD, NULL);
-	if (old) {
-		if (old->revision == me->revision) {
-			fprintf(stderr,
-				"%s: match `%s' already registered.\n",
-				program_name, me->name);
-			exit(1);
-		}
-
-		/* Now we have two (or more) options, check compatibility. */
-		if (compatible_match_revision(old->name, old->revision)
-		    && old->revision > me->revision)
-			return;
-
-		/* Replace if compatible. */
-		if (!compatible_match_revision(me->name, me->revision))
-			return;
-
-		/* Delete old one. */
-		for (i = &iptables_matches; *i!=old; i = &(*i)->next);
-		*i = old->next;
-	}
-
-	if (me->size != IPT_ALIGN(me->size)) {
-		fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
-			program_name, me->name, (unsigned int)me->size);
-		exit(1);
-	}
-
-	/* Append to list. */
-	for (i = &iptables_matches; *i; i = &(*i)->next);
-	me->next = NULL;
-	*i = me;
-
-	me->m = NULL;
-	me->mflags = 0;
-}
-
-void
-register_target(struct iptables_target *me)
-{
-	struct iptables_target *old;
-
-	if (strcmp(me->version, program_version) != 0) {
-		fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
-			program_name, me->name, me->version, program_version);
-		exit(1);
-	}
-
-	/* Revision field stole a char from name. */
-	if (strlen(me->name) >= IPT_FUNCTION_MAXNAMELEN-1) {
-		fprintf(stderr, "%s: target `%s' has invalid name\n",
-			program_name, me->name);
-		exit(1);
-	}
-
-	old = find_target(me->name, DURING_LOAD);
-	if (old) {
-		struct iptables_target **i;
-
-		if (old->revision == me->revision) {
-			fprintf(stderr,
-				"%s: target `%s' already registered.\n",
-				program_name, me->name);
-			exit(1);
-		}
-
-		/* Now we have two (or more) options, check compatibility. */
-		if (compatible_target_revision(old->name, old->revision)
-		    && old->revision > me->revision)
-			return;
-
-		/* Replace if compatible. */
-		if (!compatible_target_revision(me->name, me->revision))
-			return;
-
-		/* Delete old one. */
-		for (i = &iptables_targets; *i!=old; i = &(*i)->next);
-		*i = old->next;
-	}
-
-	if (me->size != IPT_ALIGN(me->size)) {
-		fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
-			program_name, me->name, (unsigned int)me->size);
-		exit(1);
-	}
-
-	/* Prepend to list. */
-	me->next = iptables_targets;
-	iptables_targets = me;
-	me->t = NULL;
-	me->tflags = 0;
+	me->family = PF_INET;
+	xtables_register_target(me);
 }
 
 static void
@@ -1580,7 +1304,7 @@
 
 	mask = fw_calloc(1, size
 			 + IPT_ALIGN(sizeof(struct ipt_entry_target))
-			 + iptables_targets->size);
+			 + xtables_targets->size);
 
 	memset(mask, 0xFF, sizeof(struct ipt_entry));
 	mptr = mask + sizeof(struct ipt_entry);
@@ -1594,7 +1318,7 @@
 
 	memset(mptr, 0xFF,
 	       IPT_ALIGN(sizeof(struct ipt_entry_target))
-	       + iptables_targets->userspacesize);
+	       + xtables_targets->userspacesize);
 
 	return mask;
 }
@@ -1754,19 +1478,6 @@
 	return found;
 }
 
-int load_iptables_ko(const char *modprobe, int quiet)
-{
-	static int loaded = 0;
-	static int ret = -1;
-
-	if (!loaded) {
-		ret = xtables_insmod("ip_tables", modprobe, quiet);
-		loaded = (ret == 0);
-	}
-
-	return ret;
-}
-
 static struct ipt_entry *
 generate_entry(const struct ipt_entry *fw,
 	       struct iptables_rule_match *matches,
@@ -1870,10 +1581,10 @@
 
 	/* clear mflags in case do_command gets called a second time
 	 * (we clear the global list of all matches for security)*/
-	for (m = iptables_matches; m; m = m->next)
+	for (m = xtables_matches; m; m = m->next)
 		m->mflags = 0;
 
-	for (t = iptables_targets; t; t = t->next) {
+	for (t = xtables_targets; t; t = t->next) {
 		t->tflags = 0;
 		t->used = 0;
 	}
@@ -2347,7 +2058,7 @@
 		*handle = iptc_init(*table);
 
 	/* try to insmod the module if iptc_init failed */
-	if (!*handle && load_iptables_ko(modprobe, 0) != -1)
+	if (!*handle && load_xtables_ko(modprobe, 0) != -1)
 		*handle = iptc_init(*table);
 
 	if (!*handle)