Merge branch 'stable' into 'master'
diff --git a/configure.ac b/configure.ac
index ba616ab..ffd088c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,8 +2,8 @@
 AC_INIT([iptables], [1.4.17])
 
 # See libtool.info "Libtool's versioning system"
-libxtables_vcurrent=9
-libxtables_vage=0
+libxtables_vcurrent=10
+libxtables_vage=1
 
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_HEADERS([config.h])
diff --git a/extensions/libip6t_LOG.man b/extensions/libip6t_LOG.man
index b7803fe..0a48640 100644
--- a/extensions/libip6t_LOG.man
+++ b/extensions/libip6t_LOG.man
@@ -11,7 +11,10 @@
 then DROP (or REJECT).
 .TP
 \fB\-\-log\-level\fP \fIlevel\fP
-Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+Level of logging, which can be (system-specific) numeric or a mnemonic.
+Possible values are (in decreasing order of priority): \fBemerg\fP,
+\fBalert\fP, \fBcrit\fP, \fBerror\fP, \fBwarning\fP, \fBnotice\fP, \fBinfo\fP
+or \fBdebug\fP.
 .TP
 \fB\-\-log\-prefix\fP \fIprefix\fP
 Prefix log messages with the specified prefix; up to 29 letters long,
diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man
index 47c35e0..f2574f8 100644
--- a/extensions/libipt_LOG.man
+++ b/extensions/libipt_LOG.man
@@ -11,7 +11,10 @@
 then DROP (or REJECT).
 .TP
 \fB\-\-log\-level\fP \fIlevel\fP
-Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+Level of logging, which can be (system-specific) numeric or a mnemonic.
+Possible values are (in decreasing order of priority): \fBemerg\fP,
+\fBalert\fP, \fBcrit\fP, \fBerror\fP, \fBwarning\fP, \fBnotice\fP, \fBinfo\fP
+or \fBdebug\fP.
 .TP
 \fB\-\-log\-prefix\fP \fIprefix\fP
 Prefix log messages with the specified prefix; up to 29 letters long,
diff --git a/include/xtables.h b/include/xtables.h
index 2cc1a02..75de958 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -417,6 +417,8 @@
 extern struct xtables_target *xtables_find_target(const char *name,
 	enum xtables_tryload);
 
+extern void xtables_rule_matches_free(struct xtables_rule_match **matches);
+
 /* Your shared library should call one of these. */
 extern void xtables_register_match(struct xtables_match *me);
 extern void xtables_register_matches(struct xtables_match *, unsigned int);
@@ -467,6 +469,22 @@
  */
 extern void xtables_save_string(const char *value);
 
+#define FMT_NUMERIC		0x0001
+#define FMT_NOCOUNTS		0x0002
+#define FMT_KILOMEGAGIGA	0x0004
+#define FMT_OPTIONS		0x0008
+#define FMT_NOTABLE		0x0010
+#define FMT_NOTARGET		0x0020
+#define FMT_VIA			0x0040
+#define FMT_NONEWLINE		0x0080
+#define FMT_LINENUMBERS		0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                        | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+extern void xtables_print_num(uint64_t number, unsigned int format);
+
 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
 #	ifdef _INIT
 #		undef _init
diff --git a/iptables/ip6tables-restore.8 b/iptables/ip6tables-restore.8
index 59a3b2e..dbe19da 100644
--- a/iptables/ip6tables-restore.8
+++ b/iptables/ip6tables-restore.8
@@ -21,7 +21,8 @@
 .SH NAME
 ip6tables-restore \(em Restore IPv6 Tables
 .SH SYNOPSIS
-\fBip6tables\-restore\fP [\fB\-c\fP] [\fB\-n\fP]
+\fBip6tables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+[\fB\-T\fP \fIname\fP]
 .SH DESCRIPTION
 .PP
 .B ip6tables-restore
@@ -31,8 +32,23 @@
 \fB\-c\fR, \fB\-\-counters\fR
 restore the values of all packet and byte counters
 .TP
+\fB\-h\fP, \fB\-\-help\fP
+Print a short option summary.
+.TP
 \fB\-n\fR, \fB\-\-noflush\fR 
-don't flush the previous contents of the table. If not specified, 
+don't flush the previous contents of the table. If not specified,
+\fBip6tables-restore\fP flushes (deletes) all previous contents of the
+respective table.
+.TP
+\fB\-t\fP, \fB\-\-test\fP
+Only parse and construct the ruleset, but do not commit it.
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Print additional debug info during ruleset processing.
+.TP
+\fB\-M\fP, \fB\-\-modprobe\fP \fImodprobe_program\fP
+Specify the path to the modprobe program. By default, ip6tables-restore will
+inspect /proc/sys/kernel/modprobe to determine the executable's path.
 .TP
 \fB\-T\fP, \fB\-\-table\fP \fIname\fP
 Restore only the named table even if the input stream contains other ones.
diff --git a/iptables/ip6tables.8.in b/iptables/ip6tables.8.in
index 078bcac..58b7bfc 100644
--- a/iptables/ip6tables.8.in
+++ b/iptables/ip6tables.8.in
@@ -240,6 +240,15 @@
 The following parameters make up a rule specification (as used in the
 add, delete, insert, replace and append commands).
 .TP
+\fB\-4\fP, \fB\-\-ipv4\fP
+If a rule using the \fB\-4\fP option is inserted with (and only with)
+ip6tables-restore, it will be silently ignored. Any other uses will throw an
+error. This option allows to put both IPv4 and IPv6 rules in a single rule file
+for use with both iptables-restore and ip6tables-restore.
+.TP
+\fB\-6\fP, \fB\-\-ipv6\fP
+This option has no effect in ip6tables and ip6tables-restore.
+.TP
 [\fB!\fP] \fB\-p\fP, \fB\-\-protocol\fP \fIprotocol\fP
 The protocol of the rule or of the packet to check.
 The specified protocol can be one of \fBtcp\fP, \fBudp\fP, \fBudplite\fP,
@@ -281,6 +290,13 @@
 (source) flag for a detailed description of the syntax.  The flag
 \fB\-\-dst\fP is an alias for this option.
 .TP
+\fB\-m\fP, \fB\-\-match\fP \fImatch\fP
+Specifies a match to use, that is, an extension module that tests for a
+specific property. The set of matches make up the condition under which a
+target is invoked. Matches are evaluated first to last as specified on the
+command line and work in short-circuit fashion, i.e. if one extension yields
+false, evaluation will stop.
+.TP
 \fB\-j\fP, \fB\-\-jump\fP \fItarget\fP
 This specifies the target of the rule; i.e., what to do if the packet
 matches it.  The target can be a user-defined chain (other than the
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 0e11a9e..556647f 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -52,21 +52,6 @@
 #define FALSE 0
 #endif
 
-#define FMT_NUMERIC	0x0001
-#define FMT_NOCOUNTS	0x0002
-#define FMT_KILOMEGAGIGA 0x0004
-#define FMT_OPTIONS	0x0008
-#define FMT_NOTABLE	0x0010
-#define FMT_NOTARGET	0x0020
-#define FMT_VIA		0x0040
-#define FMT_NONEWLINE	0x0080
-#define FMT_LINENUMBERS 0x0100
-
-#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
-			| FMT_NUMERIC | FMT_NOTABLE)
-#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
-
-
 #define CMD_NONE		0x0000U
 #define CMD_INSERT		0x0001U
 #define CMD_DELETE		0x0002U
@@ -442,31 +427,6 @@
 	}
 }
 
-static void
-print_num(uint64_t number, unsigned int format)
-{
-	if (format & FMT_KILOMEGAGIGA) {
-		if (number > 99999) {
-			number = (number + 500) / 1000;
-			if (number > 9999) {
-				number = (number + 500) / 1000;
-				if (number > 9999) {
-					number = (number + 500) / 1000;
-					if (number > 9999) {
-						number = (number + 500) / 1000;
-						printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
-					}
-					else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
-				}
-				else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
-			} else
-				printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
-		} else
-			printf(FMT("%5llu ","%llu "), (unsigned long long)number);
-	} else
-		printf(FMT("%8llu ","%llu "), (unsigned long long)number);
-}
-
 
 static void
 print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
@@ -478,9 +438,9 @@
 		printf(" (policy %s", pol);
 		if (!(format & FMT_NOCOUNTS)) {
 			fputc(' ', stdout);
-			print_num(counters.pcnt, (format|FMT_NOTABLE));
+			xtables_print_num(counters.pcnt, (format|FMT_NOTABLE));
 			fputs("packets, ", stdout);
-			print_num(counters.bcnt, (format|FMT_NOTABLE));
+			xtables_print_num(counters.bcnt, (format|FMT_NOTABLE));
 			fputs("bytes", stdout);
 		}
 		printf(")\n");
@@ -563,8 +523,8 @@
 		printf(FMT("%-4u ", "%u "), num);
 
 	if (!(format & FMT_NOCOUNTS)) {
-		print_num(fw->counters.pcnt, format);
-		print_num(fw->counters.bcnt, format);
+		xtables_print_num(fw->counters.pcnt, format);
+		xtables_print_num(fw->counters.bcnt, format);
 	}
 
 	if (!(format & FMT_NOTARGET))
@@ -1249,27 +1209,6 @@
 	return e;
 }
 
-static void clear_rule_matches(struct xtables_rule_match **matches)
-{
-	struct xtables_rule_match *matchp, *tmp;
-
-	for (matchp = *matches; matchp;) {
-		tmp = matchp->next;
-		if (matchp->match->m) {
-			free(matchp->match->m);
-			matchp->match->m = NULL;
-		}
-		if (matchp->match == matchp->match->next) {
-			free(matchp->match);
-			matchp->match = NULL;
-		}
-		free(matchp);
-		matchp = tmp;
-	}
-
-	*matches = NULL;
-}
-
 static void command_jump(struct iptables_command_state *cs)
 {
 	size_t size;
@@ -1963,7 +1902,7 @@
 	if (verbose > 1)
 		dump_entries6(*handle);
 
-	clear_rule_matches(&cs.matches);
+	xtables_rule_matches_free(&cs.matches);
 
 	if (e != NULL) {
 		free(e);
diff --git a/iptables/iptables-restore.8 b/iptables/iptables-restore.8
index 0dd20cb..2b1d102 100644
--- a/iptables/iptables-restore.8
+++ b/iptables/iptables-restore.8
@@ -21,7 +21,8 @@
 .SH NAME
 iptables-restore \(em Restore IP Tables
 .SH SYNOPSIS
-\fBiptables\-restore\fP [\fB\-c\fP] [\fB\-n\fP] [\fB\-T\fP \fIname\fP]
+\fBiptables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+[\fB\-T\fP \fIname\fP]
 .SH DESCRIPTION
 .PP
 .B iptables-restore
@@ -31,10 +32,23 @@
 \fB\-c\fR, \fB\-\-counters\fR
 restore the values of all packet and byte counters
 .TP
+\fB\-h\fP, \fB\-\-help\fP
+Print a short option summary.
+.TP
 \fB\-n\fR, \fB\-\-noflush\fR 
 don't flush the previous contents of the table. If not specified, 
 .B iptables-restore
-flushes (deletes) all previous contents of the respective IP Table.
+flushes (deletes) all previous contents of the respective table.
+.TP
+\fB\-t\fP, \fB\-\-test\fP
+Only parse and construct the ruleset, but do not commit it.
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Print additional debug info during ruleset processing.
+.TP
+\fB\-M\fP, \fB\-\-modprobe\fP \fImodprobe_program\fP
+Specify the path to the modprobe program. By default, iptables-restore will
+inspect /proc/sys/kernel/modprobe to determine the executable's path.
 .TP
 \fB\-T\fP, \fB\-\-table\fP \fIname\fP
 Restore only the named table even if the input stream contains other ones.
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index d6b409d..013ac77 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -243,6 +243,15 @@
 The following parameters make up a rule specification (as used in the
 add, delete, insert, replace and append commands).
 .TP
+\fB\-4\fP, \fB\-\-ipv4\fP
+This option has no effect in iptables and iptables-restore.
+.TP
+\fB\-6\fP, \fB\-\-ipv6\fP
+If a rule using the \fB\-6\fP option is inserted with (and only with)
+iptables-restore, it will be silently ignored. Any other uses will throw an
+error. This option allows to put both IPv4 and IPv6 rules in a single rule file
+for use with both iptables-restore and ip6tables-restore.
+.TP
 [\fB!\fP] \fB\-p\fP, \fB\-\-protocol\fP \fIprotocol\fP
 The protocol of the rule or of the packet to check.
 The specified protocol can be one of \fBtcp\fP, \fBudp\fP, \fBudplite\fP,
@@ -277,6 +286,13 @@
 (source) flag for a detailed description of the syntax.  The flag
 \fB\-\-dst\fP is an alias for this option.
 .TP
+\fB\-m\fP, \fB\-\-match\fP \fImatch\fP
+Specifies a match to use, that is, an extension module that tests for a
+specific property. The set of matches make up the condition under which a
+target is invoked. Matches are evaluated first to last as specified on the
+command line and work in short-circuit fashion, i.e. if one extension yields
+false, evaluation will stop.
+.TP
 \fB\-j\fP, \fB\-\-jump\fP \fItarget\fP
 This specifies the target of the rule; i.e., what to do if the packet
 matches it.  The target can be a user-defined chain (other than the
diff --git a/iptables/iptables.c b/iptables/iptables.c
index f765cf9..00e3f01 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -48,21 +48,6 @@
 #define FALSE 0
 #endif
 
-#define FMT_NUMERIC	0x0001
-#define FMT_NOCOUNTS	0x0002
-#define FMT_KILOMEGAGIGA 0x0004
-#define FMT_OPTIONS	0x0008
-#define FMT_NOTABLE	0x0010
-#define FMT_NOTARGET	0x0020
-#define FMT_VIA		0x0040
-#define FMT_NONEWLINE	0x0080
-#define FMT_LINENUMBERS 0x0100
-
-#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
-			| FMT_NUMERIC | FMT_NOTABLE)
-#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
-
-
 #define CMD_NONE		0x0000U
 #define CMD_INSERT		0x0001U
 #define CMD_DELETE		0x0002U
@@ -186,22 +171,6 @@
 #define prog_name iptables_globals.program_name
 #define prog_vers iptables_globals.program_version
 
-/* Primitive headers... */
-/* defined in netinet/in.h */
-#if 0
-#ifndef IPPROTO_ESP
-#define IPPROTO_ESP 50
-#endif
-#ifndef IPPROTO_AH
-#define IPPROTO_AH 51
-#endif
-#endif
-
-enum {
-	IPT_DOTTED_ADDR = 0,
-	IPT_DOTTED_MASK
-};
-
 static void __attribute__((noreturn))
 exit_tryhelp(int status)
 {
@@ -445,32 +414,6 @@
 }
 
 static void
-print_num(uint64_t number, unsigned int format)
-{
-	if (format & FMT_KILOMEGAGIGA) {
-		if (number > 99999) {
-			number = (number + 500) / 1000;
-			if (number > 9999) {
-				number = (number + 500) / 1000;
-				if (number > 9999) {
-					number = (number + 500) / 1000;
-					if (number > 9999) {
-						number = (number + 500) / 1000;
-						printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
-					}
-					else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
-				}
-				else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
-			} else
-				printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
-		} else
-			printf(FMT("%5llu ","%llu "), (unsigned long long)number);
-	} else
-		printf(FMT("%8llu ","%llu "), (unsigned long long)number);
-}
-
-
-static void
 print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
 {
 	struct xt_counters counters;
@@ -480,9 +423,9 @@
 		printf(" (policy %s", pol);
 		if (!(format & FMT_NOCOUNTS)) {
 			fputc(' ', stdout);
-			print_num(counters.pcnt, (format|FMT_NOTABLE));
+			xtables_print_num(counters.pcnt, (format|FMT_NOTABLE));
 			fputs("packets, ", stdout);
-			print_num(counters.bcnt, (format|FMT_NOTABLE));
+			xtables_print_num(counters.bcnt, (format|FMT_NOTABLE));
 			fputs("bytes", stdout);
 		}
 		printf(")\n");
@@ -567,8 +510,8 @@
 		printf(FMT("%-4u ", "%u "), num);
 
 	if (!(format & FMT_NOCOUNTS)) {
-		print_num(fw->counters.pcnt, format);
-		print_num(fw->counters.bcnt, format);
+		xtables_print_num(fw->counters.pcnt, format);
+		xtables_print_num(fw->counters.bcnt, format);
 	}
 
 	if (!(format & FMT_NOTARGET))
@@ -1257,27 +1200,6 @@
 	return e;
 }
 
-static void clear_rule_matches(struct xtables_rule_match **matches)
-{
-	struct xtables_rule_match *matchp, *tmp;
-
-	for (matchp = *matches; matchp;) {
-		tmp = matchp->next;
-		if (matchp->match->m) {
-			free(matchp->match->m);
-			matchp->match->m = NULL;
-		}
-		if (matchp->match == matchp->match->next) {
-			free(matchp->match);
-			matchp->match = NULL;
-		}
-		free(matchp);
-		matchp = tmp;
-	}
-
-	*matches = NULL;
-}
-
 static void command_jump(struct iptables_command_state *cs)
 {
 	size_t size;
@@ -1979,7 +1901,7 @@
 	if (verbose > 1)
 		dump_entries(*handle);
 
-	clear_rule_matches(&cs.matches);
+	xtables_rule_matches_free(&cs.matches);
 
 	if (e != NULL) {
 		free(e);
diff --git a/libxtables/xtables.c b/libxtables/xtables.c
index 4c91286..009ab91 100644
--- a/libxtables/xtables.c
+++ b/libxtables/xtables.c
@@ -1075,6 +1075,28 @@
 	} while (n > 0);
 }
 
+/* receives a list of xtables_rule_match, release them */
+void xtables_rule_matches_free(struct xtables_rule_match **matches)
+{
+	struct xtables_rule_match *matchp, *tmp;
+
+	for (matchp = *matches; matchp;) {
+		tmp = matchp->next;
+		if (matchp->match->m) {
+			free(matchp->match->m);
+			matchp->match->m = NULL;
+		}
+		if (matchp->match == matchp->match->next) {
+			free(matchp->match);
+			matchp->match = NULL;
+		}
+		free(matchp);
+		matchp = tmp;
+	}
+
+	*matches = NULL;
+}
+
 /**
  * xtables_param_act - act on condition
  * @status:	a constant from enum xtables_exittype
@@ -1890,6 +1912,35 @@
 	return -1;
 }
 
+void xtables_print_num(uint64_t number, unsigned int format)
+{
+	if (!(format & FMT_KILOMEGAGIGA)) {
+		printf(FMT("%8llu ","%llu "), (unsigned long long)number);
+		return;
+	}
+	if (number <= 99999) {
+		printf(FMT("%5llu ","%llu "), (unsigned long long)number);
+		return;
+	}
+	number = (number + 500) / 1000;
+	if (number <= 9999) {
+		printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
+		return;
+	}
+	number = (number + 500) / 1000;
+	if (number <= 9999) {
+		printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
+		return;
+	}
+	number = (number + 500) / 1000;
+	if (number <= 9999) {
+		printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
+		return;
+	}
+	number = (number + 500) / 1000;
+	printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
+}
+
 int kernel_version;
 
 void get_kernel_version(void)