Merge remote-tracking branch 'upstream/stable-1.4.20' into update

Conflicts:
	.gitignore
	include/linux/types.h
	libiptc/libiptc.c

Change-Id: I2c949ba9de090db9ae09d914f4ac5c13e5b7d4da
diff --git a/.gitignore b/.gitignore
index 552ec7b..b734ea9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,11 @@
-.*.d
-.*.dd
 *.a
 *.la
 *.lo
-*.oo
 *.so
 *.o
-.deps
+.deps/
 .dirstamp
-.libs
+.libs/
 Makefile
 Makefile.in
 
@@ -18,19 +15,13 @@
 /extensions/matches?.man
 /extensions/targets?.man
 
+/include/xtables-version.h
+/include/iptables/internal.h
 
 /aclocal.m4
-/autom4te*.cache
-/compile
-/config.guess
-/config.h*
-/config.log
-/config.status
-/config.sub
+/autom4te.cache/
+/build-aux/
+/config.*
 /configure
-/depcomp
-/install-sh
 /libtool
-/ltmain.sh
-/missing
 /stamp-h1
diff --git a/INSTALL b/INSTALL
index acb56cd..d62b428 100644
--- a/INSTALL
+++ b/INSTALL
@@ -31,7 +31,7 @@
 --with-xtlibdir=
 
 	The path to where Xtables extensions should be installed to. It
-	defaults to ${prefix}/libexec/xtables.
+	defaults to ${libdir}/xtables.
 
 --enable-devel (or --disable-devel)
 
@@ -70,6 +70,8 @@
 (-O0 is used to turn off instruction reordering, which makes debugging
 much easier.)
 
+To show debug traces you can add -DDEBUG to CFLAGS option
+
 
 Other notes
 ===========
diff --git a/Makefile.am b/Makefile.am
index 34b3501..c38d360 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,16 +3,18 @@
 ACLOCAL_AMFLAGS  = -I m4
 AUTOMAKE_OPTIONS = foreign subdir-objects
 
-SUBDIRS          = extensions libiptc iptables
+SUBDIRS          = libiptc libxtables
 if ENABLE_DEVEL
 SUBDIRS         += include
 endif
 if ENABLE_LIBIPQ
 SUBDIRS         += libipq
 endif
-if HAVE_LIBNFNETLINK
 SUBDIRS         += utils
-endif
+# Depends on libxtables:
+SUBDIRS         += extensions
+# Depends on extensions/libext.a:
+SUBDIRS         += iptables
 
 .PHONY: tarball
 tarball:
@@ -23,4 +25,4 @@
 	rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
 
 config.status: extensions/GNUmakefile.in \
-	include/xtables.h.in include/iptables/internal.h.in
+	include/xtables-version.h.in include/iptables/internal.h.in
diff --git a/autogen.sh b/autogen.sh
index 62a89e1..a0c4395 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh -e
 
 autoreconf -fi;
 rm -Rf autom4te*.cache;
diff --git a/configure.ac b/configure.ac
index e902ab9..f8affed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,10 +1,11 @@
 
-AC_INIT([iptables], [1.4.11.1])
+AC_INIT([iptables], [1.4.20])
 
 # See libtool.info "Libtool's versioning system"
-libxtables_vcurrent=6
+libxtables_vcurrent=10
 libxtables_vage=0
 
+AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_HEADERS([config.h])
 AC_CONFIG_MACRO_DIR([m4])
 AC_PROG_INSTALL
@@ -12,6 +13,7 @@
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_DISABLE_STATIC
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 AM_PROG_LIBTOOL
 
 AC_ARG_WITH([kernel],
@@ -30,7 +32,7 @@
 	AS_HELP_STRING([--with-xtlibdir=PATH],
 	[Path where to install Xtables extensions [[LIBEXECDIR/xtables]]]),
 	[xtlibdir="$withval"],
-	[xtlibdir="${libexecdir}/xtables"])
+	[xtlibdir="${libdir}/xtables"])
 AC_ARG_ENABLE([ipv4],
 	AS_HELP_STRING([--disable-ipv4], [Do not build iptables]),
 	[enable_ipv4="$enableval"], [enable_ipv4="yes"])
@@ -47,7 +49,11 @@
 	[Install Xtables development headers]),
 	[enable_devel="$enableval"], [enable_devel="yes"])
 AC_ARG_ENABLE([libipq],
-	AS_HELP_STRING([--enable-libipq], [Build and install libipq]))
+	AS_HELP_STRING([--enable-libipq], [Build and install libipq]),
+	[enable_libipq="$enableval"], [enable_libipq="no"])
+AC_ARG_ENABLE([bpf-compiler],
+	AS_HELP_STRING([--enable-bpf-compiler], [Build bpf compiler]),
+	[enable_bpfc="yes"], [enable_bpfc="no"])
 AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
 	[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
 	[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
@@ -57,19 +63,36 @@
 	[libiptc_LDFLAGS2="-Wl,--no-as-needed"])
 AC_SUBST([libiptc_LDFLAGS2])
 
+AC_MSG_CHECKING([whether $LD knows -Wl,--no-undefined])
+saved_LDFLAGS="$LDFLAGS";
+LDFLAGS="-Wl,--no-undefined";
+AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void) {}])],
+	[noundef_LDFLAGS="$LDFLAGS"; AC_MSG_RESULT([yes])],
+	[AC_MSG_RESULT([no])]
+)
+LDFLAGS="$saved_LDFLAGS";
+
 blacklist_modules="";
 
-AC_CHECK_HEADER([linux/dccp.h])
+AC_CHECK_HEADERS([linux/dccp.h linux/ip_vs.h linux/magic.h linux/proc_fs.h])
 if test "$ac_cv_header_linux_dccp_h" != "yes"; then
 	blacklist_modules="$blacklist_modules dccp";
 fi;
-
-AC_CHECK_HEADER([linux/ip_vs.h])
 if test "$ac_cv_header_linux_ip_vs_h" != "yes"; then
 	blacklist_modules="$blacklist_modules ipvs";
 fi;
 
+PKG_CHECK_MODULES([libnetfilter_conntrack], [libnetfilter_conntrack >= 1.0.4],
+	[nfconntrack=1], [nfconntrack=0])
+AM_CONDITIONAL([HAVE_LIBNETFILTER_CONNTRACK], [test "$nfconntrack" = 1])
+
+if test "$nfconntrack" -ne 1; then
+	blacklist_modules="$blacklist_modules connlabel";
+	echo "WARNING: libnetfilter_conntrack not found, connlabel match will not be built";
+fi;
+
 AC_SUBST([blacklist_modules])
+AC_CHECK_SIZEOF([struct ip6_hdr], [], [#include <netinet/ip6.h>])
 
 AM_CONDITIONAL([ENABLE_STATIC], [test "$enable_static" = "yes"])
 AM_CONDITIONAL([ENABLE_SHARED], [test "$enable_shared" = "yes"])
@@ -78,6 +101,11 @@
 AM_CONDITIONAL([ENABLE_LARGEFILE], [test "$enable_largefile" = "yes"])
 AM_CONDITIONAL([ENABLE_DEVEL], [test "$enable_devel" = "yes"])
 AM_CONDITIONAL([ENABLE_LIBIPQ], [test "$enable_libipq" = "yes"])
+AM_CONDITIONAL([ENABLE_BPFC], [test "$enable_bpfc" = "yes"])
+
+if test "x$enable_bpfc" = "xyes"; then
+	AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler))
+fi
 
 PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
 	[nfnetlink=1], [nfnetlink=0])
@@ -90,15 +118,30 @@
 	-DXTABLES_LIBDIR=\\\"\${xtlibdir}\\\" -DXTABLES_INTERNAL";
 kinclude_CPPFLAGS="";
 if [[ -n "$kbuilddir" ]]; then
-	kinclude_CPPFLAGS="$kinclude_CPPFLAGS -I$kbuilddir/include";
+	kinclude_CPPFLAGS="$kinclude_CPPFLAGS -I$kbuilddir/include/uapi -I$kbuilddir/include";
 fi;
 if [[ -n "$ksourcedir" ]]; then
-	kinclude_CPPFLAGS="$kinclude_CPPFLAGS -I$ksourcedir/include";
+	kinclude_CPPFLAGS="$kinclude_CPPFLAGS -I$ksourcedir/include/uapi -I$ksourcedir/include";
 fi;
 pkgdatadir='${datadir}/xtables';
 
+define([EXPAND_VARIABLE],
+[$2=[$]$1
+if test $prefix = 'NONE'; then
+	prefix="/usr/local"
+fi
+while true; do
+  case "[$]$2" in
+    *\[$]* ) eval "$2=[$]$2" ;;
+    *) break ;;
+  esac
+done
+eval "$2=[$]$2"
+])dnl EXPAND_VARIABLE
+
 AC_SUBST([regular_CFLAGS])
 AC_SUBST([regular_CPPFLAGS])
+AC_SUBST([noundef_LDFLAGS])
 AC_SUBST([kinclude_CPPFLAGS])
 AC_SUBST([kbuilddir])
 AC_SUBST([ksourcedir])
@@ -112,6 +155,44 @@
 
 AC_CONFIG_FILES([Makefile extensions/GNUmakefile include/Makefile
 	iptables/Makefile iptables/xtables.pc
-	libipq/Makefile libiptc/Makefile libiptc/libiptc.pc utils/Makefile
-	include/xtables.h include/iptables/internal.h])
+	iptables/iptables.8 iptables/ip6tables.8
+	iptables/iptables-extensions.8.tmpl
+	libipq/Makefile libipq/libipq.pc
+	libiptc/Makefile libiptc/libiptc.pc
+	libiptc/libip4tc.pc libiptc/libip6tc.pc
+	libxtables/Makefile utils/Makefile
+	include/xtables-version.h include/iptables/internal.h])
 AC_OUTPUT
+
+
+EXPAND_VARIABLE(xtlibdir, e_xtlibdir)
+EXPAND_VARIABLE(pkgconfigdir, e_pkgconfigdir)
+
+echo "
+Iptables Configuration:
+  IPv4 support:				${enable_ipv4}
+  IPv6 support:				${enable_ipv6}
+  Devel support:			${enable_devel}
+  IPQ support:				${enable_libipq}
+  Large file support:			${enable_largefile}
+  BPF utils support:			${enable_bpfc}
+
+Build parameters:
+  Put plugins into executable (static):	${enable_static}
+  Support plugins via dlopen (shared):	${enable_shared}
+  Installation prefix (--prefix):	${prefix}
+  Xtables extension directory:		${e_xtlibdir}
+  Pkg-config directory:			${e_pkgconfigdir}"
+
+if [[ -n "$ksourcedir" ]]; then
+	echo "  Kernel source directory:		${ksourcedir}"
+fi;
+if [[ -n "$kbuilddir" ]]; then
+	echo "  Kernel build directory:		${kbuilddir}"
+fi;
+
+echo "  Host:					${host}
+  GCC binary:				${CC}"
+
+test x"$blacklist_modules" = "x" || echo "
+Iptables modules that will not be built: $blacklist_modules"
diff --git a/extensions/.gitignore b/extensions/.gitignore
new file mode 100644
index 0000000..b1260f0
--- /dev/null
+++ b/extensions/.gitignore
@@ -0,0 +1,9 @@
+.*.d
+.*.dd
+*.oo
+
+/GNUmakefile
+/initext.c
+/initext?.c
+/matches.man
+/targets.man
diff --git a/extensions/GNUmakefile.in b/extensions/GNUmakefile.in
index fbaf2ec..780e715 100644
--- a/extensions/GNUmakefile.in
+++ b/extensions/GNUmakefile.in
@@ -1,28 +1,29 @@
 # -*- Makefile -*-
 
-top_builddir := @top_builddir@
-builddir     := @builddir@
-top_srcdir  := @top_srcdir@
-srcdir      := @srcdir@
-ksourcedir  := @ksourcedir@
-prefix      := @prefix@
-exec_prefix := @exec_prefix@
-libdir      := @libdir@
-libexecdir  := @libexecdir@
-xtlibdir    := @xtlibdir@
+top_builddir = @top_builddir@
+builddir     = @builddir@
+top_srcdir   = @top_srcdir@
+srcdir       = @srcdir@
+ksourcedir   = @ksourcedir@
+prefix       = @prefix@
+exec_prefix  = @exec_prefix@
+libdir       = @libdir@
+libexecdir   = @libexecdir@
+xtlibdir     = @xtlibdir@
 
-CC             := @CC@
-CCLD           := ${CC}
-CFLAGS         := @CFLAGS@
-CPPFLAGS       := @CPPFLAGS@
-LDFLAGS        := @LDFLAGS@
-regular_CFLAGS := @regular_CFLAGS@
-regular_CPPFLAGS := @regular_CPPFLAGS@
-kinclude_CPPFLAGS := @kinclude_CPPFLAGS@
+CC                 = @CC@
+CCLD               = ${CC}
+CFLAGS             = @CFLAGS@
+CPPFLAGS           = @CPPFLAGS@
+LDFLAGS            = @LDFLAGS@
+regular_CFLAGS     = @regular_CFLAGS@
+regular_CPPFLAGS   = @regular_CPPFLAGS@
+kinclude_CPPFLAGS  = @kinclude_CPPFLAGS@
 
-AM_CFLAGS      := ${regular_CFLAGS}
-AM_CPPFLAGS     = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
+AM_CFLAGS       = ${regular_CFLAGS}
+AM_CPPFLAGS     = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_builddir} -I${top_srcdir}/include ${kinclude_CPPFLAGS} @libnetfilter_conntrack_CFLAGS@
 AM_DEPFLAGS     = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
+AM_LDFLAGS      = @noundef_LDFLAGS@
 
 ifeq (${V},)
 AM_LIBTOOL_SILENT = --silent
@@ -37,16 +38,17 @@
 #
 #	Wildcard module list
 #
-pfx_build_mod := $(patsubst ${srcdir}/libxt_%.c,%,$(wildcard ${srcdir}/libxt_*.c))
-@ENABLE_IPV4_TRUE@ pf4_build_mod := $(patsubst ${srcdir}/libipt_%.c,%,$(wildcard ${srcdir}/libipt_*.c))
-@ENABLE_IPV6_TRUE@ pf6_build_mod := $(patsubst ${srcdir}/libip6t_%.c,%,$(wildcard ${srcdir}/libip6t_*.c))
+pfx_build_mod := $(patsubst ${srcdir}/libxt_%.c,%,$(sort $(wildcard ${srcdir}/libxt_*.c)))
+pfx_symlinks  := NOTRACK state
+@ENABLE_IPV4_TRUE@ pf4_build_mod := $(patsubst ${srcdir}/libipt_%.c,%,$(sort $(wildcard ${srcdir}/libipt_*.c)))
+@ENABLE_IPV6_TRUE@ pf6_build_mod := $(patsubst ${srcdir}/libip6t_%.c,%,$(sort $(wildcard ${srcdir}/libip6t_*.c)))
 pfx_build_mod := $(filter-out @blacklist_modules@,${pfx_build_mod})
 pf4_build_mod := $(filter-out @blacklist_modules@,${pf4_build_mod})
 pf6_build_mod := $(filter-out @blacklist_modules@,${pf6_build_mod})
 pfx_objs      := $(patsubst %,libxt_%.o,${pfx_build_mod})
 pf4_objs      := $(patsubst %,libipt_%.o,${pf4_build_mod})
 pf6_objs      := $(patsubst %,libip6t_%.o,${pf6_build_mod})
-pfx_solibs    := $(patsubst %,libxt_%.so,${pfx_build_mod})
+pfx_solibs    := $(patsubst %,libxt_%.so,${pfx_build_mod} ${pfx_symlinks})
 pf4_solibs    := $(patsubst %,libipt_%.so,${pf4_build_mod})
 pf6_solibs    := $(patsubst %,libip6t_%.so,${pf6_build_mod})
 
@@ -54,9 +56,7 @@
 #
 # Building blocks
 #
-targets := libext.a libext4.a libext6.a \
-           matches4.man matches6.man \
-           targets4.man targets6.man
+targets := libext.a libext4.a libext6.a matches.man targets.man
 targets_install :=
 @ENABLE_STATIC_TRUE@ libext_objs := ${pfx_objs}
 @ENABLE_STATIC_TRUE@ libext4_objs := ${pf4_objs}
@@ -75,10 +75,10 @@
 	if test -n "${targets_install}"; then install -pm0755 $^ "${DESTDIR}${xtlibdir}/"; fi;
 
 clean:
-	rm -f *.o *.oo *.so *.a {matches,targets}[46].man initext.c initext4.c initext6.c;
+	rm -f *.o *.oo *.so *.a {matches,targets}.man initext.c initext4.c initext6.c;
+	rm -f .*.d .*.dd;
 
 distclean: clean
-	rm -f .*.d .*.dd;
 
 init%.o: init%.c
 	${AM_VERBOSE_CC} ${CC} ${AM_CPPFLAGS} ${AM_DEPFLAGS} ${AM_CFLAGS} -D_INIT=$*_init ${CFLAGS} -o $@ -c $<;
@@ -90,11 +90,20 @@
 #	Shared libraries
 #
 lib%.so: lib%.oo
-	${AM_VERBOSE_CCLD} ${CCLD} ${AM_LDFLAGS} -shared ${LDFLAGS} -o $@ $<;
+	${AM_VERBOSE_CCLD} ${CCLD} ${AM_LDFLAGS} -shared ${LDFLAGS} -o $@ $< -L../libxtables/.libs -lxtables ${$*_LIBADD};
 
 lib%.oo: ${srcdir}/lib%.c
 	${AM_VERBOSE_CC} ${CC} ${AM_CPPFLAGS} ${AM_DEPFLAGS} ${AM_CFLAGS} -D_INIT=lib$*_init -DPIC -fPIC ${CFLAGS} -o $@ -c $<;
 
+libxt_NOTRACK.so: libxt_CT.so
+	ln -fs $< $@
+libxt_state.so: libxt_conntrack.so
+	ln -fs $< $@
+
+# Need the LIBADDs in iptables/Makefile.am too for libxtables_la_LIBADD
+xt_RATEEST_LIBADD   = -lm
+xt_statistic_LIBADD = -lm
+xt_connlabel_LIBADD = @libnetfilter_conntrack_LIBS@
 
 #
 #	Static bits
@@ -185,36 +194,33 @@
 #
 #	Manual pages
 #
-ex_matches = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:lower:][:digit:]_]+\b'))
-ex_targets = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:upper:][:digit:]_]+\b'))
+ex_matches = $(shell echo ${1} | LC_ALL=POSIX grep -Eo '\b[[:lower:][:digit:]_]+\b')
+ex_targets = $(shell echo ${1} | LC_ALL=POSIX grep -Eo '\b[[:upper:][:digit:]_]+\b')
 man_run    = \
 	${AM_VERBOSE_GEN} \
-	for ext in $(1); do \
+	for ext in $(sort ${1}); do \
 		f="${srcdir}/libxt_$$ext.man"; \
-		cf="${srcdir}/libxt_$$ext.c"; \
-		if [ -f "$$f" ] && grep -Eq "$(3)|NFPROTO_UNSPEC" "$$cf"; then \
-			echo -e "\t+ $$f" >&2; \
-			echo ".SS $$ext"; \
-			cat "$$f" || exit $$?; \
-			continue; \
-		fi; \
-		f="${srcdir}/lib$(2)t_$$ext.man"; \
 		if [ -f "$$f" ]; then \
 			echo -e "\t+ $$f" >&2; \
 			echo ".SS $$ext"; \
 			cat "$$f" || exit $$?; \
-			continue; \
+		fi; \
+		f="${srcdir}/libip6t_$$ext.man"; \
+		if [ -f "$$f" ]; then \
+			echo -e "\t+ $$f" >&2; \
+			echo ".SS $$ext (IPv6-specific)"; \
+			cat "$$f" || exit $$?; \
+		fi; \
+		f="${srcdir}/libipt_$$ext.man"; \
+		if [ -f "$$f" ]; then \
+			echo -e "\t+ $$f" >&2; \
+			echo ".SS $$ext (IPv4-specific)"; \
+			cat "$$f" || exit $$?; \
 		fi; \
 	done >$@;
 
-matches4.man: .initext.dd .initext4.dd $(wildcard ${srcdir}/lib*.man)
-	$(call man_run,$(call ex_matches,${pfx_build_mod} ${pf4_build_mod}),ip,NFPROTO_IPV4)
+matches.man: .initext.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
+	$(call man_run,$(call ex_matches,${pfx_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
 
-matches6.man: .initext.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
-	$(call man_run,$(call ex_matches,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6)
-
-targets4.man: .initext.dd .initext4.dd $(wildcard ${srcdir}/lib*.man)
-	$(call man_run,$(call ex_targets,${pfx_build_mod} ${pf4_build_mod}),ip,NFPROTO_IPV4)
-
-targets6.man: .initext.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
-	$(call man_run,$(call ex_targets,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6)
+targets.man: .initext.dd .initext4.dd .initext6.dd $(wildcard ${srcdir}/lib*.man)
+	$(call man_run,$(call ex_targets,${pfx_build_mod} ${pf4_build_mod} ${pf6_build_mod} ${pfx_symlinks}))
diff --git a/extensions/libip6t_DNAT.c b/extensions/libip6t_DNAT.c
new file mode 100644
index 0000000..eaa6bf1
--- /dev/null
+++ b/extensions/libip6t_DNAT.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Rusty Russell's IPv4 DNAT target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <iptables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+enum {
+	O_TO_DEST = 0,
+	O_RANDOM,
+	O_PERSISTENT,
+	O_X_TO_DEST,
+	F_TO_DEST   = 1 << O_TO_DEST,
+	F_RANDOM   = 1 << O_RANDOM,
+	F_X_TO_DEST = 1 << O_X_TO_DEST,
+};
+
+static void DNAT_help(void)
+{
+	printf(
+"DNAT target options:\n"
+" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
+"				Address to map destination to.\n"
+"[--random] [--persistent]\n");
+}
+
+static const struct xt_option_entry DNAT_opts[] = {
+	{.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_MULTI},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+/* Ranges expected in network order. */
+static void
+parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
+{
+	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
+	const struct in6_addr *ip;
+
+	arg = strdup(orig_arg);
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+
+	start = strchr(arg, '[');
+	if (start == NULL) {
+		start = arg;
+		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
+		colon = strchr(arg, ':');
+		if (colon && strchr(colon+1, ':'))
+			colon = NULL;
+	}
+	else {
+		start++;
+		end = strchr(start, ']');
+		if (end == NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Invalid address format");
+
+		*end = '\0';
+		colon = strchr(end + 1, ':');
+	}
+
+	if (colon) {
+		int port;
+
+		if (!portok)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Need TCP, UDP, SCTP or DCCP with port specification");
+
+		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+		port = atoi(colon+1);
+		if (port <= 0 || port > 65535)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Port `%s' not valid\n", colon+1);
+
+		error = strchr(colon+1, ':');
+		if (error)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid port:port syntax - use dash\n");
+
+		dash = strchr(colon, '-');
+		if (!dash) {
+			range->min_proto.tcp.port
+				= range->max_proto.tcp.port
+				= htons(port);
+		} else {
+			int maxport;
+
+			maxport = atoi(dash + 1);
+			if (maxport <= 0 || maxport > 65535)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Port `%s' not valid\n", dash+1);
+			if (maxport < port)
+				/* People are stupid. */
+				xtables_error(PARAMETER_PROBLEM,
+					   "Port range `%s' funky\n", colon+1);
+			range->min_proto.tcp.port = htons(port);
+			range->max_proto.tcp.port = htons(maxport);
+		}
+		/* Starts with colon or [] colon? No IP info...*/
+		if (colon == arg || colon == arg+2) {
+			free(arg);
+			return;
+		}
+		*colon = '\0';
+	}
+
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
+	dash = strchr(start, '-');
+	if (colon && dash && dash > colon)
+		dash = NULL;
+
+	if (dash)
+		*dash = '\0';
+
+	ip = xtables_numeric_to_ip6addr(start);
+	if (!ip)
+		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+			      start);
+	range->min_addr.in6 = *ip;
+	if (dash) {
+		ip = xtables_numeric_to_ip6addr(dash + 1);
+		if (!ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+				      dash+1);
+		range->max_addr.in6 = *ip;
+	} else
+		range->max_addr = range->min_addr;
+
+	free(arg);
+	return;
+}
+
+static void DNAT_parse(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	struct nf_nat_range *range = cb->data;
+	int portok;
+
+	if (entry->ipv6.proto == IPPROTO_TCP ||
+	    entry->ipv6.proto == IPPROTO_UDP ||
+	    entry->ipv6.proto == IPPROTO_SCTP ||
+	    entry->ipv6.proto == IPPROTO_DCCP ||
+	    entry->ipv6.proto == IPPROTO_ICMP)
+		portok = 1;
+	else
+		portok = 0;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_DEST:
+		if (cb->xflags & F_X_TO_DEST) {
+			if (!kernel_version)
+				get_kernel_version();
+			if (kernel_version > LINUX_VERSION(2, 6, 10))
+				xtables_error(PARAMETER_PROBLEM,
+					   "DNAT: Multiple --to-destination not supported");
+		}
+		parse_to(cb->arg, portok, range);
+		break;
+	case O_PERSISTENT:
+		range->flags |= NF_NAT_RANGE_PERSISTENT;
+		break;
+	}
+}
+
+static void DNAT_fcheck(struct xt_fcheck_call *cb)
+{
+	static const unsigned int f = F_TO_DEST | F_RANDOM;
+	struct nf_nat_range *mr = cb->data;
+
+	if ((cb->xflags & f) == f)
+		mr->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
+static void print_range(const struct nf_nat_range *range)
+{
+	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
+		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
+			printf("[");
+		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
+		if (memcmp(&range->min_addr, &range->max_addr,
+			   sizeof(range->min_addr)))
+			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
+		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
+			printf("]");
+	}
+	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(":");
+		printf("%hu", ntohs(range->min_proto.tcp.port));
+		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+			printf("-%hu", ntohs(range->max_proto.tcp.port));
+	}
+}
+
+static void DNAT_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	printf(" to:");
+	print_range(range);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" random");
+	if (range->flags & NF_NAT_RANGE_PERSISTENT)
+		printf(" persistent");
+}
+
+static void DNAT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	printf(" --to-destination ");
+	print_range(range);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" --random");
+	if (range->flags & NF_NAT_RANGE_PERSISTENT)
+		printf(" --persistent");
+}
+
+static struct xtables_target snat_tg_reg = {
+	.name		= "DNAT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.revision	= 1,
+	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.help		= DNAT_help,
+	.x6_parse	= DNAT_parse,
+	.x6_fcheck	= DNAT_fcheck,
+	.print		= DNAT_print,
+	.save		= DNAT_save,
+	.x6_options	= DNAT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&snat_tg_reg);
+}
diff --git a/extensions/libip6t_DNPT.c b/extensions/libip6t_DNPT.c
new file mode 100644
index 0000000..a442de6
--- /dev/null
+++ b/extensions/libip6t_DNPT.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_NPT.h>
+
+enum {
+	O_SRC_PFX	= 1 << 0,
+	O_DST_PFX	= 1 << 1,
+};
+
+static const struct xt_option_entry DNPT_options[] = {
+	{ .name = "src-pfx", .id = O_SRC_PFX, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_MAND },
+	{ .name = "dst-pfx", .id = O_DST_PFX, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_MAND },
+	{ }
+};
+
+static void DNPT_help(void)
+{
+	printf("DNPT target options:"
+	       "\n"
+	       " --src-pfx prefix/length\n"
+	       " --dst-pfx prefix/length\n"
+	       "\n");
+}
+
+static void DNPT_parse(struct xt_option_call *cb)
+{
+	struct ip6t_npt_tginfo *npt = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SRC_PFX:
+		npt->src_pfx = cb->val.haddr;
+		npt->src_pfx_len = cb->val.hlen;
+		break;
+	case O_DST_PFX:
+		npt->dst_pfx = cb->val.haddr;
+		npt->dst_pfx_len = cb->val.hlen;
+		break;
+	}
+}
+
+static void DNPT_print(const void *ip, const struct xt_entry_target *target,
+		       int numeric)
+{
+	const struct ip6t_npt_tginfo *npt = (const void *)target->data;
+
+	printf("src-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
+				 npt->src_pfx_len);
+	printf("dst-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
+				 npt->dst_pfx_len);
+}
+
+static void DNPT_save(const void *ip, const struct xt_entry_target *target)
+{
+	static const struct in6_addr zero_addr;
+	const struct ip6t_npt_tginfo *info = (const void *)target->data;
+
+	if (memcmp(&info->src_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
+	    info->src_pfx_len != 0)
+		printf("--src-pfx %s/%u ",
+		       xtables_ip6addr_to_numeric(&info->src_pfx.in6),
+		       info->src_pfx_len);
+	if (memcmp(&info->dst_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
+	    info->dst_pfx_len != 0)
+		printf("--dst-pfx %s/%u ",
+		       xtables_ip6addr_to_numeric(&info->dst_pfx.in6),
+		       info->dst_pfx_len);
+}
+
+static struct xtables_target snpt_tg_reg = {
+	.name		= "DNPT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_npt_tginfo)),
+	.userspacesize	= offsetof(struct ip6t_npt_tginfo, adjustment),
+	.help		= DNPT_help,
+	.x6_parse	= DNPT_parse,
+	.print		= DNPT_print,
+	.save		= DNPT_save,
+	.x6_options	= DNPT_options,
+};
+
+void _init(void)
+{
+	xtables_register_target(&snpt_tg_reg);
+}
diff --git a/extensions/libip6t_DNPT.man b/extensions/libip6t_DNPT.man
new file mode 100644
index 0000000..61beeee
--- /dev/null
+++ b/extensions/libip6t_DNPT.man
@@ -0,0 +1,30 @@
+Provides stateless destination IPv6-to-IPv6 Network Prefix Translation (as
+described by RFC 6296).
+.PP
+You have to use this target in the
+.B mangle
+table, not in the
+.B nat
+table. It takes the following options:
+.TP
+\fB\-\-src\-pfx\fP [\fIprefix/\fP\fIlength]
+Set source prefix that you want to translate and length
+.TP
+\fB\-\-dst\-pfx\fP [\fIprefix/\fP\fIlength]
+Set destination prefix that you want to use in the translation and length
+.PP
+You have to use the SNPT target to undo the translation. Example:
+.IP
+ip6tables \-t mangle \-I POSTROUTING \-s fd00::/64 \! \-o vboxnet0
+\-j SNPT \-\-src-pfx fd00::/64 \-\-dst-pfx 2001:e20:2000:40f::/64
+.IP
+ip6tables \-t mangle \-I PREROUTING \-i wlan0 \-d 2001:e20:2000:40f::/64
+\-j DNPT \-\-src-pfx 2001:e20:2000:40f::/64 \-\-dst-pfx fd00::/64
+.PP
+You may need to enable IPv6 neighbor proxy:
+.IP
+sysctl -w net.ipv6.conf.all.proxy_ndp=1
+.PP
+You also have to use the
+.B NOTRACK
+target to disable connection tracking for translated flows.
diff --git a/extensions/libip6t_HL.c b/extensions/libip6t_HL.c
index 254b191..52ca5d3 100644
--- a/extensions/libip6t_HL.c
+++ b/extensions/libip6t_HL.c
@@ -20,12 +20,12 @@
 
 #define s struct ip6t_HL_info
 static const struct xt_option_entry HL_opts[] = {
-	{.name = "ttl-set", .type = XTTYPE_UINT8, .id = O_HL_SET,
+	{.name = "hl-set", .type = XTTYPE_UINT8, .id = O_HL_SET,
 	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
-	{.name = "ttl-dec", .type = XTTYPE_UINT8, .id = O_HL_DEC,
+	{.name = "hl-dec", .type = XTTYPE_UINT8, .id = O_HL_DEC,
 	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit),
 	 .min = 1},
-	{.name = "ttl-inc", .type = XTTYPE_UINT8, .id = O_HL_INC,
+	{.name = "hl-inc", .type = XTTYPE_UINT8, .id = O_HL_INC,
 	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit),
 	 .min = 1},
 	XTOPT_TABLEEND,
diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c
index a419ec9..4639268 100644
--- a/extensions/libip6t_LOG.c
+++ b/extensions/libip6t_LOG.c
@@ -87,19 +87,19 @@
 				   "Newlines not allowed in --log-prefix");
 		break;
 	case O_LOG_TCPSEQ:
-		info->logflags = IP6T_LOG_TCPSEQ;
+		info->logflags |= IP6T_LOG_TCPSEQ;
 		break;
 	case O_LOG_TCPOPTS:
-		info->logflags = IP6T_LOG_TCPOPT;
+		info->logflags |= IP6T_LOG_TCPOPT;
 		break;
 	case O_LOG_IPOPTS:
-		info->logflags = IP6T_LOG_IPOPT;
+		info->logflags |= IP6T_LOG_IPOPT;
 		break;
 	case O_LOG_UID:
-		info->logflags = IP6T_LOG_UID;
+		info->logflags |= IP6T_LOG_UID;
 		break;
 	case O_LOG_MAC:
-		info->logflags = IP6T_LOG_MACDECODE;
+		info->logflags |= IP6T_LOG_MACDECODE;
 		break;
 	}
 }
@@ -146,8 +146,10 @@
 	const struct ip6t_log_info *loginfo
 		= (const struct ip6t_log_info *)target->data;
 
-	if (strcmp(loginfo->prefix, "") != 0)
-		printf(" --log-prefix \"%s\"", loginfo->prefix);
+	if (strcmp(loginfo->prefix, "") != 0) {
+		printf(" --log-prefix");
+		xtables_save_string(loginfo->prefix);
+	}
 
 	if (loginfo->level != LOG_DEFAULT_LEVEL)
 		printf(" --log-level %d", loginfo->level);
diff --git a/extensions/libip6t_MASQUERADE.c b/extensions/libip6t_MASQUERADE.c
new file mode 100644
index 0000000..eb9213e
--- /dev/null
+++ b/extensions/libip6t_MASQUERADE.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Rusty Russell's IPv4 MASQUERADE target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+enum {
+	O_TO_PORTS = 0,
+	O_RANDOM,
+};
+
+static void MASQUERADE_help(void)
+{
+	printf(
+"MASQUERADE target options:\n"
+" --to-ports <port>[-<port>]\n"
+"				Port (range) to map to.\n"
+" --random\n"
+"				Randomize source port.\n");
+}
+
+static const struct xt_option_entry MASQUERADE_opts[] = {
+	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct nf_nat_range *r)
+{
+	char *end;
+	unsigned int port, maxport;
+
+	r->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
+		xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
+
+	switch (*end) {
+	case '\0':
+		r->min_proto.tcp.port
+			= r->max_proto.tcp.port
+			= htons(port);
+		return;
+	case '-':
+		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX))
+			break;
+
+		if (maxport < port)
+			break;
+
+		r->min_proto.tcp.port = htons(port);
+		r->max_proto.tcp.port = htons(maxport);
+		return;
+	default:
+		break;
+	}
+	xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
+}
+
+static void MASQUERADE_parse(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	struct nf_nat_range *r = cb->data;
+	int portok;
+
+	if (entry->ipv6.proto == IPPROTO_TCP ||
+	    entry->ipv6.proto == IPPROTO_UDP ||
+	    entry->ipv6.proto == IPPROTO_SCTP ||
+	    entry->ipv6.proto == IPPROTO_DCCP ||
+	    entry->ipv6.proto == IPPROTO_ICMP)
+		portok = 1;
+	else
+		portok = 0;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_PORTS:
+		if (!portok)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Need TCP, UDP, SCTP or DCCP with port specification");
+		parse_ports(cb->arg, r);
+		break;
+	case O_RANDOM:
+		r->flags |=  NF_NAT_RANGE_PROTO_RANDOM;
+		break;
+	}
+}
+
+static void
+MASQUERADE_print(const void *ip, const struct xt_entry_target *target,
+                 int numeric)
+{
+	const struct nf_nat_range *r = (const void *)target->data;
+
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(" masq ports: ");
+		printf("%hu", ntohs(r->min_proto.tcp.port));
+		if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+			printf("-%hu", ntohs(r->max_proto.tcp.port));
+	}
+
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" random");
+}
+
+static void
+MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct nf_nat_range *r = (const void *)target->data;
+
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(" --to-ports %hu", ntohs(r->min_proto.tcp.port));
+		if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+			printf("-%hu", ntohs(r->max_proto.tcp.port));
+	}
+
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" --random");
+}
+
+static struct xtables_target masquerade_tg_reg = {
+	.name		= "MASQUERADE",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.help		= MASQUERADE_help,
+	.x6_parse	= MASQUERADE_parse,
+	.print		= MASQUERADE_print,
+	.save		= MASQUERADE_save,
+	.x6_options	= MASQUERADE_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&masquerade_tg_reg);
+}
diff --git a/extensions/libip6t_NETMAP.c b/extensions/libip6t_NETMAP.c
new file mode 100644
index 0000000..a4df70e
--- /dev/null
+++ b/extensions/libip6t_NETMAP.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Svenning Soerensen's IPv4 NETMAP target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <xtables.h>
+#include <libiptc/libip6tc.h>
+#include <linux/netfilter/nf_nat.h>
+
+#define MODULENAME "NETMAP"
+
+enum {
+	O_TO = 0,
+};
+
+static const struct xt_option_entry NETMAP_opts[] = {
+	{.name = "to", .id = O_TO, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_MAND},
+	XTOPT_TABLEEND,
+};
+
+static void NETMAP_help(void)
+{
+	printf(MODULENAME" target options:\n"
+	       "  --%s address[/mask]\n"
+	       "				Network address to map to.\n\n",
+	       NETMAP_opts[0].name);
+}
+
+static void NETMAP_parse(struct xt_option_call *cb)
+{
+	struct nf_nat_range *range = cb->data;
+	unsigned int i;
+
+	xtables_option_parse(cb);
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
+	for (i = 0; i < 4; i++) {
+		range->min_addr.ip6[i] = cb->val.haddr.ip6[i] &
+					 cb->val.hmask.ip6[i];
+		range->max_addr.ip6[i] = range->min_addr.ip6[i] |
+					 ~cb->val.hmask.ip6[i];
+	}
+}
+
+static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct nf_nat_range *r = (const void *)target->data;
+	struct in6_addr a;
+	unsigned int i;
+	int bits;
+
+	a = r->min_addr.in6;
+	printf("%s", xtables_ip6addr_to_numeric(&a));
+	for (i = 0; i < 4; i++)
+		a.s6_addr32[i] = ~(r->min_addr.ip6[i] ^ r->max_addr.ip6[i]);
+	bits = xtables_ip6mask_to_cidr(&a);
+	if (bits < 0)
+		printf("/%s", xtables_ip6addr_to_numeric(&a));
+	else
+		printf("/%d", bits);
+}
+
+static void NETMAP_save(const void *ip, const struct xt_entry_target *target)
+{
+	printf(" --%s ", NETMAP_opts[0].name);
+	NETMAP_print(ip, target, 0);
+}
+
+static struct xtables_target netmap_tg_reg = {
+	.name		= MODULENAME,
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.help		= NETMAP_help,
+	.x6_parse	= NETMAP_parse,
+	.print		= NETMAP_print,
+	.save		= NETMAP_save,
+	.x6_options	= NETMAP_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&netmap_tg_reg);
+}
diff --git a/extensions/libip6t_REDIRECT.c b/extensions/libip6t_REDIRECT.c
new file mode 100644
index 0000000..1724aa6
--- /dev/null
+++ b/extensions/libip6t_REDIRECT.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+enum {
+	O_TO_PORTS = 0,
+	O_RANDOM,
+	F_TO_PORTS = 1 << O_TO_PORTS,
+	F_RANDOM   = 1 << O_RANDOM,
+};
+
+static void REDIRECT_help(void)
+{
+	printf(
+"REDIRECT target options:\n"
+" --to-ports <port>[-<port>]\n"
+"				Port (range) to map to.\n"
+" [--random]\n");
+}
+
+static const struct xt_option_entry REDIRECT_opts[] = {
+	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct nf_nat_range *range)
+{
+	char *end = "";
+	unsigned int port, maxport;
+
+	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
+	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
+		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
+
+	switch (*end) {
+	case '\0':
+		range->min_proto.tcp.port
+			= range->max_proto.tcp.port
+			= htons(port);
+		return;
+	case '-':
+		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
+		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
+			break;
+
+		if (maxport < port)
+			break;
+
+		range->min_proto.tcp.port = htons(port);
+		range->max_proto.tcp.port = htons(maxport);
+		return;
+	default:
+		break;
+	}
+	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
+}
+
+static void REDIRECT_parse(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	struct nf_nat_range *range = (void *)(*cb->target)->data;
+	int portok;
+
+	if (entry->ipv6.proto == IPPROTO_TCP
+	    || entry->ipv6.proto == IPPROTO_UDP
+	    || entry->ipv6.proto == IPPROTO_SCTP
+	    || entry->ipv6.proto == IPPROTO_DCCP
+	    || entry->ipv6.proto == IPPROTO_ICMP)
+		portok = 1;
+	else
+		portok = 0;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_PORTS:
+		if (!portok)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Need TCP, UDP, SCTP or DCCP with port specification");
+		parse_ports(cb->arg, range);
+		if (cb->xflags & F_RANDOM)
+			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+		break;
+	case O_RANDOM:
+		if (cb->xflags & F_TO_PORTS)
+			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+		break;
+	}
+}
+
+static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
+                           int numeric)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(" redir ports ");
+		printf("%hu", ntohs(range->min_proto.tcp.port));
+		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+			printf("-%hu", ntohs(range->max_proto.tcp.port));
+		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" random");
+	}
+}
+
+static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(" --to-ports ");
+		printf("%hu", ntohs(range->min_proto.tcp.port));
+		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+			printf("-%hu", ntohs(range->max_proto.tcp.port));
+		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" --random");
+	}
+}
+
+static struct xtables_target redirect_tg_reg = {
+	.name		= "REDIRECT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.help		= REDIRECT_help,
+	.x6_parse	= REDIRECT_parse,
+	.print		= REDIRECT_print,
+	.save		= REDIRECT_save,
+	.x6_options	= REDIRECT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&redirect_tg_reg);
+}
diff --git a/extensions/libip6t_SNAT.c b/extensions/libip6t_SNAT.c
new file mode 100644
index 0000000..7382ad0
--- /dev/null
+++ b/extensions/libip6t_SNAT.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * Based on Rusty Russell's IPv4 SNAT target. Development of IPv6 NAT
+ * funded by Astaro.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <iptables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+enum {
+	O_TO_SRC = 0,
+	O_RANDOM,
+	O_PERSISTENT,
+	O_X_TO_SRC,
+	F_TO_SRC   = 1 << O_TO_SRC,
+	F_RANDOM   = 1 << O_RANDOM,
+	F_X_TO_SRC = 1 << O_X_TO_SRC,
+};
+
+static void SNAT_help(void)
+{
+	printf(
+"SNAT target options:\n"
+" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
+"				Address to map source to.\n"
+"[--random] [--persistent]\n");
+}
+
+static const struct xt_option_entry SNAT_opts[] = {
+	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_MULTI},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+/* Ranges expected in network order. */
+static void
+parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
+{
+	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
+	const struct in6_addr *ip;
+
+	arg = strdup(orig_arg);
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+
+	start = strchr(arg, '[');
+	if (start == NULL) {
+		start = arg;
+		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
+		colon = strchr(arg, ':');
+		if (colon && strchr(colon+1, ':'))
+			colon = NULL;
+	}
+	else {
+		start++;
+		end = strchr(start, ']');
+		if (end == NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Invalid address format");
+
+		*end = '\0';
+		colon = strchr(end + 1, ':');
+	}
+
+	if (colon) {
+		int port;
+
+		if (!portok)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Need TCP, UDP, SCTP or DCCP with port specification");
+
+		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+
+		port = atoi(colon+1);
+		if (port <= 0 || port > 65535)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Port `%s' not valid\n", colon+1);
+
+		error = strchr(colon+1, ':');
+		if (error)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid port:port syntax - use dash\n");
+
+		dash = strchr(colon, '-');
+		if (!dash) {
+			range->min_proto.tcp.port
+				= range->max_proto.tcp.port
+				= htons(port);
+		} else {
+			int maxport;
+
+			maxport = atoi(dash + 1);
+			if (maxport <= 0 || maxport > 65535)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Port `%s' not valid\n", dash+1);
+			if (maxport < port)
+				/* People are stupid. */
+				xtables_error(PARAMETER_PROBLEM,
+					   "Port range `%s' funky\n", colon+1);
+			range->min_proto.tcp.port = htons(port);
+			range->max_proto.tcp.port = htons(maxport);
+		}
+		/* Starts with colon or [] colon? No IP info...*/
+		if (colon == arg || colon == arg+2) {
+			free(arg);
+			return;
+		}
+		*colon = '\0';
+	}
+
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
+	dash = strchr(start, '-');
+	if (colon && dash && dash > colon)
+		dash = NULL;
+
+	if (dash)
+		*dash = '\0';
+
+	ip = xtables_numeric_to_ip6addr(start);
+	if (!ip)
+		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+			      start);
+	range->min_addr.in6 = *ip;
+	if (dash) {
+		ip = xtables_numeric_to_ip6addr(dash + 1);
+		if (!ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+				      dash+1);
+		range->max_addr.in6 = *ip;
+	} else
+		range->max_addr = range->min_addr;
+
+	free(arg);
+	return;
+}
+
+static void SNAT_parse(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	struct nf_nat_range *range = cb->data;
+	int portok;
+
+	if (entry->ipv6.proto == IPPROTO_TCP ||
+	    entry->ipv6.proto == IPPROTO_UDP ||
+	    entry->ipv6.proto == IPPROTO_SCTP ||
+	    entry->ipv6.proto == IPPROTO_DCCP ||
+	    entry->ipv6.proto == IPPROTO_ICMP)
+		portok = 1;
+	else
+		portok = 0;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_SRC:
+		if (cb->xflags & F_X_TO_SRC) {
+			if (!kernel_version)
+				get_kernel_version();
+			if (kernel_version > LINUX_VERSION(2, 6, 10))
+				xtables_error(PARAMETER_PROBLEM,
+					   "SNAT: Multiple --to-source not supported");
+		}
+		parse_to(cb->arg, portok, range);
+		break;
+	case O_PERSISTENT:
+		range->flags |= NF_NAT_RANGE_PERSISTENT;
+		break;
+	}
+}
+
+static void SNAT_fcheck(struct xt_fcheck_call *cb)
+{
+	static const unsigned int f = F_TO_SRC | F_RANDOM;
+	struct nf_nat_range *range = cb->data;
+
+	if ((cb->xflags & f) == f)
+		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
+static void print_range(const struct nf_nat_range *range)
+{
+	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
+		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
+			printf("[");
+		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
+		if (memcmp(&range->min_addr, &range->max_addr,
+			   sizeof(range->min_addr)))
+			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
+		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
+			printf("]");
+	}
+	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
+		printf(":");
+		printf("%hu", ntohs(range->min_proto.tcp.port));
+		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
+			printf("-%hu", ntohs(range->max_proto.tcp.port));
+	}
+}
+
+static void SNAT_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	printf(" to:");
+	print_range(range);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" random");
+	if (range->flags & NF_NAT_RANGE_PERSISTENT)
+		printf(" persistent");
+}
+
+static void SNAT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct nf_nat_range *range = (const void *)target->data;
+
+	printf(" --to-source ");
+	print_range(range);
+	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" --random");
+	if (range->flags & NF_NAT_RANGE_PERSISTENT)
+		printf(" --persistent");
+}
+
+static struct xtables_target snat_tg_reg = {
+	.name		= "SNAT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.revision	= 1,
+	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
+	.help		= SNAT_help,
+	.x6_parse	= SNAT_parse,
+	.x6_fcheck	= SNAT_fcheck,
+	.print		= SNAT_print,
+	.save		= SNAT_save,
+	.x6_options	= SNAT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&snat_tg_reg);
+}
diff --git a/extensions/libip6t_SNPT.c b/extensions/libip6t_SNPT.c
new file mode 100644
index 0000000..4f10de0
--- /dev/null
+++ b/extensions/libip6t_SNPT.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_NPT.h>
+
+enum {
+	O_SRC_PFX	= 1 << 0,
+	O_DST_PFX	= 1 << 1,
+};
+
+static const struct xt_option_entry SNPT_options[] = {
+	{ .name = "src-pfx", .id = O_SRC_PFX, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_MAND },
+	{ .name = "dst-pfx", .id = O_DST_PFX, .type = XTTYPE_HOSTMASK,
+	  .flags = XTOPT_MAND },
+	{ }
+};
+
+static void SNPT_help(void)
+{
+	printf("SNPT target options:"
+	       "\n"
+	       " --src-pfx prefix/length\n"
+	       " --dst-pfx prefix/length\n"
+	       "\n");
+}
+
+static void SNPT_parse(struct xt_option_call *cb)
+{
+	struct ip6t_npt_tginfo *npt = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SRC_PFX:
+		npt->src_pfx = cb->val.haddr;
+		npt->src_pfx_len = cb->val.hlen;
+		break;
+	case O_DST_PFX:
+		npt->dst_pfx = cb->val.haddr;
+		npt->dst_pfx_len = cb->val.hlen;
+		break;
+	}
+}
+
+static void SNPT_print(const void *ip, const struct xt_entry_target *target,
+		       int numeric)
+{
+	const struct ip6t_npt_tginfo *npt = (const void *)target->data;
+
+	printf("src-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->src_pfx.in6),
+				 npt->src_pfx_len);
+	printf("dst-pfx %s/%u ", xtables_ip6addr_to_numeric(&npt->dst_pfx.in6),
+				 npt->dst_pfx_len);
+}
+
+static void SNPT_save(const void *ip, const struct xt_entry_target *target)
+{
+	static const struct in6_addr zero_addr;
+	const struct ip6t_npt_tginfo *info = (const void *)target->data;
+
+	if (memcmp(&info->src_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
+	    info->src_pfx_len != 0)
+		printf("--src-pfx %s/%u ",
+		       xtables_ip6addr_to_numeric(&info->src_pfx.in6),
+		       info->src_pfx_len);
+	if (memcmp(&info->dst_pfx.in6, &zero_addr, sizeof(zero_addr)) != 0 ||
+	    info->dst_pfx_len != 0)
+		printf("--dst-pfx %s/%u ",
+		       xtables_ip6addr_to_numeric(&info->dst_pfx.in6),
+		       info->dst_pfx_len);
+}
+
+static struct xtables_target snpt_tg_reg = {
+	.name		= "SNPT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_npt_tginfo)),
+	.userspacesize	= offsetof(struct ip6t_npt_tginfo, adjustment),
+	.help		= SNPT_help,
+	.x6_parse	= SNPT_parse,
+	.print		= SNPT_print,
+	.save		= SNPT_save,
+	.x6_options	= SNPT_options,
+};
+
+void _init(void)
+{
+	xtables_register_target(&snpt_tg_reg);
+}
diff --git a/extensions/libip6t_SNPT.man b/extensions/libip6t_SNPT.man
new file mode 100644
index 0000000..78d644a
--- /dev/null
+++ b/extensions/libip6t_SNPT.man
@@ -0,0 +1,30 @@
+Provides stateless source IPv6-to-IPv6 Network Prefix Translation (as described
+by RFC 6296).
+.PP
+You have to use this target in the
+.B mangle
+table, not in the
+.B nat
+table. It takes the following options:
+.TP
+\fB\-\-src\-pfx\fP [\fIprefix/\fP\fIlength]
+Set source prefix that you want to translate and length
+.TP
+\fB\-\-dst\-pfx\fP [\fIprefix/\fP\fIlength]
+Set destination prefix that you want to use in the translation and length
+.PP
+You have to use the DNPT target to undo the translation. Example:
+.IP
+ip6tables \-t mangle \-I POSTROUTING \-s fd00::/64 \! \-o vboxnet0
+\-j SNPT \-\-src-pfx fd00::/64 \-\-dst-pfx 2001:e20:2000:40f::/64
+.IP
+ip6tables \-t mangle \-I PREROUTING \-i wlan0 \-d 2001:e20:2000:40f::/64
+\-j DNPT \-\-src-pfx 2001:e20:2000:40f::/64 \-\-dst-pfx fd00::/64
+.PP
+You may need to enable IPv6 neighbor proxy:
+.IP
+sysctl -w net.ipv6.conf.all.proxy_ndp=1
+.PP
+You also have to use the
+.B NOTRACK
+target to disable connection tracking for translated flows.
diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c
index 4125bd3..3fd4c01 100644
--- a/extensions/libip6t_dst.c
+++ b/extensions/libip6t_dst.c
@@ -111,6 +111,9 @@
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
+	case O_DSTLEN:
+		optinfo->flags |= IP6T_OPTS_LEN;
+		break;
 	case O_DSTOPTS:
 		optinfo->optsnr = parse_options(cb->arg, optinfo->opts);
 		optinfo->flags |= IP6T_OPTS_OPTS;
diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c
index 4779386..023df62 100644
--- a/extensions/libip6t_frag.c
+++ b/extensions/libip6t_frag.c
@@ -41,6 +41,13 @@
 };
 #undef s
 
+static void frag_init(struct xt_entry_match *m)
+{
+	struct ip6t_frag *fraginfo = (void *)m->data;
+
+	fraginfo->ids[1] = ~0U;
+}
+
 static void frag_parse(struct xt_option_call *cb)
 {
 	struct ip6t_frag *fraginfo = cb->data;
@@ -50,6 +57,22 @@
 	case O_FRAGID:
 		if (cb->nvals == 1)
 			fraginfo->ids[1] = fraginfo->ids[0];
+		if (cb->invert)
+			fraginfo->invflags |= IP6T_FRAG_INV_IDS;
+		/*
+		 * Note however that IP6T_FRAG_IDS is not tested by anything,
+		 * so it is merely here for completeness.
+		 */
+		fraginfo->flags |= IP6T_FRAG_IDS;
+		break;
+	case O_FRAGLEN:
+		/*
+		 * As of Linux 3.0, the kernel does not check for
+		 * fraglen at all.
+		 */
+		if (cb->invert)
+			fraginfo->invflags |= IP6T_FRAG_INV_LEN;
+		fraginfo->flags |= IP6T_FRAG_LEN;
 		break;
 	case O_FRAGRES:
 		fraginfo->flags |= IP6T_FRAG_RES;
@@ -157,6 +180,7 @@
 	.size          = XT_ALIGN(sizeof(struct ip6t_frag)),
 	.userspacesize = XT_ALIGN(sizeof(struct ip6t_frag)),
 	.help          = frag_help,
+	.init          = frag_init,
 	.print         = frag_print,
 	.save          = frag_save,
 	.x6_parse      = frag_parse,
diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c
index 809e80d..c0389ed 100644
--- a/extensions/libip6t_hbh.c
+++ b/extensions/libip6t_hbh.c
@@ -108,6 +108,7 @@
 	case O_HBH_LEN:
 		if (cb->invert)
 			optinfo->invflags |= IP6T_OPTS_INV_LEN;
+		optinfo->flags |= IP6T_OPTS_LEN;
 		break;
 	case O_HBH_OPTS:
 		optinfo->optsnr = parse_options(cb->arg, optinfo->opts);
diff --git a/extensions/libip6t_mh.man b/extensions/libip6t_mh.man
index 4559e78..8ec08c6 100644
--- a/extensions/libip6t_mh.man
+++ b/extensions/libip6t_mh.man
@@ -8,5 +8,5 @@
 .IR type
 or one of the MH type names shown by the command
 .nf
- ip6tables \-p ipv6\-mh \-h
+ ip6tables \-p mh \-h
 .fi
diff --git a/extensions/libipt_CLUSTERIP.c b/extensions/libipt_CLUSTERIP.c
index 301e0e1..f4b638b 100644
--- a/extensions/libipt_CLUSTERIP.c
+++ b/extensions/libipt_CLUSTERIP.c
@@ -144,7 +144,7 @@
 	const struct ipt_clusterip_tgt_info *cipinfo =
 		(const struct ipt_clusterip_tgt_info *)target->data;
 	
-	if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) {
+	if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
 		printf(" CLUSTERIP");
 		return;
 	}
@@ -164,7 +164,7 @@
 
 	/* if this is not a new entry, we don't need to save target
 	 * parameters */
-	if (!cipinfo->flags & CLUSTERIP_FLAG_NEW)
+	if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW))
 		return;
 
 	printf(" --new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u",
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
index 3b55c69..ff18799 100644
--- a/extensions/libipt_DNAT.c
+++ b/extensions/libipt_DNAT.c
@@ -6,7 +6,7 @@
 #include <iptables.h> /* get_kernel_version */
 #include <limits.h> /* INT_MAX in ip_tables.h */
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 
 enum {
 	O_TO_DEST = 0,
@@ -23,7 +23,7 @@
 struct ipt_natinfo
 {
 	struct xt_entry_target t;
-	struct nf_nat_multi_range mr;
+	struct nf_nat_ipv4_multi_range_compat mr;
 };
 
 static void DNAT_help(void)
@@ -44,7 +44,7 @@
 };
 
 static struct ipt_natinfo *
-append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
+append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
 {
 	unsigned int size;
 
@@ -66,7 +66,7 @@
 static struct xt_entry_target *
 parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
 {
-	struct nf_nat_range range;
+	struct nf_nat_ipv4_range range;
 	char *arg, *colon, *dash, *error;
 	const struct in_addr *ip;
 
@@ -83,7 +83,7 @@
 			xtables_error(PARAMETER_PROBLEM,
 				   "Need TCP, UDP, SCTP or DCCP with port specification");
 
-		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 
 		port = atoi(colon+1);
 		if (port <= 0 || port > 65535)
@@ -122,7 +122,7 @@
 		*colon = '\0';
 	}
 
-	range.flags |= IP_NAT_RANGE_MAP_IPS;
+	range.flags |= NF_NAT_RANGE_MAP_IPS;
 	dash = strchr(arg, '-');
 	if (colon && dash && dash > colon)
 		dash = NULL;
@@ -174,24 +174,26 @@
 					   "DNAT: Multiple --to-destination not supported");
 		}
 		*cb->target = parse_to(cb->arg, portok, info);
-		/* WTF do we need this for?? */
-		if (cb->xflags & F_RANDOM)
-			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
 		cb->xflags |= F_X_TO_DEST;
 		break;
-	case O_RANDOM:
-		if (cb->xflags & F_TO_DEST)
-			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
-		break;
 	case O_PERSISTENT:
-		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
+		info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
 		break;
 	}
 }
 
-static void print_range(const struct nf_nat_range *r)
+static void DNAT_fcheck(struct xt_fcheck_call *cb)
 {
-	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+	static const unsigned int f = F_TO_DEST | F_RANDOM;
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
+
+	if ((cb->xflags & f) == f)
+		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
+static void print_range(const struct nf_nat_ipv4_range *r)
+{
+	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
 		struct in_addr a;
 
 		a.s_addr = r->min_ip;
@@ -201,7 +203,7 @@
 			printf("-%s", xtables_ipaddr_to_numeric(&a));
 		}
 	}
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(":");
 		printf("%hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
@@ -218,9 +220,9 @@
 	printf(" to:");
 	for (i = 0; i < info->mr.rangesize; i++) {
 		print_range(&info->mr.range[i]);
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" random");
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
 			printf(" persistent");
 	}
 }
@@ -233,9 +235,9 @@
 	for (i = 0; i < info->mr.rangesize; i++) {
 		printf(" --to-destination ");
 		print_range(&info->mr.range[i]);
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" --random");
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
 			printf(" --persistent");
 	}
 }
@@ -244,10 +246,11 @@
 	.name		= "DNAT",
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
+	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
 	.help		= DNAT_help,
 	.x6_parse	= DNAT_parse,
+	.x6_fcheck	= DNAT_fcheck,
 	.print		= DNAT_print,
 	.save		= DNAT_save,
 	.x6_options	= DNAT_opts,
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
index b270bcf..77f16d1 100644
--- a/extensions/libipt_LOG.c
+++ b/extensions/libipt_LOG.c
@@ -87,19 +87,19 @@
 				   "Newlines not allowed in --log-prefix");
 		break;
 	case O_LOG_TCPSEQ:
-		info->logflags = IPT_LOG_TCPSEQ;
+		info->logflags |= IPT_LOG_TCPSEQ;
 		break;
 	case O_LOG_TCPOPTS:
-		info->logflags = IPT_LOG_TCPOPT;
+		info->logflags |= IPT_LOG_TCPOPT;
 		break;
 	case O_LOG_IPOPTS:
-		info->logflags = IPT_LOG_IPOPT;
+		info->logflags |= IPT_LOG_IPOPT;
 		break;
 	case O_LOG_UID:
-		info->logflags = IPT_LOG_UID;
+		info->logflags |= IPT_LOG_UID;
 		break;
 	case O_LOG_MAC:
-		info->logflags = IPT_LOG_MACDECODE;
+		info->logflags |= IPT_LOG_MACDECODE;
 		break;
 	}
 }
diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man
deleted file mode 100644
index 47c35e0..0000000
--- a/extensions/libipt_LOG.man
+++ /dev/null
@@ -1,31 +0,0 @@
-Turn on kernel logging of matching packets.  When this option is set
-for a rule, the Linux kernel will print some information on all
-matching packets (like most IP header fields) via the kernel log
-(where it can be read with
-.I dmesg
-or 
-.IR syslogd (8)).
-This is a "non-terminating target", i.e. rule traversal continues at
-the next rule.  So if you want to LOG the packets you refuse, use two
-separate rules with the same matching criteria, first using target LOG
-then DROP (or REJECT).
-.TP
-\fB\-\-log\-level\fP \fIlevel\fP
-Level of logging (numeric or see \fIsyslog.conf\fP(5)).
-.TP
-\fB\-\-log\-prefix\fP \fIprefix\fP
-Prefix log messages with the specified prefix; up to 29 letters long,
-and useful for distinguishing messages in the logs.
-.TP
-\fB\-\-log\-tcp\-sequence\fP
-Log TCP sequence numbers. This is a security risk if the log is
-readable by users.
-.TP
-\fB\-\-log\-tcp\-options\fP
-Log options from the TCP packet header.
-.TP
-\fB\-\-log\-ip\-options\fP
-Log options from the IP packet header.
-.TP
-\fB\-\-log\-uid\fP
-Log the userid of the process which generated the packet.
diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c
index 7ba42df..ea07445 100644
--- a/extensions/libipt_MASQUERADE.c
+++ b/extensions/libipt_MASQUERADE.c
@@ -6,7 +6,7 @@
 #include <xtables.h>
 #include <limits.h> /* INT_MAX in ip_tables.h */
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 
 enum {
 	O_TO_PORTS = 0,
@@ -31,7 +31,7 @@
 
 static void MASQUERADE_init(struct xt_entry_target *t)
 {
-	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
+	struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
 
 	/* Actually, it's 0, but it's ignored at the moment. */
 	mr->rangesize = 1;
@@ -39,12 +39,12 @@
 
 /* Parses ports */
 static void
-parse_ports(const char *arg, struct nf_nat_multi_range *mr)
+parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
 {
 	char *end;
 	unsigned int port, maxport;
 
-	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+	mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 
 	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
 		xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
@@ -75,7 +75,7 @@
 {
 	const struct ipt_entry *entry = cb->xt_entry;
 	int portok;
-	struct nf_nat_multi_range *mr = cb->data;
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
 
 	if (entry->ip.proto == IPPROTO_TCP
 	    || entry->ip.proto == IPPROTO_UDP
@@ -95,7 +95,7 @@
 		parse_ports(cb->arg, mr);
 		break;
 	case O_RANDOM:
-		mr->range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
+		mr->range[0].flags |=  NF_NAT_RANGE_PROTO_RANDOM;
 		break;
 	}
 }
@@ -104,33 +104,33 @@
 MASQUERADE_print(const void *ip, const struct xt_entry_target *target,
                  int numeric)
 {
-	const struct nf_nat_multi_range *mr = (const void *)target->data;
-	const struct nf_nat_range *r = &mr->range[0];
+	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
 
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(" masq ports: ");
 		printf("%hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
 			printf("-%hu", ntohs(r->max.tcp.port));
 	}
 
-	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
 		printf(" random");
 }
 
 static void
 MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
 {
-	const struct nf_nat_multi_range *mr = (const void *)target->data;
-	const struct nf_nat_range *r = &mr->range[0];
+	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
 
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(" --to-ports %hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
 			printf("-%hu", ntohs(r->max.tcp.port));
 	}
 
-	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
+	if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
 		printf(" --random");
 }
 
@@ -138,8 +138,8 @@
 	.name		= "MASQUERADE",
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
+	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
 	.help		= MASQUERADE_help,
 	.init		= MASQUERADE_init,
 	.x6_parse	= MASQUERADE_parse,
diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c
index 5c4471a..dee7b01 100644
--- a/extensions/libipt_NETMAP.c
+++ b/extensions/libipt_NETMAP.c
@@ -7,7 +7,7 @@
 #include <stdlib.h>
 #include <getopt.h>
 #include <xtables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 
 #define MODULENAME "NETMAP"
 
@@ -45,7 +45,7 @@
 
 static void NETMAP_init(struct xt_entry_target *t)
 {
-	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
+	struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
 
 	/* Actually, it's 0, but it's ignored at the moment. */
 	mr->rangesize = 1;
@@ -53,11 +53,11 @@
 
 static void NETMAP_parse(struct xt_option_call *cb)
 {
-	struct nf_nat_multi_range *mr = cb->data;
-	struct nf_nat_range *range = &mr->range[0];
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
+	struct nf_nat_ipv4_range *range = &mr->range[0];
 
 	xtables_option_parse(cb);
-	range->flags |= IP_NAT_RANGE_MAP_IPS;
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
 	range->min_ip = cb->val.haddr.ip & cb->val.hmask.ip;
 	range->max_ip = range->min_ip | ~cb->val.hmask.ip;
 }
@@ -65,8 +65,8 @@
 static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
                          int numeric)
 {
-	const struct nf_nat_multi_range *mr = (const void *)target->data;
-	const struct nf_nat_range *r = &mr->range[0];
+	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
 	struct in_addr a;
 	int bits;
 
@@ -90,8 +90,8 @@
 	.name		= MODULENAME,
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
+	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
 	.help		= NETMAP_help,
 	.init		= NETMAP_init,
 	.x6_parse	= NETMAP_parse,
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
index e67360a..610a949 100644
--- a/extensions/libipt_REDIRECT.c
+++ b/extensions/libipt_REDIRECT.c
@@ -4,7 +4,7 @@
 #include <xtables.h>
 #include <limits.h> /* INT_MAX in ip_tables.h */
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 
 enum {
 	O_TO_PORTS = 0,
@@ -30,7 +30,7 @@
 
 static void REDIRECT_init(struct xt_entry_target *t)
 {
-	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
+	struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
 
 	/* Actually, it's 0, but it's ignored at the moment. */
 	mr->rangesize = 1;
@@ -38,12 +38,12 @@
 
 /* Parses ports */
 static void
-parse_ports(const char *arg, struct nf_nat_multi_range *mr)
+parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
 {
 	char *end = "";
 	unsigned int port, maxport;
 
-	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+	mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 
 	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
 	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
@@ -75,7 +75,7 @@
 static void REDIRECT_parse(struct xt_option_call *cb)
 {
 	const struct ipt_entry *entry = cb->xt_entry;
-	struct nf_nat_multi_range *mr = (void *)(*cb->target)->data;
+	struct nf_nat_ipv4_multi_range_compat *mr = (void *)(*cb->target)->data;
 	int portok;
 
 	if (entry->ip.proto == IPPROTO_TCP
@@ -95,11 +95,11 @@
 				   "Need TCP, UDP, SCTP or DCCP with port specification");
 		parse_ports(cb->arg, mr);
 		if (cb->xflags & F_RANDOM)
-			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
 		break;
 	case O_RANDOM:
 		if (cb->xflags & F_TO_PORTS)
-			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
 		break;
 	}
 }
@@ -107,30 +107,30 @@
 static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
                            int numeric)
 {
-	const struct nf_nat_multi_range *mr = (const void *)target->data;
-	const struct nf_nat_range *r = &mr->range[0];
+	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
 
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(" redir ports ");
 		printf("%hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
 			printf("-%hu", ntohs(r->max.tcp.port));
-		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" random");
 	}
 }
 
 static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
 {
-	const struct nf_nat_multi_range *mr = (const void *)target->data;
-	const struct nf_nat_range *r = &mr->range[0];
+	const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
 
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(" --to-ports ");
 		printf("%hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
 			printf("-%hu", ntohs(r->max.tcp.port));
-		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (mr->range[0].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" --random");
 	}
 }
@@ -139,8 +139,8 @@
 	.name		= "REDIRECT",
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
+	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
 	.help		= REDIRECT_help,
 	.init		= REDIRECT_init,
  	.x6_parse	= REDIRECT_parse,
diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c
index 2ff6c82..5d5bf63 100644
--- a/extensions/libipt_SAME.c
+++ b/extensions/libipt_SAME.c
@@ -2,14 +2,15 @@
 #include <string.h>
 #include <stdlib.h>
 #include <xtables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 #include <linux/netfilter_ipv4/ipt_SAME.h>
 
 enum {
 	O_TO_ADDR = 0,
 	O_NODST,
 	O_RANDOM,
-	F_RANDOM = 1 << O_RANDOM,
+	F_TO_ADDR = 1 << O_TO_ADDR,
+	F_RANDOM  = 1 << O_RANDOM,
 };
 
 static void SAME_help(void)
@@ -36,7 +37,7 @@
 };
 
 /* Parses range of IPs */
-static void parse_to(const char *orig_arg, struct nf_nat_range *range)
+static void parse_to(const char *orig_arg, struct nf_nat_ipv4_range *range)
 {
 	char *dash, *arg;
 	const struct in_addr *ip;
@@ -44,7 +45,7 @@
 	arg = strdup(orig_arg);
 	if (arg == NULL)
 		xtables_error(RESOURCE_PROBLEM, "strdup");
-	range->flags |= IP_NAT_RANGE_MAP_IPS;
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
 	dash = strchr(arg, '-');
 
 	if (dash)
@@ -84,10 +85,6 @@
 				   "is %i ranges.\n",
 				   IPT_SAME_MAX_RANGE);
 		parse_to(cb->arg, &mr->range[mr->rangesize]);
-		/* WTF do we need this for? */
-		if (cb->xflags & F_RANDOM)
-			mr->range[mr->rangesize].flags 
-				|= IP_NAT_RANGE_PROTO_RANDOM;
 		mr->rangesize++;
 		break;
 	case O_NODST:
@@ -95,11 +92,22 @@
 		break;
 	case O_RANDOM:
 		for (count=0; count < mr->rangesize; count++)
-			mr->range[count].flags |= IP_NAT_RANGE_PROTO_RANDOM;
+			mr->range[count].flags |= NF_NAT_RANGE_PROTO_RANDOM;
 		break;
 	}
 }
 
+static void SAME_fcheck(struct xt_fcheck_call *cb)
+{
+	static const unsigned int f = F_TO_ADDR | F_RANDOM;
+	struct ipt_same_info *mr = cb->data;
+	unsigned int count;
+
+	if ((cb->xflags & f) == f)
+		for (count = 0; count < mr->rangesize; ++count)
+			mr->range[count].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
 static void SAME_print(const void *ip, const struct xt_entry_target *target,
                        int numeric)
 {
@@ -110,7 +118,7 @@
 	printf(" same:");
 
 	for (count = 0; count < mr->rangesize; count++) {
-		const struct nf_nat_range *r = &mr->range[count];
+		const struct nf_nat_ipv4_range *r = &mr->range[count];
 		struct in_addr a;
 
 		a.s_addr = r->min_ip;
@@ -120,7 +128,7 @@
 		
 		if (r->min_ip != r->max_ip)
 			printf("-%s", xtables_ipaddr_to_numeric(&a));
-		if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) 
+		if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
 			random_selection = 1;
 	}
 	
@@ -138,7 +146,7 @@
 	int random_selection = 0;
 
 	for (count = 0; count < mr->rangesize; count++) {
-		const struct nf_nat_range *r = &mr->range[count];
+		const struct nf_nat_ipv4_range *r = &mr->range[count];
 		struct in_addr a;
 
 		a.s_addr = r->min_ip;
@@ -147,7 +155,7 @@
 
 		if (r->min_ip != r->max_ip)
 			printf("-%s", xtables_ipaddr_to_numeric(&a));
-		if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) 
+		if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
 			random_selection = 1;
 	}
 	
@@ -166,6 +174,7 @@
 	.userspacesize	= XT_ALIGN(sizeof(struct ipt_same_info)),
 	.help		= SAME_help,
 	.x6_parse	= SAME_parse,
+	.x6_fcheck	= SAME_fcheck,
 	.print		= SAME_print,
 	.save		= SAME_save,
 	.x6_options	= SAME_opts,
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
index 8023306..1a24f3d 100644
--- a/extensions/libipt_SNAT.c
+++ b/extensions/libipt_SNAT.c
@@ -6,7 +6,7 @@
 #include <iptables.h>
 #include <limits.h> /* INT_MAX in ip_tables.h */
 #include <linux/netfilter_ipv4/ip_tables.h>
-#include <net/netfilter/nf_nat.h>
+#include <linux/netfilter/nf_nat.h>
 
 enum {
 	O_TO_SRC = 0,
@@ -23,7 +23,7 @@
 struct ipt_natinfo
 {
 	struct xt_entry_target t;
-	struct nf_nat_multi_range mr;
+	struct nf_nat_ipv4_multi_range_compat mr;
 };
 
 static void SNAT_help(void)
@@ -44,7 +44,7 @@
 };
 
 static struct ipt_natinfo *
-append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
+append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
 {
 	unsigned int size;
 
@@ -66,7 +66,7 @@
 static struct xt_entry_target *
 parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
 {
-	struct nf_nat_range range;
+	struct nf_nat_ipv4_range range;
 	char *arg, *colon, *dash, *error;
 	const struct in_addr *ip;
 
@@ -83,7 +83,7 @@
 			xtables_error(PARAMETER_PROBLEM,
 				   "Need TCP, UDP, SCTP or DCCP with port specification");
 
-		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 
 		port = atoi(colon+1);
 		if (port <= 0 || port > 65535)
@@ -122,7 +122,7 @@
 		*colon = '\0';
 	}
 
-	range.flags |= IP_NAT_RANGE_MAP_IPS;
+	range.flags |= NF_NAT_RANGE_MAP_IPS;
 	dash = strchr(arg, '-');
 	if (colon && dash && dash > colon)
 		dash = NULL;
@@ -174,24 +174,26 @@
 					   "SNAT: Multiple --to-source not supported");
 		}
 		*cb->target = parse_to(cb->arg, portok, info);
-		/* WTF do we need this for?? */
-		if (cb->xflags & F_RANDOM)
-			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
 		cb->xflags |= F_X_TO_SRC;
 		break;
-	case O_RANDOM:
-		if (cb->xflags & F_TO_SRC)
-			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
-		break;
 	case O_PERSISTENT:
-		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
+		info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
 		break;
 	}
 }
 
-static void print_range(const struct nf_nat_range *r)
+static void SNAT_fcheck(struct xt_fcheck_call *cb)
 {
-	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+	static const unsigned int f = F_TO_SRC | F_RANDOM;
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
+
+	if ((cb->xflags & f) == f)
+		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+}
+
+static void print_range(const struct nf_nat_ipv4_range *r)
+{
+	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
 		struct in_addr a;
 
 		a.s_addr = r->min_ip;
@@ -201,7 +203,7 @@
 			printf("-%s", xtables_ipaddr_to_numeric(&a));
 		}
 	}
-	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
 		printf(":");
 		printf("%hu", ntohs(r->min.tcp.port));
 		if (r->max.tcp.port != r->min.tcp.port)
@@ -218,9 +220,9 @@
 	printf(" to:");
 	for (i = 0; i < info->mr.rangesize; i++) {
 		print_range(&info->mr.range[i]);
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" random");
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
 			printf(" persistent");
 	}
 }
@@ -233,9 +235,9 @@
 	for (i = 0; i < info->mr.rangesize; i++) {
 		printf(" --to-source ");
 		print_range(&info->mr.range[i]);
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
 			printf(" --random");
-		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
 			printf(" --persistent");
 	}
 }
@@ -244,10 +246,11 @@
 	.name		= "SNAT",
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
-	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
+	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
 	.help		= SNAT_help,
 	.x6_parse	= SNAT_parse,
+	.x6_fcheck	= SNAT_fcheck,
 	.print		= SNAT_print,
 	.save		= SNAT_save,
 	.x6_options	= SNAT_opts,
diff --git a/extensions/libipt_ULOG.c b/extensions/libipt_ULOG.c
index a341a8d..5163eea 100644
--- a/extensions/libipt_ULOG.c
+++ b/extensions/libipt_ULOG.c
@@ -38,9 +38,9 @@
 	{.name = "ulog-prefix", .id = O_ULOG_PREFIX, .type = XTTYPE_STRING,
 	 .flags = XTOPT_PUT, XTOPT_POINTER(struct ipt_ulog_info, prefix),
 	 .min = 1},
-	{.name = "ulog-cprange", .id = O_ULOG_CPRANGE, .type = XTTYPE_UINT64,
+	{.name = "ulog-cprange", .id = O_ULOG_CPRANGE, .type = XTTYPE_UINT64},
+	{.name = "ulog-qthreshold", .id = O_ULOG_QTHR, .type = XTTYPE_UINT64,
 	 .min = 1, .max = ULOG_MAX_QLEN},
-	{.name = "ulog-qthreshold", .id = O_ULOG_QTHR, .type = XTTYPE_UINT64},
 	XTOPT_TABLEEND,
 };
 
diff --git a/extensions/libipt_ULOG.man b/extensions/libipt_ULOG.man
index 649b6e3..c91f776 100644
--- a/extensions/libipt_ULOG.man
+++ b/extensions/libipt_ULOG.man
@@ -1,4 +1,5 @@
-This target provides userspace logging of matching packets.  When this
+This is the deprecated ipv4-only predecessor of the NFLOG target.
+It provides userspace logging of matching packets.  When this
 target is set for a rule, the Linux kernel will multicast this packet
 through a
 .IR netlink 
diff --git a/extensions/libipt_ecn.man b/extensions/libipt_ecn.man
deleted file mode 100644
index 7f80647..0000000
--- a/extensions/libipt_ecn.man
+++ /dev/null
@@ -1,11 +0,0 @@
-This allows you to match the ECN bits of the IPv4 and TCP header.  ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
-.TP
-[\fB!\fP] \fB\-\-ecn\-tcp\-cwr\fP
-This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
-.TP
-[\fB!\fP] \fB\-\-ecn\-tcp\-ece\fP
-This matches if the TCP ECN ECE (ECN Echo) bit is set.
-.TP
-[\fB!\fP] \fB\-\-ecn\-ip\-ect\fP \fInum\fP
-This matches a particular IPv4 ECT (ECN-Capable Transport). You have to specify
-a number between `0' and `3'.
diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c
index b60c57e..a8d9dda 100644
--- a/extensions/libipt_realm.c
+++ b/extensions/libipt_realm.c
@@ -41,7 +41,7 @@
 
 static void realm_parse(struct xt_option_call *cb)
 {
-	struct ipt_realm_info *realminfo = cb->data;
+	struct xt_realm_info *realminfo = cb->data;
 	int id;
 	char *end;
 
@@ -87,7 +87,7 @@
 static void realm_print(const void *ip, const struct xt_entry_match *match,
                         int numeric)
 {
-	const struct ipt_realm_info *ri = (const void *)match->data;
+	const struct xt_realm_info *ri = (const void *)match->data;
 
 	if (ri->invert)
 		printf(" !");
@@ -98,7 +98,7 @@
 
 static void realm_save(const void *ip, const struct xt_entry_match *match)
 {
-	const struct ipt_realm_info *ri = (const void *)match->data;
+	const struct xt_realm_info *ri = (const void *)match->data;
 
 	if (ri->invert)
 		printf(" !");
@@ -111,8 +111,8 @@
 	.name		= "realm",
 	.version	= XTABLES_VERSION,
 	.family		= NFPROTO_IPV4,
-	.size		= XT_ALIGN(sizeof(struct ipt_realm_info)),
-	.userspacesize	= XT_ALIGN(sizeof(struct ipt_realm_info)),
+	.size		= XT_ALIGN(sizeof(struct xt_realm_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_realm_info)),
 	.help		= realm_help,
 	.init		= realm_init,
 	.print		= realm_print,
diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c
index 6370cb6..5fe08cc 100644
--- a/extensions/libipt_ttl.c
+++ b/extensions/libipt_ttl.c
@@ -20,7 +20,7 @@
 {
 	printf(
 "ttl match options:\n"
-"  --ttl-eq value	Match time to live value\n"
+"[!] --ttl-eq value	Match time to live value\n"
 "  --ttl-lt value	Match TTL < value\n"
 "  --ttl-gt value	Match TTL > value\n");
 }
diff --git a/extensions/libipt_ttl.man b/extensions/libipt_ttl.man
index 849f704..1f32277 100644
--- a/extensions/libipt_ttl.man
+++ b/extensions/libipt_ttl.man
@@ -1,6 +1,6 @@
 This module matches the time to live field in the IP header.
 .TP
-\fB\-\-ttl\-eq\fP \fIttl\fP
+[\fB!\fP] \fB\-\-ttl\-eq\fP \fIttl\fP
 Matches the given TTL value.
 .TP
 \fB\-\-ttl\-gt\fP \fIttl\fP
diff --git a/extensions/libxt_CLASSIFY.c b/extensions/libxt_CLASSIFY.c
index ee0f9e1..e04657a 100644
--- a/extensions/libxt_CLASSIFY.c
+++ b/extensions/libxt_CLASSIFY.c
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) 2003-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <stdio.h>
 #include <xtables.h>
 #include <linux/netfilter/xt_CLASSIFY.h>
diff --git a/extensions/libxt_CONNSECMARK.c b/extensions/libxt_CONNSECMARK.c
index df2e6b8..0b3cd79 100644
--- a/extensions/libxt_CONNSECMARK.c
+++ b/extensions/libxt_CONNSECMARK.c
@@ -87,7 +87,7 @@
 	const struct xt_connsecmark_target_info *info =
 		(struct xt_connsecmark_target_info*)target->data;
 
-	printf("--");
+	printf(" --");
 	print_connsecmark(info);
 }
 
diff --git a/extensions/libxt_CT.c b/extensions/libxt_CT.c
index 7b93bfa..6b28fe1 100644
--- a/extensions/libxt_CT.c
+++ b/extensions/libxt_CT.c
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) 2010-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <xtables.h>
@@ -16,9 +20,23 @@
 	);
 }
 
+static void ct_help_v1(void)
+{
+	printf(
+"CT target options:\n"
+" --notrack			Don't track connection\n"
+" --helper name			Use conntrack helper 'name' for connection\n"
+" --timeout name 		Use timeout policy 'name' for connection\n"
+" --ctevents event[,event...]	Generate specified conntrack events for connection\n"
+" --expevents event[,event...]	Generate specified expectation events for connection\n"
+" --zone ID			Assign/Lookup connection in zone ID\n"
+	);
+}
+
 enum {
 	O_NOTRACK = 0,
 	O_HELPER,
+	O_TIMEOUT,
 	O_CTEVENTS,
 	O_EXPEVENTS,
 	O_ZONE,
@@ -37,6 +55,21 @@
 };
 #undef s
 
+#define s struct xt_ct_target_info_v1
+static const struct xt_option_entry ct_opts_v1[] = {
+	{.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
+	{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
+	{.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, timeout)},
+	{.name = "ctevents", .id = O_CTEVENTS, .type = XTTYPE_STRING},
+	{.name = "expevents", .id = O_EXPEVENTS, .type = XTTYPE_STRING},
+	{.name = "zone", .id = O_ZONE, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, zone)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
 struct event_tbl {
 	const char	*name;
 	unsigned int	event;
@@ -114,6 +147,28 @@
 	}
 }
 
+static void ct_parse_v1(struct xt_option_call *cb)
+{
+	struct xt_ct_target_info_v1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_NOTRACK:
+		info->flags |= XT_CT_NOTRACK;
+		break;
+	case O_CTEVENTS:
+		info->ct_events = ct_parse_events(ct_event_tbl,
+						  ARRAY_SIZE(ct_event_tbl),
+						  cb->arg);
+		break;
+	case O_EXPEVENTS:
+		info->exp_events = ct_parse_events(exp_event_tbl,
+						   ARRAY_SIZE(exp_event_tbl),
+						   cb->arg);
+		break;
+	}
+}
+
 static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric)
 {
 	const struct xt_ct_target_info *info =
@@ -134,11 +189,40 @@
 		printf("zone %u ", info->zone);
 }
 
+static void
+ct_print_v1(const void *ip, const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_ct_target_info_v1 *info =
+		(const struct xt_ct_target_info_v1 *)target->data;
+
+	if (info->flags & XT_CT_NOTRACK_ALIAS) {
+		printf (" NOTRACK");
+		return;
+	}
+	printf(" CT");
+	if (info->flags & XT_CT_NOTRACK)
+		printf(" notrack");
+	if (info->helper[0])
+		printf(" helper %s", info->helper);
+	if (info->timeout[0])
+		printf(" timeout %s", info->timeout);
+	if (info->ct_events)
+		ct_print_events("ctevents", ct_event_tbl,
+				ARRAY_SIZE(ct_event_tbl), info->ct_events);
+	if (info->exp_events)
+		ct_print_events("expevents", exp_event_tbl,
+				ARRAY_SIZE(exp_event_tbl), info->exp_events);
+	if (info->zone)
+		printf("zone %u ", info->zone);
+}
+
 static void ct_save(const void *ip, const struct xt_entry_target *target)
 {
 	const struct xt_ct_target_info *info =
 		(const struct xt_ct_target_info *)target->data;
 
+	if (info->flags & XT_CT_NOTRACK_ALIAS)
+		return;
 	if (info->flags & XT_CT_NOTRACK)
 		printf(" --notrack");
 	if (info->helper[0])
@@ -153,20 +237,138 @@
 		printf(" --zone %u", info->zone);
 }
 
-static struct xtables_target ct_target = {
-	.family		= NFPROTO_UNSPEC,
-	.name		= "CT",
-	.version	= XTABLES_VERSION,
-	.size		= XT_ALIGN(sizeof(struct xt_ct_target_info)),
-	.userspacesize	= offsetof(struct xt_ct_target_info, ct),
-	.help		= ct_help,
-	.print		= ct_print,
-	.save		= ct_save,
-	.x6_parse	= ct_parse,
-	.x6_options	= ct_opts,
+static void ct_save_v1(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_ct_target_info_v1 *info =
+		(const struct xt_ct_target_info_v1 *)target->data;
+
+	if (info->flags & XT_CT_NOTRACK_ALIAS)
+		return;
+	if (info->flags & XT_CT_NOTRACK)
+		printf(" --notrack");
+	if (info->helper[0])
+		printf(" --helper %s", info->helper);
+	if (info->timeout[0])
+		printf(" --timeout %s", info->timeout);
+	if (info->ct_events)
+		ct_print_events("--ctevents", ct_event_tbl,
+				ARRAY_SIZE(ct_event_tbl), info->ct_events);
+	if (info->exp_events)
+		ct_print_events("--expevents", exp_event_tbl,
+				ARRAY_SIZE(exp_event_tbl), info->exp_events);
+	if (info->zone)
+		printf(" --zone %u", info->zone);
+}
+
+static const char *
+ct_print_name_alias(const struct xt_entry_target *target)
+{
+	struct xt_ct_target_info *info = (void *)target->data;
+
+	return info->flags & XT_CT_NOTRACK_ALIAS ? "NOTRACK" : "CT";
+}
+
+static void notrack_ct0_tg_init(struct xt_entry_target *target)
+{
+	struct xt_ct_target_info *info = (void *)target->data;
+
+	info->flags = XT_CT_NOTRACK;
+}
+
+static void notrack_ct1_tg_init(struct xt_entry_target *target)
+{
+	struct xt_ct_target_info_v1 *info = (void *)target->data;
+
+	info->flags = XT_CT_NOTRACK;
+}
+
+static void notrack_ct2_tg_init(struct xt_entry_target *target)
+{
+	struct xt_ct_target_info_v1 *info = (void *)target->data;
+
+	info->flags = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS;
+}
+
+static struct xtables_target ct_target_reg[] = {
+	{
+		.family		= NFPROTO_UNSPEC,
+		.name		= "CT",
+		.version	= XTABLES_VERSION,
+		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info)),
+		.userspacesize	= offsetof(struct xt_ct_target_info, ct),
+		.help		= ct_help,
+		.print		= ct_print,
+		.save		= ct_save,
+		.x6_parse	= ct_parse,
+		.x6_options	= ct_opts,
+	},
+	{
+		.family		= NFPROTO_UNSPEC,
+		.name		= "CT",
+		.revision	= 1,
+		.version	= XTABLES_VERSION,
+		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
+		.userspacesize	= offsetof(struct xt_ct_target_info_v1, ct),
+		.help		= ct_help_v1,
+		.print		= ct_print_v1,
+		.save		= ct_save_v1,
+		.x6_parse	= ct_parse_v1,
+		.x6_options	= ct_opts_v1,
+	},
+	{
+		.family		= NFPROTO_UNSPEC,
+		.name		= "CT",
+		.revision	= 2,
+		.version	= XTABLES_VERSION,
+		.size		= XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
+		.userspacesize	= offsetof(struct xt_ct_target_info_v1, ct),
+		.help		= ct_help_v1,
+		.print		= ct_print_v1,
+		.save		= ct_save_v1,
+		.alias		= ct_print_name_alias,
+		.x6_parse	= ct_parse_v1,
+		.x6_options	= ct_opts_v1,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "NOTRACK",
+		.real_name     = "CT",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info)),
+		.userspacesize = offsetof(struct xt_ct_target_info, ct),
+		.init          = notrack_ct0_tg_init,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "NOTRACK",
+		.real_name     = "CT",
+		.revision      = 1,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
+		.userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
+		.init          = notrack_ct1_tg_init,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "NOTRACK",
+		.real_name     = "CT",
+		.revision      = 2,
+		.ext_flags     = XTABLES_EXT_ALIAS,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_ct_target_info_v1)),
+		.userspacesize = offsetof(struct xt_ct_target_info_v1, ct),
+		.init          = notrack_ct2_tg_init,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "NOTRACK",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+	},
 };
 
 void _init(void)
 {
-	xtables_register_target(&ct_target);
+	xtables_register_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg));
 }
diff --git a/extensions/libxt_CT.man b/extensions/libxt_CT.man
index ff258b7..a93eb14 100644
--- a/extensions/libxt_CT.man
+++ b/extensions/libxt_CT.man
@@ -23,3 +23,8 @@
 \fB\-\-zone\fP \fIid\fP
 Assign this packet to zone \fIid\fP and only have lookups done in that zone.
 By default, packets have zone 0.
+.TP
+\fB\-\-timeout\fP \fIname\fP
+Use the timeout policy identified by \fIname\fP for the connection. This is
+provides more flexible timeout policy definition than global timeout values
+available at /proc/sys/net/netfilter/nf_conntrack_*_timeout_*.
diff --git a/extensions/libipt_DNAT.man b/extensions/libxt_DNAT.man
similarity index 85%
rename from extensions/libipt_DNAT.man
rename to extensions/libxt_DNAT.man
index d5ded35..225274f 100644
--- a/extensions/libipt_DNAT.man
+++ b/extensions/libxt_DNAT.man
@@ -7,20 +7,17 @@
 chains, and user-defined chains which are only called from those
 chains.  It specifies that the destination address of the packet
 should be modified (and all future packets in this connection will
-also be mangled), and rules should cease being examined.  It takes one
-type of option:
+also be mangled), and rules should cease being examined.  It takes the
+following options:
 .TP
 \fB\-\-to\-destination\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]]
 which can specify a single new destination IP address, an inclusive
-range of IP addresses, and optionally, a port range (which is only
-valid if the rule also specifies
-\fB\-p tcp\fP
-or
-\fB\-p udp\fP).
+range of IP addresses. Optionally a port range,
+if the rule also specifies one of the following protocols:
+\fBtcp\fP, \fBudp\fP, \fBdccp\fP or \fBsctp\fP.
 If no port range is specified, then the destination port will never be
 modified. If no IP address is specified then only the destination port
 will be modified.
-
 In Kernels up to 2.6.10 you can add several \-\-to\-destination options. For
 those kernels, if you specify more than one destination address, either via an
 address range or multiple \-\-to\-destination options, a simple round-robin (one
@@ -37,3 +34,5 @@
 Gives a client the same source-/destination-address for each connection.
 This supersedes the SAME target. Support for persistent mappings is available
 from 2.6.29-rc2.
+.TP
+IPv6 support available since Linux kernels >= 3.7.
diff --git a/extensions/libxt_HMARK.c b/extensions/libxt_HMARK.c
new file mode 100644
index 0000000..94aebe9
--- /dev/null
+++ b/extensions/libxt_HMARK.c
@@ -0,0 +1,450 @@
+/*
+ * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
+ * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Description: shared library add-on to iptables to add HMARK target support
+ *
+ * Initial development by Hans Schillstrom. Pablo's improvements to this piece
+ * of software has been sponsored by Sophos Astaro <http://www.sophos.com>.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "xtables.h"
+#include <linux/netfilter/xt_HMARK.h>
+
+static void HMARK_help(void)
+{
+	printf(
+"HMARK target options, i.e. modify hash calculation by:\n"
+"  --hmark-tuple [src|dst|sport|dport|spi|proto|ct][,...]\n"
+"  --hmark-mod value		    nfmark modulus value\n"
+"  --hmark-offset value		    Last action add value to nfmark\n\n"
+"  --hmark-rnd			    Random see for hashing\n"
+" Alternatively, fine tuning of what will be included in hash calculation\n"
+"  --hmark-src-prefix length	    Source address mask CIDR prefix\n"
+"  --hmark-dst-prefix length	    Dest address mask CIDR prefix\n"
+"  --hmark-sport-mask value	    Mask src port with value\n"
+"  --hmark-dport-mask value	    Mask dst port with value\n"
+"  --hmark-spi-mask value	    For esp and ah AND spi with value\n"
+"  --hmark-sport value		    OR src port with value\n"
+"  --hmark-dport value		    OR dst port with value\n"
+"  --hmark-spi value		    For esp and ah OR spi with value\n"
+"  --hmark-proto-mask value	    Mask Protocol with value\n");
+}
+
+#define hi struct xt_hmark_info
+
+enum {
+	O_HMARK_SADDR_MASK,
+	O_HMARK_DADDR_MASK,
+	O_HMARK_SPI,
+	O_HMARK_SPI_MASK,
+	O_HMARK_SPORT,
+	O_HMARK_DPORT,
+	O_HMARK_SPORT_MASK,
+	O_HMARK_DPORT_MASK,
+	O_HMARK_PROTO_MASK,
+	O_HMARK_RND,
+	O_HMARK_MODULUS,
+	O_HMARK_OFFSET,
+	O_HMARK_CT,
+	O_HMARK_TYPE,
+};
+
+#define HMARK_OPT_PKT_MASK			\
+	((1 << O_HMARK_SADDR_MASK)		| \
+	 (1 << O_HMARK_DADDR_MASK)		| \
+	 (1 << O_HMARK_SPI_MASK)		| \
+	 (1 << O_HMARK_SPORT_MASK)		| \
+	 (1 << O_HMARK_DPORT_MASK)		| \
+	 (1 << O_HMARK_PROTO_MASK)		| \
+	 (1 << O_HMARK_SPI_MASK)		| \
+	 (1 << O_HMARK_SPORT)			| \
+	 (1 << O_HMARK_DPORT)			| \
+	 (1 << O_HMARK_SPI))
+
+static const struct xt_option_entry HMARK_opts[] = {
+	{ .name  = "hmark-tuple",
+	  .type  = XTTYPE_STRING,
+	  .id	 = O_HMARK_TYPE,
+	},
+	{ .name  = "hmark-src-prefix",
+	  .type  = XTTYPE_PLENMASK,
+	  .id	 = O_HMARK_SADDR_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, src_mask)
+	},
+	{ .name  = "hmark-dst-prefix",
+	  .type  = XTTYPE_PLENMASK,
+	  .id	 = O_HMARK_DADDR_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, dst_mask)
+	},
+	{ .name  = "hmark-sport-mask",
+	  .type  = XTTYPE_UINT16,
+	  .id	 = O_HMARK_SPORT_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_mask.p16.src)
+	},
+	{ .name  = "hmark-dport-mask",
+	  .type  = XTTYPE_UINT16,
+	  .id	 = O_HMARK_DPORT_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_mask.p16.dst)
+	},
+	{ .name  = "hmark-spi-mask",
+	  .type  = XTTYPE_UINT32,
+	  .id	 = O_HMARK_SPI_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_mask.v32)
+	},
+	{ .name  = "hmark-sport",
+	  .type  = XTTYPE_UINT16,
+	  .id	 = O_HMARK_SPORT,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_set.p16.src)
+	},
+	{ .name  = "hmark-dport",
+	  .type  = XTTYPE_UINT16,
+	  .id	 = O_HMARK_DPORT,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_set.p16.dst)
+	},
+	{ .name  = "hmark-spi",
+	  .type  = XTTYPE_UINT32,
+	  .id	 = O_HMARK_SPI,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, port_set.v32)
+	},
+	{ .name  = "hmark-proto-mask",
+	  .type  = XTTYPE_UINT16,
+	  .id	 = O_HMARK_PROTO_MASK,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, proto_mask)
+	},
+	{ .name  = "hmark-rnd",
+	  .type  = XTTYPE_UINT32,
+	  .id	 = O_HMARK_RND,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, hashrnd)
+	},
+	{ .name = "hmark-mod",
+	  .type = XTTYPE_UINT32,
+	  .id = O_HMARK_MODULUS,
+	  .min = 1,
+	  .flags = XTOPT_PUT | XTOPT_MAND, XTOPT_POINTER(hi, hmodulus)
+	},
+	{ .name  = "hmark-offset",
+	  .type  = XTTYPE_UINT32,
+	  .id	 = O_HMARK_OFFSET,
+	  .flags = XTOPT_PUT, XTOPT_POINTER(hi, hoffset)
+	},
+	XTOPT_TABLEEND,
+};
+
+static int
+hmark_parse(const char *type, size_t len, struct xt_hmark_info *info,
+	    unsigned int *xflags)
+{
+	if (strncasecmp(type, "ct", len) == 0) {
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_CT);
+		*xflags |= (1 << O_HMARK_CT);
+	} else if (strncasecmp(type, "src", len) == 0) {
+		memset(&info->src_mask, 0xff, sizeof(info->src_mask));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SADDR_MASK);
+		*xflags |= (1 << O_HMARK_SADDR_MASK);
+	} else if (strncasecmp(type, "dst", len) == 0) {
+		memset(&info->dst_mask, 0xff, sizeof(info->dst_mask));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_DADDR_MASK);
+		*xflags |= (1 << O_HMARK_DADDR_MASK);
+	} else if (strncasecmp(type, "sport", len) == 0) {
+		memset(&info->port_mask.p16.src, 0xff,
+			sizeof(info->port_mask.p16.src));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPORT_MASK);
+		*xflags |= (1 << O_HMARK_SPORT_MASK);
+	} else if (strncasecmp(type, "dport", len) == 0) {
+		memset(&info->port_mask.p16.dst, 0xff,
+			sizeof(info->port_mask.p16.dst));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_DPORT_MASK);
+		*xflags |= (1 << O_HMARK_DPORT_MASK);
+	} else if (strncasecmp(type, "proto", len) == 0) {
+		memset(&info->proto_mask, 0xff, sizeof(info->proto_mask));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_PROTO_MASK);
+		*xflags |= (1 << O_HMARK_PROTO_MASK);
+	} else if (strncasecmp(type, "spi", len) == 0) {
+		memset(&info->port_mask.v32, 0xff, sizeof(info->port_mask.v32));
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPI_MASK);
+		*xflags |= (1 << O_HMARK_SPI_MASK);
+	} else
+		return 0;
+
+	return 1;
+}
+
+static void
+hmark_parse_type(struct xt_option_call *cb)
+{
+	const char *arg = cb->arg;
+	struct xt_hmark_info *info = cb->data;
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg ||
+		    !hmark_parse(arg, comma-arg, info, &cb->xflags))
+			xtables_error(PARAMETER_PROBLEM, "Bad type \"%s\"", arg);
+		arg = comma+1;
+	}
+	if (!*arg)
+		xtables_error(PARAMETER_PROBLEM, "\"--hmark-tuple\" requires "
+						 "a list of types with no "
+						 "spaces, e.g. "
+						 "src,dst,sport,dport,proto");
+	if (strlen(arg) == 0 ||
+	    !hmark_parse(arg, strlen(arg), info, &cb->xflags))
+		xtables_error(PARAMETER_PROBLEM, "Bad type \"%s\"", arg);
+}
+
+static void HMARK_parse(struct xt_option_call *cb, int plen)
+{
+	struct xt_hmark_info *info = cb->data;
+
+	xtables_option_parse(cb);
+
+	switch (cb->entry->id) {
+	case O_HMARK_TYPE:
+		hmark_parse_type(cb);
+		break;
+	case O_HMARK_SADDR_MASK:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SADDR_MASK);
+		break;
+	case O_HMARK_DADDR_MASK:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_DADDR_MASK);
+		break;
+	case O_HMARK_SPI:
+		info->port_set.v32 = htonl(cb->val.u32);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPI);
+		break;
+	case O_HMARK_SPORT:
+		info->port_set.p16.src = htons(cb->val.u16);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPORT);
+		break;
+	case O_HMARK_DPORT:
+		info->port_set.p16.dst = htons(cb->val.u16);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_DPORT);
+		break;
+	case O_HMARK_SPORT_MASK:
+		info->port_mask.p16.src = htons(cb->val.u16);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPORT_MASK);
+		break;
+	case O_HMARK_DPORT_MASK:
+		info->port_mask.p16.dst = htons(cb->val.u16);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_DPORT_MASK);
+		break;
+	case O_HMARK_SPI_MASK:
+		info->port_mask.v32 = htonl(cb->val.u32);
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_SPI_MASK);
+		break;
+	case O_HMARK_PROTO_MASK:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_PROTO_MASK);
+		break;
+	case O_HMARK_RND:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_RND);
+		break;
+	case O_HMARK_MODULUS:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_MODULUS);
+		break;
+	case O_HMARK_OFFSET:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_OFFSET);
+		break;
+	case O_HMARK_CT:
+		info->flags |= XT_HMARK_FLAG(XT_HMARK_CT);
+		break;
+	}
+	cb->xflags |= (1 << cb->entry->id);
+}
+
+static void HMARK_ip4_parse(struct xt_option_call *cb)
+{
+	HMARK_parse(cb, 32);
+}
+static void HMARK_ip6_parse(struct xt_option_call *cb)
+{
+	HMARK_parse(cb, 128);
+}
+
+static void HMARK_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & (1 << O_HMARK_MODULUS)))
+		xtables_error(PARAMETER_PROBLEM, "--hmark-mod is mandatory");
+	if (!(cb->xflags & (1 << O_HMARK_RND)))
+		xtables_error(PARAMETER_PROBLEM, "--hmark-rnd is mandatory");
+	if (cb->xflags & (1 << O_HMARK_SPI_MASK) &&
+	    (cb->xflags & ((1 << O_HMARK_SPORT_MASK) |
+			   (1 << O_HMARK_DPORT_MASK))))
+		xtables_error(PARAMETER_PROBLEM, "you cannot use "
+				"--hmark-spi-mask and --hmark-?port-mask,"
+				"at the same time");
+	if (!((cb->xflags & HMARK_OPT_PKT_MASK) ||
+	       cb->xflags & (1 << O_HMARK_CT)))
+		xtables_error(PARAMETER_PROBLEM, "you have to specify "
+				"--hmark-tuple at least");
+}
+
+static void HMARK_print(const struct xt_hmark_info *info)
+{
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPORT_MASK))
+		printf("sport-mask 0x%x ", htons(info->port_mask.p16.src));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DPORT_MASK))
+		printf("dport-mask 0x%x ", htons(info->port_mask.p16.dst));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK))
+		printf("spi-mask 0x%x ", htonl(info->port_mask.v32));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPORT))
+		printf("sport 0x%x ", htons(info->port_set.p16.src));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DPORT))
+		printf("dport 0x%x ", htons(info->port_set.p16.dst));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI))
+		printf("spi 0x%x ", htonl(info->port_set.v32));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_PROTO_MASK))
+		printf("proto-mask 0x%x ", info->proto_mask);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_RND))
+		printf("rnd 0x%x ", info->hashrnd);
+}
+
+static void HMARK_ip6_print(const void *ip,
+			    const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_hmark_info *info =
+			(const struct xt_hmark_info *)target->data;
+
+	printf(" HMARK ");
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_MODULUS))
+		printf("mod %u ", info->hmodulus);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_OFFSET))
+		printf("+ 0x%x ", info->hoffset);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT))
+		printf("ct, ");
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SADDR_MASK))
+		printf("src-prefix %s ",
+		       xtables_ip6mask_to_numeric(&info->src_mask.in6) + 1);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DADDR_MASK))
+		printf("dst-prefix %s ",
+		       xtables_ip6mask_to_numeric(&info->dst_mask.in6) + 1);
+	HMARK_print(info);
+}
+static void HMARK_ip4_print(const void *ip,
+			    const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_hmark_info *info =
+		(const struct xt_hmark_info *)target->data;
+
+	printf(" HMARK ");
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_MODULUS))
+		printf("mod %u ", info->hmodulus);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_OFFSET))
+		printf("+ 0x%x ", info->hoffset);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT))
+		printf("ct, ");
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SADDR_MASK))
+		printf("src-prefix %u ",
+		       xtables_ipmask_to_cidr(&info->src_mask.in));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DADDR_MASK))
+		printf("dst-prefix %u ",
+		       xtables_ipmask_to_cidr(&info->dst_mask.in));
+	HMARK_print(info);
+}
+
+static void HMARK_save(const struct xt_hmark_info *info)
+{
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPORT_MASK))
+		printf(" --hmark-sport-mask 0x%04x",
+		       htons(info->port_mask.p16.src));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DPORT_MASK))
+		printf(" --hmark-dport-mask 0x%04x",
+		       htons(info->port_mask.p16.dst));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK))
+		printf(" --hmark-spi-mask 0x%08x",
+		       htonl(info->port_mask.v32));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPORT))
+		printf(" --hmark-sport 0x%04x",
+		       htons(info->port_set.p16.src));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DPORT))
+		printf(" --hmark-dport 0x%04x",
+		       htons(info->port_set.p16.dst));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI))
+		printf(" --hmark-spi 0x%08x", htonl(info->port_set.v32));
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_PROTO_MASK))
+		printf(" --hmark-proto-mask 0x%02x", info->proto_mask);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_RND))
+		printf(" --hmark-rnd 0x%08x", info->hashrnd);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_MODULUS))
+		printf(" --hmark-mod %u", info->hmodulus);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_OFFSET))
+		printf(" --hmark-offset %u", info->hoffset);
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT))
+		printf(" --hmark-tuple ct");
+}
+
+static void HMARK_ip6_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_hmark_info *info =
+		(const struct xt_hmark_info *)target->data;
+	int ret;
+
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SADDR_MASK)) {
+		ret = xtables_ip6mask_to_cidr(&info->src_mask.in6);
+		printf(" --hmark-src-prefix %d", ret);
+	}
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DADDR_MASK)) {
+		ret = xtables_ip6mask_to_cidr(&info->dst_mask.in6);
+		printf(" --hmark-dst-prefix %d", ret);
+	}
+	HMARK_save(info);
+}
+
+static void HMARK_ip4_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_hmark_info *info =
+		(const struct xt_hmark_info *)target->data;
+	int ret;
+
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_SADDR_MASK)) {
+		ret = xtables_ipmask_to_cidr(&info->src_mask.in);
+		printf(" --hmark-src-prefix %d", ret);
+	}
+	if (info->flags & XT_HMARK_FLAG(XT_HMARK_DADDR_MASK)) {
+		ret = xtables_ipmask_to_cidr(&info->dst_mask.in);
+		printf(" --hmark-dst-prefix %d", ret);
+	}
+	HMARK_save(info);
+}
+
+static struct xtables_target mark_tg_reg[] = {
+	{
+		.family        = NFPROTO_IPV4,
+		.name	       = "HMARK",
+		.version       = XTABLES_VERSION,
+		.size	       = XT_ALIGN(sizeof(struct xt_hmark_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_hmark_info)),
+		.help	       = HMARK_help,
+		.print	       = HMARK_ip4_print,
+		.save	       = HMARK_ip4_save,
+		.x6_parse      = HMARK_ip4_parse,
+		.x6_fcheck     = HMARK_check,
+		.x6_options    = HMARK_opts,
+	},
+	{
+		.family        = NFPROTO_IPV6,
+		.name	       = "HMARK",
+		.version       = XTABLES_VERSION,
+		.size	       = XT_ALIGN(sizeof(struct xt_hmark_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_hmark_info)),
+		.help	       = HMARK_help,
+		.print	       = HMARK_ip6_print,
+		.save	       = HMARK_ip6_save,
+		.x6_parse      = HMARK_ip6_parse,
+		.x6_fcheck     = HMARK_check,
+		.x6_options    = HMARK_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg));
+}
diff --git a/extensions/libxt_HMARK.man b/extensions/libxt_HMARK.man
new file mode 100644
index 0000000..e7b5426
--- /dev/null
+++ b/extensions/libxt_HMARK.man
@@ -0,0 +1,60 @@
+Like MARK, i.e. set the fwmark, but the mark is calculated from hashing
+packet selector at choice. You have also to specify the mark range and,
+optionally, the offset to start from. ICMP error messages are inspected
+and used to calculate the hashing.
+.PP
+Existing options are:
+.TP
+\fB\-\-hmark\-tuple\fP tuple\fI\fP
+Possible tuple members are:
+.B src
+meaning source address (IPv4, IPv6 address),
+.B dst
+meaning destination address (IPv4, IPv6 address),
+.B sport
+meaning source port (TCP, UDP, UDPlite, SCTP, DCCP),
+.B dport
+meaning destination port (TCP, UDP, UDPlite, SCTP, DCCP),
+.B spi
+meaning Security Parameter Index (AH, ESP), and
+.B ct
+meaning the usage of the conntrack tuple instead of the packet selectors.
+.TP
+\fB\-\-hmark\-mod\fP \fIvalue (must be > 0)\fP
+Modulus for hash calculation (to limit the range of possible marks)
+.TP
+\fB\-\-hmark\-offset\fP \fIvalue\fP
+Offset to start marks from.
+.TP
+For advanced usage, instead of using \-\-hmark\-tuple, you can specify custom
+prefixes and masks:
+.TP
+\fB\-\-hmark\-src\-prefix\fP \fIcidr\fP
+The source address mask in CIDR notation.
+.TP
+\fB\-\-hmark\-dst\-prefix\fP \fIcidr\fP
+The destination address mask in CIDR notation.
+.TP
+\fB\-\-hmark\-sport\-mask\fP \fIvalue\fP
+A 16 bit source port mask in hexadecimal.
+.TP
+\fB\-\-hmark\-dport\-mask\fP \fIvalue\fP
+A 16 bit destination port mask in hexadecimal.
+.TP
+\fB\-\-hmark\-spi\-mask\fP \fIvalue\fP
+A 32 bit field with spi mask.
+.TP
+\fB\-\-hmark\-proto\-mask\fP \fIvalue\fP
+An 8 bit field with layer 4 protocol number.
+.TP
+\fB\-\-hmark\-rnd\fP \fIvalue\fP
+A 32 bit random custom value to feed hash calculation.
+.PP
+\fIExamples:\fP
+.PP
+iptables \-t mangle \-A PREROUTING \-m conntrack \-\-ctstate NEW
+ \-j HMARK \-\-hmark-tuple ct,src,dst,proto \-\-hmark-offset 10000
+\-\-hmark\-mod 10 \-\-hmark\-rnd 0xfeedcafe
+.PP
+iptables \-t mangle \-A PREROUTING -j HMARK \-\-hmark\-offset 10000
+\-\-hmark-tuple src,dst,proto \-\-hmark-mod 10 \-\-hmark\-rnd 0xdeafbeef
diff --git a/extensions/libxt_LED.c b/extensions/libxt_LED.c
index 9d68fa2..8622c37 100644
--- a/extensions/libxt_LED.c
+++ b/extensions/libxt_LED.c
@@ -59,8 +59,10 @@
 	case O_LED_DELAY:
 		if (strncasecmp(cb->arg, "inf", 3) == 0)
 			led->delay = -1;
-		else
-			led->delay = strtoul(cb->arg, NULL, 0);
+		else if (!xtables_strtoui(cb->arg, NULL, &led->delay, 0, UINT32_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				"Delay value must be within range 0..%u",
+				UINT32_MAX);
 		break;
 	case O_LED_ALWAYS_BLINK:
 		led->always_blink = 1;
diff --git a/extensions/libip6t_LOG.man b/extensions/libxt_LOG.man
similarity index 72%
rename from extensions/libip6t_LOG.man
rename to extensions/libxt_LOG.man
index b7803fe..6d3a83a 100644
--- a/extensions/libip6t_LOG.man
+++ b/extensions/libxt_LOG.man
@@ -1,9 +1,9 @@
 Turn on kernel logging of matching packets.  When this option is set
 for a rule, the Linux kernel will print some information on all
-matching packets (like most IPv6 IPv6-header fields) via the kernel log
+matching packets (like most IP/IPv6 header fields) via the kernel log
 (where it can be read with
 .I dmesg
-or 
+or
 .IR syslogd (8)).
 This is a "non-terminating target", i.e. rule traversal continues at
 the next rule.  So if you want to LOG the packets you refuse, use two
@@ -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,
@@ -25,7 +28,7 @@
 Log options from the TCP packet header.
 .TP
 \fB\-\-log\-ip\-options\fP
-Log options from the IPv6 packet header.
+Log options from the IP/IPv6 packet header.
 .TP
 \fB\-\-log\-uid\fP
 Log the userid of the process which generated the packet.
diff --git a/extensions/libipt_MASQUERADE.man b/extensions/libxt_MASQUERADE.man
similarity index 84%
rename from extensions/libipt_MASQUERADE.man
rename to extensions/libxt_MASQUERADE.man
index 2dae964..c9e3950 100644
--- a/extensions/libipt_MASQUERADE.man
+++ b/extensions/libxt_MASQUERADE.man
@@ -16,15 +16,13 @@
 This specifies a range of source ports to use, overriding the default
 .B SNAT
 source port-selection heuristics (see above).  This is only valid
-if the rule also specifies
-\fB\-p tcp\fP
-or
-\fB\-p udp\fP.
+if the rule also specifies one of the following protocols:
+\fBtcp\fP, \fBudp\fP, \fBdccp\fP or \fBsctp\fP.
 .TP
 \fB\-\-random\fP
 Randomize source port mapping
 If option
 \fB\-\-random\fP
 is used then port mapping will be randomized (kernel >= 2.6.21).
-.RS
-.PP
+.TP
+IPv6 support available since Linux kernels >= 3.7.
diff --git a/extensions/libipt_NETMAP.man b/extensions/libxt_NETMAP.man
similarity index 88%
rename from extensions/libipt_NETMAP.man
rename to extensions/libxt_NETMAP.man
index a7e90b8..06507db 100644
--- a/extensions/libipt_NETMAP.man
+++ b/extensions/libxt_NETMAP.man
@@ -7,3 +7,5 @@
 Network address to map to.  The resulting address will be constructed in the
 following way: All 'one' bits in the mask are filled in from the new `address'.
 All bits that are zero in the mask are filled in from the original address.
+.TP
+IPv6 support available since Linux kernels >= 3.7.
diff --git a/extensions/libxt_NFLOG.man b/extensions/libxt_NFLOG.man
index 66f0b97..1b6dbf1 100644
--- a/extensions/libxt_NFLOG.man
+++ b/extensions/libxt_NFLOG.man
@@ -9,7 +9,7 @@
 non-terminating target, i.e. rule traversal continues at the next rule.
 .TP
 \fB\-\-nflog\-group\fP \fInlgroup\fP
-The netlink group (1 \- 2^32\-1) to which packets are (only applicable for
+The netlink group (0 - 2^16\-1) to which packets are (only applicable for
 nfnetlink_log). The default value is 0.
 .TP
 \fB\-\-nflog\-prefix\fP \fIprefix\fP
diff --git a/extensions/libxt_NFQUEUE.c b/extensions/libxt_NFQUEUE.c
index e47b586..0c86918 100644
--- a/extensions/libxt_NFQUEUE.c
+++ b/extensions/libxt_NFQUEUE.c
@@ -13,8 +13,10 @@
 	O_QUEUE_NUM = 0,
 	O_QUEUE_BALANCE,
 	O_QUEUE_BYPASS,
+	O_QUEUE_CPU_FANOUT,
 	F_QUEUE_NUM     = 1 << O_QUEUE_NUM,
 	F_QUEUE_BALANCE = 1 << O_QUEUE_BALANCE,
+	F_QUEUE_CPU_FANOUT = 1 << O_QUEUE_CPU_FANOUT,
 };
 
 static void NFQUEUE_help(void)
@@ -37,7 +39,15 @@
 {
 	NFQUEUE_help_v1();
 	printf(
-"  --queue-bypass		Bypass Queueing if no queue instance exists.\n");
+"  --queue-bypass		Bypass Queueing if no queue instance exists.\n"
+"  --queue-cpu-fanout	Use current CPU (no hashing)\n");
+}
+
+static void NFQUEUE_help_v3(void)
+{
+	NFQUEUE_help_v2();
+	printf(
+"  --queue-cpu-fanout	Use current CPU (no hashing)\n");
 }
 
 #define s struct xt_NFQ_info
@@ -48,6 +58,8 @@
 	{.name = "queue-balance", .id = O_QUEUE_BALANCE,
 	 .type = XTTYPE_UINT16RC, .excl = F_QUEUE_NUM},
 	{.name = "queue-bypass", .id = O_QUEUE_BYPASS, .type = XTTYPE_NONE},
+	{.name = "queue-cpu-fanout", .id = O_QUEUE_CPU_FANOUT,
+	 .type = XTTYPE_NONE, .also = F_QUEUE_BALANCE},
 	XTOPT_TABLEEND,
 };
 #undef s
@@ -92,6 +104,18 @@
 	}
 }
 
+static void NFQUEUE_parse_v3(struct xt_option_call *cb)
+{
+	struct xt_NFQ_info_v3 *info = cb->data;
+
+	NFQUEUE_parse_v2(cb);
+	switch (cb->entry->id) {
+	case O_QUEUE_CPU_FANOUT:
+		info->flags |= NFQ_FLAG_CPU_FANOUT;
+		break;
+	}
+}
+
 static void NFQUEUE_print(const void *ip,
                           const struct xt_entry_target *target, int numeric)
 {
@@ -120,10 +144,20 @@
 	const struct xt_NFQ_info_v2 *info = (void *) target->data;
 
 	NFQUEUE_print_v1(ip, target, numeric);
-	if (info->bypass)
+	if (info->bypass & NFQ_FLAG_BYPASS)
 		printf(" bypass");
 }
 
+static void NFQUEUE_print_v3(const void *ip,
+                             const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_NFQ_info_v3 *info = (void *)target->data;
+
+	NFQUEUE_print_v2(ip, target, numeric);
+	if (info->flags & NFQ_FLAG_CPU_FANOUT)
+		printf(" cpu-fanout");
+}
+
 static void NFQUEUE_save(const void *ip, const struct xt_entry_target *target)
 {
 	const struct xt_NFQ_info *tinfo =
@@ -151,8 +185,18 @@
 
 	NFQUEUE_save_v1(ip, target);
 
-	if (info->bypass)
-		printf("--queue-bypass ");
+	if (info->bypass & NFQ_FLAG_BYPASS)
+		printf(" --queue-bypass");
+}
+
+static void NFQUEUE_save_v3(const void *ip,
+			    const struct xt_entry_target *target)
+{
+	const struct xt_NFQ_info_v3 *info = (void *)target->data;
+
+	NFQUEUE_save_v2(ip, target);
+	if (info->flags & NFQ_FLAG_CPU_FANOUT)
+		printf(" --queue-cpu-fanout");
 }
 
 static void NFQUEUE_init_v1(struct xt_entry_target *t)
@@ -199,6 +243,19 @@
 	.save		= NFQUEUE_save_v2,
 	.x6_parse	= NFQUEUE_parse_v2,
 	.x6_options	= NFQUEUE_opts,
+},{
+	.family		= NFPROTO_UNSPEC,
+	.revision	= 3,
+	.name		= "NFQUEUE",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_NFQ_info_v3)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_NFQ_info_v3)),
+	.help		= NFQUEUE_help_v3,
+	.init		= NFQUEUE_init_v1,
+	.print		= NFQUEUE_print_v3,
+	.save		= NFQUEUE_save_v3,
+	.x6_parse	= NFQUEUE_parse_v3,
+	.x6_options	= NFQUEUE_opts,
 }
 };
 
diff --git a/extensions/libxt_NFQUEUE.man b/extensions/libxt_NFQUEUE.man
index 910e386..7a99129 100644
--- a/extensions/libxt_NFQUEUE.man
+++ b/extensions/libxt_NFQUEUE.man
@@ -21,5 +21,12 @@
 .TP
 \fB\-\-queue\-bypass\fP
 By default, if no userspace program is listening on an NFQUEUE, then all packets that are to be queued
-are dropped.  When this option is used, the NFQUEUE rule is silently bypassed instead. The packet
-will move on to the next rule.
+are dropped.  When this option is used, the NFQUEUE rule behaves like ACCEPT instead, and the packet
+will move on to the next table.
+.PP
+.TP
+\fB\-\-queue\-cpu-fanout\fP
+Available starting Linux kernel 3.10. When used together with
+\fB--queue-balance\fP this will use the CPU ID as an index to map packets to
+the queues. The idea is that you can improve performance if there's a queue
+per CPU. This requires \fB--queue-balance\fP to be specified.
diff --git a/extensions/libxt_NOTRACK.c b/extensions/libxt_NOTRACK.c
deleted file mode 100644
index ca58700..0000000
--- a/extensions/libxt_NOTRACK.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Shared library add-on to iptables to add NOTRACK target support. */
-#include <xtables.h>
-
-static struct xtables_target notrack_target = {
-	.family		= NFPROTO_UNSPEC,
-	.name		= "NOTRACK",
-	.version	= XTABLES_VERSION,
-	.size		= XT_ALIGN(0),
-	.userspacesize	= XT_ALIGN(0),
-};
-
-void _init(void)
-{
-	xtables_register_target(&notrack_target);
-}
diff --git a/extensions/libxt_NOTRACK.man b/extensions/libxt_NOTRACK.man
index c2cdf5a..4302b93 100644
--- a/extensions/libxt_NOTRACK.man
+++ b/extensions/libxt_NOTRACK.man
@@ -1,5 +1,3 @@
-This target disables connection tracking for all packets matching that rule.
-.PP
-It can only be used in the
-.B raw
-table.
+This extension disables connection tracking for all packets matching that rule.
+It is equivalent with \-j CT \-\-notrack. Like CT, NOTRACK can only be used in
+the \fBraw\fP table.
diff --git a/extensions/libxt_RATEEST.c b/extensions/libxt_RATEEST.c
index 6369e9e..449ceab 100644
--- a/extensions/libxt_RATEEST.c
+++ b/extensions/libxt_RATEEST.c
@@ -1,19 +1,20 @@
-#include <stdbool.h>
+/*
+ * Copyright (c) 2008-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <stddef.h>
-#include <getopt.h>
 #include <math.h>
 
 #include <xtables.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_RATEEST.h>
 
-/* hack to pass raw values to final_check */
-static struct xt_rateest_target_info *RATEEST_info;
-static unsigned int interval;
-static unsigned int ewma_log;
+struct rateest_tg_udata {
+	unsigned int interval;
+	unsigned int ewma_log;
+};
 
 static void
 RATEEST_help(void)
@@ -25,18 +26,23 @@
 "  --rateest-ewmalog value	Rate measurement averaging time constant\n");
 }
 
-enum RATEEST_options {
-	RATEEST_OPT_NAME,
-	RATEEST_OPT_INTERVAL,
-	RATEEST_OPT_EWMALOG,
+enum {
+	O_NAME = 0,
+	O_INTERVAL,
+	O_EWMALOG,
 };
 
-static const struct option RATEEST_opts[] = {
-	{.name = "rateest-name",     .has_arg = true, .val = RATEEST_OPT_NAME},
-	{.name = "rateest-interval", .has_arg = true, .val = RATEEST_OPT_INTERVAL},
-	{.name = "rateest-ewmalog",  .has_arg = true, .val = RATEEST_OPT_EWMALOG},
-	XT_GETOPT_TABLEEND,
+#define s struct xt_rateest_target_info
+static const struct xt_option_entry RATEEST_opts[] = {
+	{.name = "rateest-name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name)},
+	{.name = "rateest-interval", .id = O_INTERVAL, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	{.name = "rateest-ewmalog", .id = O_EWMALOG, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	XTOPT_TABLEEND,
 };
+#undef s
 
 /* Copied from iproute */
 #define TIME_UNITS_PER_SEC	1000000
@@ -82,66 +88,34 @@
 		printf(" %uus", time);
 }
 
-static int
-RATEEST_parse(int c, char **argv, int invert, unsigned int *flags,
-	      const void *entry, struct xt_entry_target **target)
+static void RATEEST_parse(struct xt_option_call *cb)
 {
-	struct xt_rateest_target_info *info = (void *)(*target)->data;
+	struct rateest_tg_udata *udata = cb->udata;
 
-	RATEEST_info = info;
-
-	switch (c) {
-	case RATEEST_OPT_NAME:
-		if (*flags & (1 << c))
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_INTERVAL:
+		if (RATEEST_get_time(&udata->interval, cb->arg) < 0)
 			xtables_error(PARAMETER_PROBLEM,
-				   "RATEEST: can't specify --rateest-name twice");
-		*flags |= 1 << c;
-
-		strncpy(info->name, optarg, sizeof(info->name) - 1);
+				   "RATEEST: bad interval value \"%s\"",
+				   cb->arg);
 		break;
-
-	case RATEEST_OPT_INTERVAL:
-		if (*flags & (1 << c))
+	case O_EWMALOG:
+		if (RATEEST_get_time(&udata->ewma_log, cb->arg) < 0)
 			xtables_error(PARAMETER_PROBLEM,
-				   "RATEEST: can't specify --rateest-interval twice");
-		*flags |= 1 << c;
-
-		if (RATEEST_get_time(&interval, optarg) < 0)
-			xtables_error(PARAMETER_PROBLEM,
-				   "RATEEST: bad interval value `%s'", optarg);
-
-		break;
-
-	case RATEEST_OPT_EWMALOG:
-		if (*flags & (1 << c))
-			xtables_error(PARAMETER_PROBLEM,
-				   "RATEEST: can't specify --rateest-ewmalog twice");
-		*flags |= 1 << c;
-
-		if (RATEEST_get_time(&ewma_log, optarg) < 0)
-			xtables_error(PARAMETER_PROBLEM,
-				   "RATEEST: bad ewmalog value `%s'", optarg);
-
+				   "RATEEST: bad ewmalog value \"%s\"",
+				   cb->arg);
 		break;
 	}
-
-	return 1;
 }
 
-static void
-RATEEST_final_check(unsigned int flags)
+static void RATEEST_final_check(struct xt_fcheck_call *cb)
 {
-	struct xt_rateest_target_info *info = RATEEST_info;
-
-	if (!(flags & (1 << RATEEST_OPT_NAME)))
-		xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified");
-	if (!(flags & (1 << RATEEST_OPT_INTERVAL)))
-		xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified");
-	if (!(flags & (1 << RATEEST_OPT_EWMALOG)))
-		xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified");
+	struct xt_rateest_target_info *info = cb->data;
+	struct rateest_tg_udata *udata = cb->udata;
 
 	for (info->interval = 0; info->interval <= 5; info->interval++) {
-		if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
+		if (udata->interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
 			break;
 	}
 
@@ -152,7 +126,7 @@
 
 	for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) {
 		double w = 1.0 - 1.0 / (1 << info->ewma_log);
-		if (interval / (-log(w)) > ewma_log)
+		if (udata->interval / (-log(w)) > udata->ewma_log)
 			break;
 	}
 	info->ewma_log--;
@@ -197,13 +171,14 @@
 	.name		= "RATEEST",
 	.version	= XTABLES_VERSION,
 	.size		= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
-	.userspacesize	= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
+	.userspacesize	= offsetof(struct xt_rateest_target_info, est),
 	.help		= RATEEST_help,
-	.parse		= RATEEST_parse,
-	.final_check	= RATEEST_final_check,
+	.x6_parse	= RATEEST_parse,
+	.x6_fcheck	= RATEEST_final_check,
 	.print		= RATEEST_print,
 	.save		= RATEEST_save,
-	.extra_opts	= RATEEST_opts,
+	.x6_options	= RATEEST_opts,
+	.udata_size	= sizeof(struct rateest_tg_udata),
 };
 
 void _init(void)
diff --git a/extensions/libipt_REDIRECT.man b/extensions/libxt_REDIRECT.man
similarity index 68%
rename from extensions/libipt_REDIRECT.man
rename to extensions/libxt_REDIRECT.man
index 90ab19d..3400a6d 100644
--- a/extensions/libipt_REDIRECT.man
+++ b/extensions/libxt_REDIRECT.man
@@ -7,19 +7,18 @@
 chains, and user-defined chains which are only called from those
 chains.  It redirects the packet to the machine itself by changing the
 destination IP to the primary address of the incoming interface
-(locally-generated packets are mapped to the 127.0.0.1 address).
+(locally-generated packets are mapped to the localhost address,
+127.0.0.1 for IPv4 and ::1 for IPv6).
 .TP
 \fB\-\-to\-ports\fP \fIport\fP[\fB\-\fP\fIport\fP]
 This specifies a destination port or range of ports to use: without
 this, the destination port is never altered.  This is only valid
-if the rule also specifies
-\fB\-p tcp\fP
-or
-\fB\-p udp\fP.
+if the rule also specifies one of the following protocols:
+\fBtcp\fP, \fBudp\fP, \fBdccp\fP or \fBsctp\fP.
 .TP
 \fB\-\-random\fP
 If option
 \fB\-\-random\fP
 is used then port mapping will be randomized (kernel >= 2.6.22).
-.RS
-.PP
+.TP
+IPv6 support available starting Linux kernels >= 3.7.
diff --git a/extensions/libxt_SET.c b/extensions/libxt_SET.c
index 51c0cec..a11db39 100644
--- a/extensions/libxt_SET.c
+++ b/extensions/libxt_SET.c
@@ -67,10 +67,6 @@
 		xtables_error(PARAMETER_PROBLEM,
 			      "--%s can be specified only once", what);
 
-	if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
-		xtables_error(PARAMETER_PROBLEM,
-			      "Unexpected `!' after --%s", what);
-
 	if (!argv[optind]
 	    || argv[optind][0] == '-' || argv[optind][0] == '!')
 		xtables_error(PARAMETER_PROBLEM,
@@ -147,9 +143,6 @@
 }
 
 /* Revision 1 */
-
-#define set_target_help_v1	set_target_help_v0
-
 static void
 set_target_init_v1(struct xt_entry_target *target)
 {
@@ -173,11 +166,6 @@
 	if (info->dim)
 		xtables_error(PARAMETER_PROBLEM,
 			      "--%s can be specified only once", what);
-
-	if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
-		xtables_error(PARAMETER_PROBLEM,
-			      "Unexpected `!' after --%s", what);
-
 	if (!argv[optind]
 	    || argv[optind][0] == '-' || argv[optind][0] == '!')
 		xtables_error(PARAMETER_PROBLEM,
@@ -213,8 +201,6 @@
 	return 1;
 }
 
-#define set_target_check_v1	set_target_check_v0
-
 static void
 print_target(const char *prefix, const struct xt_set_info *info)
 {
@@ -251,8 +237,6 @@
 	print_target("--del-set", &info->del_set);
 }
 
-#define set_target_opts_v1	set_target_opts_v0
-
 /* Revision 2 */
 
 static void
@@ -385,13 +369,13 @@
 		.family		= NFPROTO_UNSPEC,
 		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v1)),
 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v1)),
-		.help		= set_target_help_v1,
+		.help		= set_target_help_v0,
 		.init		= set_target_init_v1,
 		.parse		= set_target_parse_v1,
-		.final_check	= set_target_check_v1,
+		.final_check	= set_target_check_v0,
 		.print		= set_target_print_v1,
 		.save		= set_target_save_v1,
-		.extra_opts	= set_target_opts_v1,
+		.extra_opts	= set_target_opts_v0,
 	},
 	{
 		.name		= "SET",
diff --git a/extensions/libxt_SET.man b/extensions/libxt_SET.man
index 739be41..c35ba93 100644
--- a/extensions/libxt_SET.man
+++ b/extensions/libxt_SET.man
@@ -1,26 +1,25 @@
-This modules adds and/or deletes entries from IP sets which can be defined 
+This module adds and/or deletes entries from IP sets which can be defined
 by ipset(8).
 .TP
 \fB\-\-add\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...]
-add the address(es)/port(s) of the packet to the sets
+add the address(es)/port(s) of the packet to the set
 .TP
 \fB\-\-del\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...]
-delete the address(es)/port(s) of the packet from the sets
+delete the address(es)/port(s) of the packet from the set
 .IP
-where flags are
+where \fIflag\fP(s) are
 .BR "src"
 and/or
 .BR "dst"
 specifications and there can be no more than six of them.
 .TP
 \fB\-\-timeout\fP \fIvalue\fP
-when adding entry, the timeout value to use instead of the default
+when adding an entry, the timeout value to use instead of the default
 one from the set definition
 .TP
 \fB\-\-exist\fP
-when adding entry if it already exists, reset the timeout value
+when adding an entry if it already exists, reset the timeout value
 to the specified one or to the default from the set definition
 .PP
-Use of -j SET requires that ipset kernel support is provided. As standard
-kernels do not ship this currently, the ipset or Xtables-addons package needs
-to be installed.
+Use of -j SET requires that ipset kernel support is provided, which, for
+standard kernels, is the case since Linux 2.6.39.
diff --git a/extensions/libipt_SNAT.man b/extensions/libxt_SNAT.man
similarity index 70%
rename from extensions/libipt_SNAT.man
rename to extensions/libxt_SNAT.man
index 626b592..f0620a2 100644
--- a/extensions/libipt_SNAT.man
+++ b/extensions/libxt_SNAT.man
@@ -2,23 +2,23 @@
 .B nat
 table, in the
 .B POSTROUTING
-chain.  It specifies that the source address of the packet should be
+and
+.B INPUT
+chains, and user-defined chains which are only called from those
+chains.  It specifies that the source address of the packet should be
 modified (and all future packets in this connection will also be
-mangled), and rules should cease being examined.  It takes one type
-of option:
+mangled), and rules should cease being examined.  It takes the
+following options:
 .TP
 \fB\-\-to\-source\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]]
 which can specify a single new source IP address, an inclusive range
-of IP addresses, and optionally, a port range (which is only valid if
-the rule also specifies
-\fB\-p tcp\fP
-or
-\fB\-p udp\fP).
+of IP addresses. Optionally a port range,
+if the rule also specifies one of the following protocols:
+\fBtcp\fP, \fBudp\fP, \fBdccp\fP or \fBsctp\fP.
 If no port range is specified, then source ports below 512 will be
 mapped to other ports below 512: those between 512 and 1023 inclusive
 will be mapped to ports below 1024, and other ports will be mapped to
 1024 or above. Where possible, no port alteration will occur.
-
 In Kernels up to 2.6.10, you can add several \-\-to\-source options. For those
 kernels, if you specify more than one source address, either via an address
 range or multiple \-\-to\-source options, a simple round-robin (one after another
@@ -35,3 +35,11 @@
 Gives a client the same source-/destination-address for each connection.
 This supersedes the SAME target. Support for persistent mappings is available
 from 2.6.29-rc2.
+.PP
+Kernels prior to 2.6.36-rc1 don't have the ability to
+.B SNAT
+in the
+.B INPUT
+chain.
+.TP
+IPv6 support available since Linux kernels >= 3.7.
diff --git a/extensions/libxt_TCPMSS.c b/extensions/libxt_TCPMSS.c
index 2266326..4b71e44 100644
--- a/extensions/libxt_TCPMSS.c
+++ b/extensions/libxt_TCPMSS.c
@@ -2,10 +2,10 @@
  *
  * Copyright (c) 2000 Marc Boucher
 */
+#include "config.h"
 #include <stdio.h>
 #include <xtables.h>
 #include <netinet/ip.h>
-#include <netinet/ip6.h>
 #include <linux/netfilter/xt_TCPMSS.h>
 
 enum {
@@ -34,7 +34,7 @@
 
 static void TCPMSS_help6(void)
 {
-	__TCPMSS_help(sizeof(struct ip6_hdr));
+	__TCPMSS_help(SIZEOF_STRUCT_IP6_HDR);
 }
 
 static const struct xt_option_entry TCPMSS4_opts[] = {
@@ -47,7 +47,7 @@
 
 static const struct xt_option_entry TCPMSS6_opts[] = {
 	{.name = "set-mss", .id = O_SET_MSS, .type = XTTYPE_UINT16,
-	 .min = 0, .max = UINT16_MAX - sizeof(struct ip6_hdr),
+	 .min = 0, .max = UINT16_MAX - SIZEOF_STRUCT_IP6_HDR,
 	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_tcpmss_info, mss)},
 	{.name = "clamp-mss-to-pmtu", .id = O_CLAMP_MSS, .type = XTTYPE_NONE},
 	XTOPT_TABLEEND,
@@ -91,36 +91,36 @@
 		printf(" --set-mss %u", mssinfo->mss);
 }
 
-static struct xtables_target tcpmss_target = {
-	.family		= NFPROTO_IPV4,
-	.name		= "TCPMSS",
-	.version	= XTABLES_VERSION,
-	.size		= XT_ALIGN(sizeof(struct xt_tcpmss_info)),
-	.userspacesize	= XT_ALIGN(sizeof(struct xt_tcpmss_info)),
-	.help		= TCPMSS_help,
-	.print		= TCPMSS_print,
-	.save		= TCPMSS_save,
-	.x6_parse	= TCPMSS_parse,
-	.x6_fcheck	= TCPMSS_check,
-	.x6_options	= TCPMSS4_opts,
-};
-
-static struct xtables_target tcpmss_target6 = {
-	.family		= NFPROTO_IPV6,
-	.name		= "TCPMSS",
-	.version	= XTABLES_VERSION,
-	.size		= XT_ALIGN(sizeof(struct xt_tcpmss_info)),
-	.userspacesize	= XT_ALIGN(sizeof(struct xt_tcpmss_info)),
-	.help		= TCPMSS_help6,
-	.print		= TCPMSS_print,
-	.save		= TCPMSS_save,
-	.x6_parse	= TCPMSS_parse,
-	.x6_fcheck	= TCPMSS_check,
-	.x6_options	= TCPMSS6_opts,
+static struct xtables_target tcpmss_tg_reg[] = {
+	{
+		.family        = NFPROTO_IPV4,
+		.name          = "TCPMSS",
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_tcpmss_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tcpmss_info)),
+		.help          = TCPMSS_help,
+		.print         = TCPMSS_print,
+		.save          = TCPMSS_save,
+		.x6_parse      = TCPMSS_parse,
+		.x6_fcheck     = TCPMSS_check,
+		.x6_options    = TCPMSS4_opts,
+	},
+	{
+		.family        = NFPROTO_IPV6,
+		.name          = "TCPMSS",
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_tcpmss_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tcpmss_info)),
+		.help          = TCPMSS_help6,
+		.print         = TCPMSS_print,
+		.save          = TCPMSS_save,
+		.x6_parse      = TCPMSS_parse,
+		.x6_fcheck     = TCPMSS_check,
+		.x6_options    = TCPMSS6_opts,
+	},
 };
 
 void _init(void)
 {
-	xtables_register_target(&tcpmss_target);
-	xtables_register_target(&tcpmss_target6);
+	xtables_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
 }
diff --git a/extensions/libxt_TEE.c b/extensions/libxt_TEE.c
index c89e580..92c7601 100644
--- a/extensions/libxt_TEE.c
+++ b/extensions/libxt_TEE.c
@@ -92,36 +92,36 @@
 		printf(" --oif %s", info->oif);
 }
 
-static struct xtables_target tee_tg_reg = {
-	.name          = "TEE",
-	.version       = XTABLES_VERSION,
-	.revision      = 1,
-	.family        = NFPROTO_IPV4,
-	.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
-	.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
-	.help          = tee_tg_help,
-	.print         = tee_tg_print,
-	.save          = tee_tg_save,
-	.x6_parse      = xtables_option_parse,
-	.x6_options    = tee_tg_opts,
-};
-
-static struct xtables_target tee_tg6_reg = {
-	.name          = "TEE",
-	.version       = XTABLES_VERSION,
-	.revision      = 1,
-	.family        = NFPROTO_IPV6,
-	.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
-	.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
-	.help          = tee_tg_help,
-	.print         = tee_tg6_print,
-	.save          = tee_tg6_save,
-	.x6_parse      = xtables_option_parse,
-	.x6_options    = tee_tg_opts,
+static struct xtables_target tee_tg_reg[] = {
+	{
+		.name          = "TEE",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
+		.help          = tee_tg_help,
+		.print         = tee_tg_print,
+		.save          = tee_tg_save,
+		.x6_parse      = xtables_option_parse,
+		.x6_options    = tee_tg_opts,
+	},
+	{
+		.name          = "TEE",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
+		.help          = tee_tg_help,
+		.print         = tee_tg6_print,
+		.save          = tee_tg6_save,
+		.x6_parse      = xtables_option_parse,
+		.x6_options    = tee_tg_opts,
+	},
 };
 
 void _init(void)
 {
-	xtables_register_target(&tee_tg_reg);
-	xtables_register_target(&tee_tg6_reg);
+	xtables_register_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
 }
diff --git a/extensions/libxt_TOS.man b/extensions/libxt_TOS.man
index 46f6737..58118ec 100644
--- a/extensions/libxt_TOS.man
+++ b/extensions/libxt_TOS.man
@@ -4,24 +4,33 @@
 \fBmangle\fP table.
 .TP
 \fB\-\-set\-tos\fP \fIvalue\fP[\fB/\fP\fImask\fP]
-Zeroes out the bits given by \fImask\fP and XORs \fIvalue\fP into the
-TOS/Priority field. If \fImask\fP is omitted, 0xFF is assumed.
+Zeroes out the bits given by \fImask\fP (see NOTE below) and XORs \fIvalue\fP
+into the TOS/Priority field. If \fImask\fP is omitted, 0xFF is assumed.
 .TP
 \fB\-\-set\-tos\fP \fIsymbol\fP
 You can specify a symbolic name when using the TOS target for IPv4. It implies
-a mask of 0xFF. The list of recognized TOS names can be obtained by calling
-iptables with \fB\-j TOS \-h\fP.
+a mask of 0xFF (see NOTE below). The list of recognized TOS names can be
+obtained by calling iptables with \fB\-j TOS \-h\fP.
 .PP
 The following mnemonics are available:
 .TP
 \fB\-\-and\-tos\fP \fIbits\fP
 Binary AND the TOS value with \fIbits\fP. (Mnemonic for \fB\-\-set\-tos
-0/\fP\fIinvbits\fP, where \fIinvbits\fP is the binary negation of \fIbits\fP.)
+0/\fP\fIinvbits\fP, where \fIinvbits\fP is the binary negation of \fIbits\fP.
+See NOTE below.)
 .TP
 \fB\-\-or\-tos\fP \fIbits\fP
 Binary OR the TOS value with \fIbits\fP. (Mnemonic for \fB\-\-set\-tos\fP
-\fIbits\fP\fB/\fP\fIbits\fP.)
+\fIbits\fP\fB/\fP\fIbits\fP. See NOTE below.)
 .TP
 \fB\-\-xor\-tos\fP \fIbits\fP
 Binary XOR the TOS value with \fIbits\fP. (Mnemonic for \fB\-\-set\-tos\fP
-\fIbits\fP\fB/0\fP.)
+\fIbits\fP\fB/0\fP. See NOTE below.)
+.PP
+NOTE: In Linux kernels up to and including 2.6.38, with the exception of
+longterm releases 2.6.32 (>=.42), 2.6.33 (>=.15), and 2.6.35 (>=.14), there is
+a bug whereby IPv6 TOS mangling does not behave as documented and differs from
+the IPv4 version. The TOS mask indicates the bits one wants to zero out, so it
+needs to be inverted before applying it to the original TOS field. However, the
+aformentioned kernels forgo the inversion which breaks --set-tos and its
+mnemonics.
diff --git a/extensions/libxt_TRACE.man b/extensions/libxt_TRACE.man
index ea0ce0f..8d590a5 100644
--- a/extensions/libxt_TRACE.man
+++ b/extensions/libxt_TRACE.man
@@ -1,4 +1,4 @@
-This target marks packes so that the kernel will log every rule which match 
+This target marks packets so that the kernel will log every rule which match 
 the packets as those traverse the tables, chains, rules.
 .PP
 A logging backend, such as ip(6)t_LOG or nfnetlink_log, must be loaded for this
diff --git a/extensions/libipt_addrtype.c b/extensions/libxt_addrtype.c
similarity index 81%
rename from extensions/libipt_addrtype.c
rename to extensions/libxt_addrtype.c
index 3dec626..e5d3033 100644
--- a/extensions/libipt_addrtype.c
+++ b/extensions/libxt_addrtype.c
@@ -1,10 +1,12 @@
 /* Shared library add-on to iptables to add addrtype matching support 
+ *
+ * Copyright (c) 2003-2013 Patrick McHardy <kaber@trash.net>
  * 
  * This program is released under the terms of GNU GPL */
 #include <stdio.h>
 #include <string.h>
 #include <xtables.h>
-#include <linux/netfilter_ipv4/ipt_addrtype.h>
+#include <linux/netfilter/xt_addrtype.h>
 
 enum {
 	O_SRC_TYPE = 0,
@@ -60,7 +62,7 @@
 " [!] --src-type type[,...]      Match source address type\n"
 " [!] --dst-type type[,...]      Match destination address type\n"
 "     --limit-iface-in           Match only on the packet's incoming device\n"
-"     --limit-iface-out          Match only on the packet's incoming device\n"
+"     --limit-iface-out          Match only on the packet's outgoing device\n"
 "\n"
 "Valid types:           \n");
 	addrtype_help_types();
@@ -98,7 +100,7 @@
 	
 static void addrtype_parse_v0(struct xt_option_call *cb)
 {
-	struct ipt_addrtype_info *info = cb->data;
+	struct xt_addrtype_info *info = cb->data;
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
@@ -117,25 +119,25 @@
 
 static void addrtype_parse_v1(struct xt_option_call *cb)
 {
-	struct ipt_addrtype_info_v1 *info = cb->data;
+	struct xt_addrtype_info_v1 *info = cb->data;
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_SRC_TYPE:
 		parse_types(cb->arg, &info->source);
 		if (cb->invert)
-			info->flags |= IPT_ADDRTYPE_INVERT_SOURCE;
+			info->flags |= XT_ADDRTYPE_INVERT_SOURCE;
 		break;
 	case O_DST_TYPE:
 		parse_types(cb->arg, &info->dest);
 		if (cb->invert)
-			info->flags |= IPT_ADDRTYPE_INVERT_DEST;
+			info->flags |= XT_ADDRTYPE_INVERT_DEST;
 		break;
 	case O_LIMIT_IFACE_IN:
-		info->flags |= IPT_ADDRTYPE_LIMIT_IFACE_IN;
+		info->flags |= XT_ADDRTYPE_LIMIT_IFACE_IN;
 		break;
 	case O_LIMIT_IFACE_OUT:
-		info->flags |= IPT_ADDRTYPE_LIMIT_IFACE_OUT;
+		info->flags |= XT_ADDRTYPE_LIMIT_IFACE_OUT;
 		break;
 	}
 }
@@ -162,8 +164,7 @@
 static void addrtype_print_v0(const void *ip, const struct xt_entry_match *match,
                               int numeric)
 {
-	const struct ipt_addrtype_info *info = 
-		(struct ipt_addrtype_info *) match->data;
+	const struct xt_addrtype_info *info = (const void *)match->data;
 
 	printf(" ADDRTYPE match");
 	if (info->source) {
@@ -183,34 +184,30 @@
 static void addrtype_print_v1(const void *ip, const struct xt_entry_match *match,
                               int numeric)
 {
-	const struct ipt_addrtype_info_v1 *info = 
-		(struct ipt_addrtype_info_v1 *) match->data;
+	const struct xt_addrtype_info_v1 *info = (const void *)match->data;
 
 	printf(" ADDRTYPE match");
 	if (info->source) {
 		printf(" src-type ");
-		if (info->flags & IPT_ADDRTYPE_INVERT_SOURCE)
+		if (info->flags & XT_ADDRTYPE_INVERT_SOURCE)
 			printf("!");
 		print_types(info->source);
 	}
 	if (info->dest) {
 		printf(" dst-type ");
-		if (info->flags & IPT_ADDRTYPE_INVERT_DEST)
+		if (info->flags & XT_ADDRTYPE_INVERT_DEST)
 			printf("!");
 		print_types(info->dest);
 	}
-	if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) {
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN)
 		printf(" limit-in");
-	}
-	if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) {
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
 		printf(" limit-out");
-	}
 }
 
 static void addrtype_save_v0(const void *ip, const struct xt_entry_match *match)
 {
-	const struct ipt_addrtype_info *info =
-		(struct ipt_addrtype_info *) match->data;
+	const struct xt_addrtype_info *info = (const void *)match->data;
 
 	if (info->source) {
 		if (info->invert_source)
@@ -228,27 +225,24 @@
 
 static void addrtype_save_v1(const void *ip, const struct xt_entry_match *match)
 {
-	const struct ipt_addrtype_info_v1 *info =
-		(struct ipt_addrtype_info_v1 *) match->data;
+	const struct xt_addrtype_info_v1 *info = (const void *)match->data;
 
 	if (info->source) {
-		if (info->flags & IPT_ADDRTYPE_INVERT_SOURCE)
+		if (info->flags & XT_ADDRTYPE_INVERT_SOURCE)
 			printf(" !");
 		printf(" --src-type ");
 		print_types(info->source);
 	}
 	if (info->dest) {
-		if (info->flags & IPT_ADDRTYPE_INVERT_DEST)
+		if (info->flags & XT_ADDRTYPE_INVERT_DEST)
 			printf(" !");
 		printf(" --dst-type ");
 		print_types(info->dest);
 	}
-	if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) {
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN)
 		printf(" --limit-iface-in");
-	}
-	if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) {
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
 		printf(" --limit-iface-out");
-	}
 }
 
 static const struct xt_option_entry addrtype_opts_v0[] = {
@@ -276,8 +270,8 @@
 		.name          = "addrtype",
 		.version       = XTABLES_VERSION,
 		.family        = NFPROTO_IPV4,
-		.size          = XT_ALIGN(sizeof(struct ipt_addrtype_info)),
-		.userspacesize = XT_ALIGN(sizeof(struct ipt_addrtype_info)),
+		.size          = XT_ALIGN(sizeof(struct xt_addrtype_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_addrtype_info)),
 		.help          = addrtype_help_v0,
 		.print         = addrtype_print_v0,
 		.save          = addrtype_save_v0,
@@ -289,9 +283,9 @@
 		.name          = "addrtype",
 		.revision      = 1,
 		.version       = XTABLES_VERSION,
-		.family        = NFPROTO_IPV4,
-		.size          = XT_ALIGN(sizeof(struct ipt_addrtype_info_v1)),
-		.userspacesize = XT_ALIGN(sizeof(struct ipt_addrtype_info_v1)),
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_addrtype_info_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_addrtype_info_v1)),
 		.help          = addrtype_help_v1,
 		.print         = addrtype_print_v1,
 		.save          = addrtype_save_v1,
diff --git a/extensions/libipt_addrtype.man b/extensions/libxt_addrtype.man
similarity index 100%
rename from extensions/libipt_addrtype.man
rename to extensions/libxt_addrtype.man
diff --git a/extensions/libxt_bpf.c b/extensions/libxt_bpf.c
new file mode 100644
index 0000000..dca97d7
--- /dev/null
+++ b/extensions/libxt_bpf.c
@@ -0,0 +1,152 @@
+/*
+ * Xtables BPF extension
+ *
+ * Written by Willem de Bruijn (willemb@google.com)
+ * Copyright Google, Inc. 2013
+ * Licensed under the GNU General Public License version 2 (GPLv2)
+*/
+
+#include <linux/netfilter/xt_bpf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <xtables.h>
+
+#define BCODE_FILE_MAX_LEN_B	1024
+
+enum {
+	O_BCODE_STDIN = 0,
+};
+
+static void bpf_help(void)
+{
+	printf(
+"bpf match options:\n"
+"--bytecode <program>	: a bpf program as generated by\n"
+"  `nfbpf_compiler RAW <filter>`\n");
+}
+
+static const struct xt_option_entry bpf_opts[] = {
+	{.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static void bpf_parse_string(struct xt_option_call *cb, const char *bpf_program,
+			     const char separator)
+{
+	struct xt_bpf_info *bi = (void *) cb->data;
+	const char *token;
+	char sp;
+	int i;
+
+	/* parse head: length. */
+	if (sscanf(bpf_program, "%hu%c", &bi->bpf_program_num_elem, &sp) != 2 ||
+		   sp != separator)
+		xtables_error(PARAMETER_PROBLEM,
+			      "bpf: error parsing program length");
+	if (!bi->bpf_program_num_elem)
+		xtables_error(PARAMETER_PROBLEM,
+			      "bpf: illegal zero length program");
+	if (bi->bpf_program_num_elem > XT_BPF_MAX_NUM_INSTR)
+		xtables_error(PARAMETER_PROBLEM,
+			      "bpf: number of instructions exceeds maximum");
+
+	/* parse instructions. */
+	i = 0;
+	token = bpf_program;
+	while ((token = strchr(token, separator)) && (++token)[0]) {
+		if (i >= bi->bpf_program_num_elem)
+			xtables_error(PARAMETER_PROBLEM,
+				      "bpf: real program length exceeds"
+				      " the encoded length parameter");
+		if (sscanf(token, "%hu %hhu %hhu %u,",
+			   &bi->bpf_program[i].code,
+			   &bi->bpf_program[i].jt,
+			   &bi->bpf_program[i].jf,
+			   &bi->bpf_program[i].k) != 4)
+			xtables_error(PARAMETER_PROBLEM,
+				      "bpf: error at instr %d", i);
+		i++;
+	}
+
+	if (i != bi->bpf_program_num_elem)
+		xtables_error(PARAMETER_PROBLEM,
+			      "bpf: parsed program length is less than the"
+			      " encoded length parameter");
+}
+
+static void bpf_parse(struct xt_option_call *cb)
+{
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_BCODE_STDIN:
+		bpf_parse_string(cb, cb->arg, ',');
+		break;
+	default:
+		xtables_error(PARAMETER_PROBLEM, "bpf: unknown option");
+	}
+}
+
+static void bpf_print_code(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_bpf_info *info = (void *) match->data;
+	int i;
+
+	for (i = 0; i < info->bpf_program_num_elem-1; i++)
+		printf("%hu %hhu %hhu %u,", info->bpf_program[i].code,
+					    info->bpf_program[i].jt,
+					    info->bpf_program[i].jf,
+					    info->bpf_program[i].k);
+
+	printf("%hu %hhu %hhu %u", info->bpf_program[i].code,
+				    info->bpf_program[i].jt,
+				    info->bpf_program[i].jf,
+				    info->bpf_program[i].k);
+}
+
+static void bpf_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_bpf_info *info = (void *) match->data;
+
+	printf(" --bytecode \"%hu,", info->bpf_program_num_elem);
+	bpf_print_code(ip, match);
+	printf("\"");
+}
+
+static void bpf_fcheck(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & (1 << O_BCODE_STDIN)))
+		xtables_error(PARAMETER_PROBLEM,
+			      "bpf: missing --bytecode parameter");
+}
+
+static void bpf_print(const void *ip, const struct xt_entry_match *match,
+		      int numeric)
+{
+	printf("match bpf ");
+	return bpf_print_code(ip, match);
+}
+
+static struct xtables_match bpf_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "bpf",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_bpf_info)),
+	.userspacesize	= XT_ALIGN(offsetof(struct xt_bpf_info, filter)),
+	.help		= bpf_help,
+	.print		= bpf_print,
+	.save		= bpf_save,
+	.x6_parse	= bpf_parse,
+	.x6_fcheck	= bpf_fcheck,
+	.x6_options	= bpf_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&bpf_match);
+}
diff --git a/extensions/libxt_bpf.man b/extensions/libxt_bpf.man
new file mode 100644
index 0000000..5b1d042
--- /dev/null
+++ b/extensions/libxt_bpf.man
@@ -0,0 +1,34 @@
+Match using Linux Socket Filter. Expects a BPF program in decimal format. This
+is the format generated by the \fBnfbpf_compile\fP utility.
+.TP
+\fB\-\-bytecode\fP \fIcode\fP
+Pass the BPF byte code format (described in the example below).
+.PP
+The code format is similar to the output of the tcpdump -ddd command: one line
+that stores the number of instructions, followed by one line for each
+instruction. Instruction lines follow the pattern 'u16 u8 u8 u32' in decimal
+notation. Fields encode the operation, jump offset if true, jump offset if
+false and generic multiuse field 'K'. Comments are not supported.
+.PP
+For example, to read only packets matching 'ip proto 6', insert the following,
+without the comments or trailing whitespace:
+.IP
+4               # number of instructions
+.br
+48 0 0 9        # load byte  ip->proto
+.br
+21 0 1 6        # jump equal IPPROTO_TCP
+.br
+6 0 0 1         # return     pass (non-zero)
+.br
+6 0 0 0         # return     fail (zero)
+.PP
+You can pass this filter to the bpf match with the following command:
+.IP
+iptables \-A OUTPUT \-m bpf \-\-bytecode '4,48 0 0 9,21 0 1 6,6 0 0 1,6 0 0 0' \-j ACCEPT
+.PP
+Or instead, you can invoke the nfbpf_compile utility.
+.IP
+iptables \-A OUTPUT \-m bpf \-\-bytecode "`nfbpf_compile RAW 'ip proto 6'`" \-j ACCEPT
+.PP
+You may want to learn more about BPF from FreeBSD's bpf(4) manpage.
diff --git a/extensions/libxt_connbytes.c b/extensions/libxt_connbytes.c
index 46a7e4b..ed2ad25 100644
--- a/extensions/libxt_connbytes.c
+++ b/extensions/libxt_connbytes.c
@@ -37,9 +37,14 @@
 	switch (cb->entry->id) {
 	case O_CONNBYTES:
 		sinfo->count.from = cb->val.u64_range[0];
-		sinfo->count.to   = cb->val.u64_range[0];
+		sinfo->count.to   = UINT64_MAX;
 		if (cb->nvals == 2)
 			sinfo->count.to = cb->val.u64_range[1];
+
+		if (sinfo->count.to < sinfo->count.from)
+			xtables_error(PARAMETER_PROBLEM, "%llu should be less than %llu",
+					(unsigned long long)sinfo->count.from,
+					(unsigned long long)sinfo->count.to);
 		if (cb->invert) {
 			i = sinfo->count.from;
 			sinfo->count.from = sinfo->count.to;
@@ -107,19 +112,29 @@
 	}
 }
 
+static void print_from_to(const struct xt_connbytes_info *sinfo, const char *prefix)
+{
+	unsigned long long from, to;
+
+	if (sinfo->count.from > sinfo->count.to) {
+		fputs(" !", stdout);
+		from = sinfo->count.to;
+		to = sinfo->count.from;
+	} else {
+		to = sinfo->count.to;
+		from = sinfo->count.from;
+	}
+	printf(" %sconnbytes %llu", prefix, from);
+	if (to && to < UINT64_MAX)
+		printf(":%llu", to);
+}
+
 static void
 connbytes_print(const void *ip, const struct xt_entry_match *match, int numeric)
 {
 	const struct xt_connbytes_info *sinfo = (const void *)match->data;
 
-	if (sinfo->count.from > sinfo->count.to) 
-		printf(" connbytes ! %llu:%llu",
-			(unsigned long long)sinfo->count.to,
-			(unsigned long long)sinfo->count.from);
-	else
-		printf(" connbytes %llu:%llu",
-			(unsigned long long)sinfo->count.from,
-			(unsigned long long)sinfo->count.to);
+	print_from_to(sinfo, "");
 
 	fputs(" connbytes mode", stdout);
 	print_mode(sinfo);
@@ -132,14 +147,7 @@
 {
 	const struct xt_connbytes_info *sinfo = (const void *)match->data;
 
-	if (sinfo->count.from > sinfo->count.to) 
-		printf(" ! --connbytes %llu:%llu",
-			(unsigned long long)sinfo->count.to,
-			(unsigned long long)sinfo->count.from);
-	else
-		printf(" --connbytes %llu:%llu",
-			(unsigned long long)sinfo->count.from,
-			(unsigned long long)sinfo->count.to);
+	print_from_to(sinfo, "--");
 
 	fputs(" --connbytes-mode", stdout);
 	print_mode(sinfo);
diff --git a/extensions/libxt_connlabel.c b/extensions/libxt_connlabel.c
new file mode 100644
index 0000000..c84a167
--- /dev/null
+++ b/extensions/libxt_connlabel.c
@@ -0,0 +1,124 @@
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_connlabel.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+enum {
+	O_LABEL = 0,
+	O_SET = 1,
+};
+
+static struct nfct_labelmap *map;
+
+static void connlabel_mt_help(void)
+{
+	puts(
+"connlabel match options:\n"
+"[!] --label name     Match if label has been set on connection\n"
+"    --set            Set label on connection");
+}
+
+static const struct xt_option_entry connlabel_mt_opts[] = {
+	{.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
+	 .min = 1, .flags = XTOPT_MAND|XTOPT_INVERT},
+	{.name = "set", .id = O_SET, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static void connlabel_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_connlabel_mtinfo *info = cb->data;
+	int tmp;
+
+	xtables_option_parse(cb);
+
+	switch (cb->entry->id) {
+	case O_LABEL:
+		tmp = nfct_labelmap_get_bit(map, cb->arg);
+		if (tmp < 0)
+			xtables_error(PARAMETER_PROBLEM, "label '%s' not found", cb->arg);
+		info->bit = tmp;
+		if (cb->invert)
+			info->options |= XT_CONNLABEL_OP_INVERT;
+		break;
+	case O_SET:
+		info->options |= XT_CONNLABEL_OP_SET;
+		break;
+	}
+
+}
+
+static const char *connlabel_get_name(int b)
+{
+	const char *name = nfct_labelmap_get_name(map, b);
+	if (name && strcmp(name, ""))
+		return name;
+	return NULL;
+}
+
+static void
+connlabel_mt_print_op(const struct xt_connlabel_mtinfo *info, const char *prefix)
+{
+	if (info->options & XT_CONNLABEL_OP_SET)
+		printf(" %sset", prefix);
+}
+
+static void
+connlabel_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_connlabel_mtinfo *info = (const void *)match->data;
+	const char *name = connlabel_get_name(info->bit);
+
+	printf(" connlabel");
+	if (info->options & XT_CONNLABEL_OP_INVERT)
+		printf(" !");
+	if (numeric || name == NULL) {
+		printf(" %u", info->bit);
+	} else {
+		printf(" '%s'", name);
+	}
+	connlabel_mt_print_op(info, "");
+}
+
+static void
+connlabel_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connlabel_mtinfo *info = (const void *)match->data;
+	const char *name = connlabel_get_name(info->bit);
+
+	if (info->options & XT_CONNLABEL_OP_INVERT)
+		printf(" !");
+	if (name)
+		printf(" --label \"%s\"", name);
+	else
+		printf(" --label \"%u\"", info->bit);
+	connlabel_mt_print_op(info, "--");
+}
+
+static struct xtables_match connlabel_mt_reg = {
+	.family        = NFPROTO_UNSPEC,
+	.name          = "connlabel",
+	.version       = XTABLES_VERSION,
+	.size          = XT_ALIGN(sizeof(struct xt_connlabel_mtinfo)),
+	.userspacesize = offsetof(struct xt_connlabel_mtinfo, bit),
+	.help          = connlabel_mt_help,
+	.print         = connlabel_mt_print,
+	.save          = connlabel_mt_save,
+	.x6_parse      = connlabel_mt_parse,
+	.x6_options    = connlabel_mt_opts,
+};
+
+void _init(void)
+{
+	map = nfct_labelmap_new(NULL);
+	if (!map) {
+		fprintf(stderr, "cannot open connlabel.conf, not registering '%s' match: %s\n",
+			connlabel_mt_reg.name, strerror(errno));
+		return;
+	}
+	xtables_register_match(&connlabel_mt_reg);
+}
diff --git a/extensions/libxt_connlabel.man b/extensions/libxt_connlabel.man
new file mode 100644
index 0000000..bdaa51e
--- /dev/null
+++ b/extensions/libxt_connlabel.man
@@ -0,0 +1,33 @@
+Module matches or adds connlabels to a connection.
+connlabels are similar to connmarks, except labels are bit-based; i.e.
+all labels may be attached to a flow at the same time.
+Up to 128 unique labels are currently supported.
+.TP
+[\fB!\fP] \fB\-\-label\fP \fBname\fP
+matches if label \fBname\fP has been set on a connection.
+Instead of a name (which will be translated to a number, see EXAMPLE below),
+a number may be used instead.  Using a number always overrides connlabel.conf.
+.TP
+\fB\-\-set\fP
+if the label has not been set on the connection, set it.
+Note that setting a label can fail.  This is because the kernel allocates the
+conntrack label storage area when the connection is created, and it only
+reserves the amount of memory required by the ruleset that exists at
+the time the connection is created.
+In this case, the match will fail (or succeed, in case \fB\-\-label\fP
+option was negated).
+.PP
+This match depends on libnetfilter_conntrack 1.0.4 or later.
+Label translation is done via the \fB/etc/xtables/connlabel.conf\fP configuration file.
+.PP
+Example:
+.IP
+.nf
+0	eth0-in
+1	eth0-out
+2	ppp-in
+3	ppp-out
+4	bulk-traffic
+5	interactive
+.fi
+.PP
diff --git a/extensions/libxt_connlimit.man b/extensions/libxt_connlimit.man
index bd369a6..ad9f40f 100644
--- a/extensions/libxt_connlimit.man
+++ b/extensions/libxt_connlimit.man
@@ -13,7 +13,8 @@
 maximum prefix length for the applicable protocol is used.
 .TP
 \fB\-\-connlimit\-saddr\fP
-Apply the limit onto the source group.
+Apply the limit onto the source group. This is the default if
+\-\-connlimit\-daddr is not specified.
 .TP
 \fB\-\-connlimit\-daddr\fP
 Apply the limit onto the destination group.
diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c
index e1d8575..128bbd2 100644
--- a/extensions/libxt_conntrack.c
+++ b/extensions/libxt_conntrack.c
@@ -13,7 +13,11 @@
 #include <string.h>
 #include <xtables.h>
 #include <linux/netfilter/xt_conntrack.h>
+#include <linux/netfilter/xt_state.h>
 #include <linux/netfilter/nf_conntrack_common.h>
+#ifndef XT_STATE_UNTRACKED
+#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
+#endif
 
 struct ip_conntrack_old_tuple {
 	struct {
@@ -110,9 +114,48 @@
 };
 #undef s
 
-#define s struct xt_conntrack_mtinfo3 /* for v1-v3 */
-/* We exploit the fact that v1-v3 share the same layout */
-static const struct xt_option_entry conntrack_mt_opts[] = {
+#define s struct xt_conntrack_mtinfo2
+/* We exploit the fact that v1-v2 share the same xt_o_e layout */
+static const struct xt_option_entry conntrack2_mt_opts[] = {
+	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctorigsrc", .id = O_CTORIGSRC, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctstatus", .id = O_CTSTATUS, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctexpire", .id = O_CTEXPIRE, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT},
+	/*
+	 * Rev 1 and 2 only store one port, and we would normally use
+	 * %XTTYPE_PORT (rather than %XTTYPE_PORTRC) for that. The resulting
+	 * error message - in case a user passed a range nevertheless -
+	 * "port 22:23 resolved to nothing" is not quite as useful as using
+	 * %XTTYPE_PORTC and libxt_conntrack's own range test.
+	 */
+	{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_NBO},
+	{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_NBO},
+	{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_NBO},
+	{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_NBO},
+	{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+#define s struct xt_conntrack_mtinfo3
+/* Difference from v2 is the non-NBO form. */
+static const struct xt_option_entry conntrack3_mt_opts[] = {
 	{.name = "ctstate", .id = O_CTSTATE, .type = XTTYPE_STRING,
 	 .flags = XTOPT_INVERT},
 	{.name = "ctproto", .id = O_CTPROTO, .type = XTTYPE_PROTOCOL,
@@ -303,10 +346,9 @@
 			sinfo->invflags |= XT_CONNTRACK_STATE;
 		break;
 	case O_CTPROTO:
+		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
 		if (cb->invert)
 			sinfo->invflags |= XT_CONNTRACK_PROTO;
-		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = cb->val.protocol;
-
 		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
 		    && (sinfo->invflags & XT_INV_PROTO))
 			xtables_error(PARAMETER_PROBLEM,
@@ -759,7 +801,9 @@
 	if (info->match_flags & XT_CONNTRACK_STATE) {
 		if (info->invert_flags & XT_CONNTRACK_STATE)
 			printf(" !");
-		printf(" %sctstate", prefix);
+		printf(" %s%s", prefix,
+			info->match_flags & XT_CONNTRACK_STATE_ALIAS
+				? "state" : "ctstate");
 		print_state(info->state_mask);
 	}
 
@@ -860,6 +904,15 @@
 	}
 }
 
+static const char *
+conntrack_print_name_alias(const struct xt_entry_match *match)
+{
+	struct xt_conntrack_mtinfo1 *info = (void *)match->data;
+
+	return info->match_flags & XT_CONNTRACK_STATE_ALIAS
+		? "state" : "conntrack";
+}
+
 static void conntrack_print(const void *ip, const struct xt_entry_match *match,
                             int numeric)
 {
@@ -965,6 +1018,144 @@
 	conntrack_dump(&up, "--", NFPROTO_IPV6, true, false);
 }
 
+static void
+state_help(void)
+{
+	printf(
+"state match options:\n"
+" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
+"				State(s) to match\n");
+}
+
+static const struct xt_option_entry state_opts[] = {
+	{.name = "state", .id = O_CTSTATE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static unsigned int
+state_parse_state(const char *state, size_t len)
+{
+	if (strncasecmp(state, "INVALID", len) == 0)
+		return XT_CONNTRACK_STATE_INVALID;
+	else if (strncasecmp(state, "NEW", len) == 0)
+		return XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
+	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
+		return XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
+	else if (strncasecmp(state, "RELATED", len) == 0)
+		return XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
+	else if (strncasecmp(state, "UNTRACKED", len) == 0)
+		return XT_CONNTRACK_STATE_UNTRACKED;
+	return 0;
+}
+
+static unsigned int
+state_parse_states(const char *arg)
+{
+	const char *comma;
+	unsigned int mask = 0, flag;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg)
+			goto badstate;
+		flag = state_parse_state(arg, comma-arg);
+		if (flag == 0)
+			goto badstate;
+		mask |= flag;
+		arg = comma+1;
+	}
+	if (!*arg)
+		xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
+					      "states with no spaces, e.g. "
+					      "ESTABLISHED,RELATED");
+	if (strlen(arg) == 0)
+		goto badstate;
+	flag = state_parse_state(arg, strlen(arg));
+	if (flag == 0)
+		goto badstate;
+	mask |= flag;
+	return mask;
+ badstate:
+	xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
+}
+
+static void state_parse(struct xt_option_call *cb)
+{
+	struct xt_state_info *sinfo = cb->data;
+
+	xtables_option_parse(cb);
+	sinfo->statemask = state_parse_states(cb->arg);
+	if (cb->invert)
+		sinfo->statemask = ~sinfo->statemask;
+}
+
+static void state_ct1_parse(struct xt_option_call *cb)
+{
+	struct xt_conntrack_mtinfo1 *sinfo = cb->data;
+
+	xtables_option_parse(cb);
+	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
+	sinfo->state_mask = state_parse_states(cb->arg);
+	if (cb->invert)
+		sinfo->invert_flags |= XT_CONNTRACK_STATE;
+}
+
+static void state_ct23_parse(struct xt_option_call *cb)
+{
+	struct xt_conntrack_mtinfo3 *sinfo = cb->data;
+
+	xtables_option_parse(cb);
+	sinfo->match_flags = XT_CONNTRACK_STATE | XT_CONNTRACK_STATE_ALIAS;
+	sinfo->state_mask = state_parse_states(cb->arg);
+	if (cb->invert)
+		sinfo->invert_flags |= XT_CONNTRACK_STATE;
+}
+
+static void state_print_state(unsigned int statemask)
+{
+	const char *sep = "";
+
+	if (statemask & XT_CONNTRACK_STATE_INVALID) {
+		printf("%sINVALID", sep);
+		sep = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
+		printf("%sNEW", sep);
+		sep = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
+		printf("%sRELATED", sep);
+		sep = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
+		printf("%sESTABLISHED", sep);
+		sep = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_UNTRACKED) {
+		printf("%sUNTRACKED", sep);
+		sep = ",";
+	}
+}
+
+static void
+state_print(const void *ip,
+      const struct xt_entry_match *match,
+      int numeric)
+{
+	const struct xt_state_info *sinfo = (const void *)match->data;
+
+	printf(" state ");
+	state_print_state(sinfo->statemask);
+}
+
+static void state_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_state_info *sinfo = (const void *)match->data;
+
+	printf(" --state ");
+	state_print_state(sinfo->statemask);
+}
+
 static struct xtables_match conntrack_mt_reg[] = {
 	{
 		.version       = XTABLES_VERSION,
@@ -978,6 +1169,7 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack_print,
 		.save          = conntrack_save,
+		.alias	       = conntrack_print_name_alias,
 		.x6_options    = conntrack_mt_opts_v0,
 	},
 	{
@@ -992,7 +1184,8 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack1_mt4_print,
 		.save          = conntrack1_mt4_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -1006,7 +1199,8 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack1_mt6_print,
 		.save          = conntrack1_mt6_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -1020,7 +1214,8 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack2_mt_print,
 		.save          = conntrack2_mt_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -1034,7 +1229,8 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack2_mt6_print,
 		.save          = conntrack2_mt6_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -1048,7 +1244,8 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack3_mt_print,
 		.save          = conntrack3_mt_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack3_mt_opts,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -1062,7 +1259,66 @@
 		.x6_fcheck     = conntrack_mt_check,
 		.print         = conntrack3_mt6_print,
 		.save          = conntrack3_mt6_save,
-		.x6_options    = conntrack_mt_opts,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack3_mt_opts,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "state",
+		.real_name     = "conntrack",
+		.revision      = 1,
+		.ext_flags     = XTABLES_EXT_ALIAS,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.help          = state_help,
+		.print         = state_print,
+		.save          = state_save,
+		.x6_parse      = state_ct1_parse,
+		.x6_options    = state_opts,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "state",
+		.real_name     = "conntrack",
+		.revision      = 2,
+		.ext_flags     = XTABLES_EXT_ALIAS,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.help          = state_help,
+		.print         = state_print,
+		.save          = state_save,
+		.x6_parse      = state_ct23_parse,
+		.x6_options    = state_opts,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "state",
+		.real_name     = "conntrack",
+		.revision      = 3,
+		.ext_flags     = XTABLES_EXT_ALIAS,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.help          = state_help,
+		.print         = state_print,
+		.save          = state_save,
+		.x6_parse      = state_ct23_parse,
+		.x6_options    = state_opts,
+	},
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "state",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_state_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_state_info)),
+		.help          = state_help,
+		.print         = state_print,
+		.save          = state_save,
+		.x6_parse      = state_parse,
+		.x6_options    = state_opts,
 	},
 };
 
diff --git a/extensions/libxt_conntrack.man b/extensions/libxt_conntrack.man
index c397f74..15fd1dd 100644
--- a/extensions/libxt_conntrack.man
+++ b/extensions/libxt_conntrack.man
@@ -42,23 +42,23 @@
 States for \fB\-\-ctstate\fP:
 .TP
 \fBINVALID\fP
-meaning that the packet is associated with no known connection
+The packet is associated with no known connection.
 .TP
 \fBNEW\fP
-meaning that the packet has started a new connection, or otherwise associated
-with a connection which has not seen packets in both directions, and
+The packet has started a new connection, or otherwise associated
+with a connection which has not seen packets in both directions.
 .TP
 \fBESTABLISHED\fP
-meaning that the packet is associated with a connection which has seen packets
-in both directions,
+The packet is associated with a connection which has seen packets
+in both directions.
 .TP
 \fBRELATED\fP
-meaning that the packet is starting a new connection, but is associated with an
+The packet is starting a new connection, but is associated with an
 existing connection, such as an FTP data transfer, or an ICMP error.
 .TP
 \fBUNTRACKED\fP
-meaning that the packet is not tracked at all, which happens if you use
-the NOTRACK target in raw table.
+The packet is not tracked at all, which happens if you explicitly untrack it
+by using \-j CT \-\-notrack in the raw table.
 .TP
 \fBSNAT\fP
 A virtual state, matching if the original source address differs from the reply
@@ -74,7 +74,7 @@
 None of the below.
 .TP
 \fBEXPECTED\fP
-This is an expected connection (i.e. a conntrack helper set it up)
+This is an expected connection (i.e. a conntrack helper set it up).
 .TP
 \fBSEEN_REPLY\fP
 Conntrack has seen packets in both directions.
diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c
index 28c59b9..a35cabb 100644
--- a/extensions/libxt_dccp.c
+++ b/extensions/libxt_dccp.c
@@ -37,7 +37,10 @@
 "[!] --source-port port[:port]                          match source port(s)\n"
 " --sport ...\n"
 "[!] --destination-port port[:port]                     match destination port(s)\n"
-" --dport ...\n");
+" --dport ...\n"
+"[!] --dccp-types type[,...]                            match when packet is one of the given types\n"
+"[!] --dccp-option option                               match if option (by number!) is set\n"
+);
 }
 
 #define s struct xt_dccp_info
@@ -50,9 +53,10 @@
 	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
 	{.name = "dport", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
 	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
-	{.name = "dccp-types", .id = O_DCCP_TYPES, .type = XTTYPE_STRING},
+	{.name = "dccp-types", .id = O_DCCP_TYPES, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
 	{.name = "dccp-option", .id = O_DCCP_OPTION, .type = XTTYPE_UINT8,
-	 .min = 1, .max = UINT8_MAX, .flags = XTOPT_PUT,
+	 .min = 1, .max = UINT8_MAX, .flags = XTOPT_INVERT | XTOPT_PUT,
 	 XTOPT_POINTER(s, option)},
 	XTOPT_TABLEEND,
 };
@@ -261,13 +265,14 @@
 	}
 
 	if (einfo->flags & XT_DCCP_TYPE) {
-		printf(" --dccp-type");
-		print_types(einfo->typemask, einfo->invflags & XT_DCCP_TYPE,0);
+		printf("%s --dccp-types",
+		       einfo->invflags & XT_DCCP_TYPE ? " !" : "");
+		print_types(einfo->typemask, false, 0);
 	}
 
 	if (einfo->flags & XT_DCCP_OPTION) {
-		printf(" --dccp-option %s%u",
-			einfo->typemask & XT_DCCP_OPTION ? "! " : "",
+		printf("%s --dccp-option %u",
+			einfo->invflags & XT_DCCP_OPTION ? " !" : "",
 			einfo->option);
 	}
 }
diff --git a/extensions/libxt_dccp.man b/extensions/libxt_dccp.man
index 82c3f70..71beb4b 100644
--- a/extensions/libxt_dccp.man
+++ b/extensions/libxt_dccp.man
@@ -9,4 +9,4 @@
 .BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" .
 .TP
 [\fB!\fP] \fB\-\-dccp\-option\fP \fInumber\fP
-Match if DCP option set.
+Match if DCCP option set.
diff --git a/extensions/libxt_devgroup.c b/extensions/libxt_devgroup.c
index 4487c83..4a69c82 100644
--- a/extensions/libxt_devgroup.c
+++ b/extensions/libxt_devgroup.c
@@ -42,58 +42,50 @@
 		fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
 }
 
+static void devgroup_parse_groupspec(const char *arg, unsigned int *group,
+				     unsigned int *mask)
+{
+	char *end;
+	bool ok;
+
+	ok = xtables_strtoui(arg, &end, group, 0, UINT32_MAX);
+	if (ok && (*end == '/' || *end == '\0')) {
+		if (*end == '/')
+			ok = xtables_strtoui(end + 1, NULL, mask,
+			                     0, UINT32_MAX);
+		else
+			*mask = ~0U;
+		if (!ok)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Bad group value \"%s\"", arg);
+	} else {
+		*group = xtables_lmap_name2id(devgroups, arg);
+		if (*group == -1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Device group \"%s\" not found", arg);
+		*mask = ~0U;
+	}
+}
+
 static void devgroup_parse(struct xt_option_call *cb)
 {
 	struct xt_devgroup_info *info = cb->data;
-	unsigned int id;
-	char *end;
+	unsigned int id, mask;
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_SRC_GROUP:
-		info->src_group = strtoul(cb->arg, &end, 0);
-		if (end != cb->arg && (*end == '/' || *end == '\0')) {
-			if (*end == '/')
-				info->src_mask = strtoul(end+1, &end, 0);
-			else
-				info->src_mask = 0xffffffff;
-			if (*end != '\0' || end == cb->arg)
-				xtables_error(PARAMETER_PROBLEM,
-					      "Bad src-group value `%s'",
-					      cb->arg);
-		} else {
-			id = xtables_lmap_name2id(devgroups, cb->arg);
-			if (id == -1)
-				xtables_error(PARAMETER_PROBLEM,
-					      "Device group `%s' not found",
-					      cb->arg);
-			info->src_group = id;
-			info->src_mask  = 0xffffffff;
-		}
+		devgroup_parse_groupspec(cb->arg, &id, &mask);
+		info->src_group = id;
+		info->src_mask  = mask;
 		info->flags |= XT_DEVGROUP_MATCH_SRC;
 		if (cb->invert)
 			info->flags |= XT_DEVGROUP_INVERT_SRC;
 		break;
 	case O_DST_GROUP:
-		info->dst_group = strtoul(cb->arg, &end, 0);
-		if (end != cb->arg && (*end == '/' || *end == '\0')) {
-			if (*end == '/')
-				info->dst_mask = strtoul(end+1, &end, 0);
-			else
-				info->dst_mask = 0xffffffff;
-			if (*end != '\0' || end == cb->arg)
-				xtables_error(PARAMETER_PROBLEM,
-					      "Bad dst-group value `%s'",
-					      cb->arg);
-		} else {
-			id = xtables_lmap_name2id(devgroups, cb->arg);
-			if (id == -1)
-				xtables_error(PARAMETER_PROBLEM,
-					      "Device group `%s' not found",
-					      cb->arg);
-			info->dst_group = id;
-			info->dst_mask  = 0xffffffff;
-		}
+		devgroup_parse_groupspec(cb->arg, &id, &mask);
+		info->dst_group = id;
+		info->dst_mask  = mask;
 		info->flags |= XT_DEVGROUP_MATCH_DST;
 		if (cb->invert)
 			info->flags |= XT_DEVGROUP_INVERT_DST;
diff --git a/extensions/libxt_devgroup.man b/extensions/libxt_devgroup.man
new file mode 100644
index 0000000..4a66c9f
--- /dev/null
+++ b/extensions/libxt_devgroup.man
@@ -0,0 +1,7 @@
+Match device group of a packets incoming/outgoing interface.
+.TP
+[\fB!\fP] \fB\-\-src\-group\fP \fIname\fP
+Match device group of incoming device
+.TP
+[\fB!\fP] \fB\-\-dst\-group\fP \fIname\fP
+Match device group of outgoing device
diff --git a/extensions/libxt_dscp.c b/extensions/libxt_dscp.c
index 69533d6..02b22a4 100644
--- a/extensions/libxt_dscp.c
+++ b/extensions/libxt_dscp.c
@@ -43,9 +43,10 @@
 static const struct xt_option_entry dscp_opts[] = {
 	{.name = "dscp", .id = O_DSCP, .excl = F_DSCP_CLASS,
 	 .type = XTTYPE_UINT8, .min = 0, .max = XT_DSCP_MAX,
-	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_dscp_info, dscp)},
+	 .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_dscp_info, dscp)},
 	{.name = "dscp-class", .id = O_DSCP_CLASS, .excl = F_DSCP,
-	 .type = XTTYPE_STRING},
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
 	XTOPT_TABLEEND,
 };
 
diff --git a/extensions/libipt_ecn.c b/extensions/libxt_ecn.c
similarity index 60%
rename from extensions/libipt_ecn.c
rename to extensions/libxt_ecn.c
index 56a0347..286782a 100644
--- a/extensions/libipt_ecn.c
+++ b/extensions/libxt_ecn.c
@@ -1,6 +1,7 @@
 /* Shared library add-on to iptables for ECN matching
  *
- * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2002 by Harald Welte <laforge@netfilter.org>
+ * (C) 2011 by Patrick McHardy <kaber@trash.net>
  *
  * This program is distributed under the terms of GNU GPL v2, 1991
  *
@@ -9,7 +10,7 @@
  */
 #include <stdio.h>
 #include <xtables.h>
-#include <linux/netfilter_ipv4/ipt_ecn.h>
+#include <linux/netfilter/xt_ecn.h>
 
 enum {
 	O_ECN_TCP_CWR = 0,
@@ -23,7 +24,7 @@
 "ECN match options\n"
 "[!] --ecn-tcp-cwr 		Match CWR bit of TCP header\n"
 "[!] --ecn-tcp-ece		Match ECE bit of TCP header\n"
-"[!] --ecn-ip-ect [0..3]	Match ECN codepoint in IPv4 header\n");
+"[!] --ecn-ip-ect [0..3]	Match ECN codepoint in IPv4/IPv6 header\n");
 }
 
 static const struct xt_option_entry ecn_opts[] = {
@@ -38,24 +39,24 @@
 
 static void ecn_parse(struct xt_option_call *cb)
 {
-	struct ipt_ecn_info *einfo = cb->data;
+	struct xt_ecn_info *einfo = cb->data;
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_ECN_TCP_CWR:
-		einfo->operation |= IPT_ECN_OP_MATCH_CWR;
+		einfo->operation |= XT_ECN_OP_MATCH_CWR;
 		if (cb->invert)
-			einfo->invert |= IPT_ECN_OP_MATCH_CWR;
+			einfo->invert |= XT_ECN_OP_MATCH_CWR;
 		break;
 	case O_ECN_TCP_ECE:
-		einfo->operation |= IPT_ECN_OP_MATCH_ECE;
+		einfo->operation |= XT_ECN_OP_MATCH_ECE;
 		if (cb->invert)
-			einfo->invert |= IPT_ECN_OP_MATCH_ECE;
+			einfo->invert |= XT_ECN_OP_MATCH_ECE;
 		break;
 	case O_ECN_IP_ECT:
 		if (cb->invert)
-			einfo->invert |= IPT_ECN_OP_MATCH_IP;
-		einfo->operation |= IPT_ECN_OP_MATCH_IP;
+			einfo->invert |= XT_ECN_OP_MATCH_IP;
+		einfo->operation |= XT_ECN_OP_MATCH_IP;
 		einfo->ip_ect = cb->val.u8;
 		break;
 	}
@@ -71,47 +72,47 @@
 static void ecn_print(const void *ip, const struct xt_entry_match *match,
                       int numeric)
 {
-	const struct ipt_ecn_info *einfo =
-		(const struct ipt_ecn_info *)match->data;
+	const struct xt_ecn_info *einfo =
+		(const struct xt_ecn_info *)match->data;
 
 	printf(" ECN match");
 
-	if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
+	if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
 		printf(" %sECE",
-		       (einfo->invert & IPT_ECN_OP_MATCH_ECE) ? "!" : "");
+		       (einfo->invert & XT_ECN_OP_MATCH_ECE) ? "!" : "");
 	}
 
-	if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
+	if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
 		printf(" %sCWR",
-		       (einfo->invert & IPT_ECN_OP_MATCH_CWR) ? "!" : "");
+		       (einfo->invert & XT_ECN_OP_MATCH_CWR) ? "!" : "");
 	}
 
-	if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
+	if (einfo->operation & XT_ECN_OP_MATCH_IP) {
 		printf(" %sECT=%d",
-		       (einfo->invert & IPT_ECN_OP_MATCH_IP) ? "!" : "",
+		       (einfo->invert & XT_ECN_OP_MATCH_IP) ? "!" : "",
 		       einfo->ip_ect);
 	}
 }
 
 static void ecn_save(const void *ip, const struct xt_entry_match *match)
 {
-	const struct ipt_ecn_info *einfo =
-		(const struct ipt_ecn_info *)match->data;
-	
-	if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
-		if (einfo->invert & IPT_ECN_OP_MATCH_ECE)
+	const struct xt_ecn_info *einfo =
+		(const struct xt_ecn_info *)match->data;
+
+	if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
+		if (einfo->invert & XT_ECN_OP_MATCH_ECE)
 			printf(" !");
 		printf(" --ecn-tcp-ece");
 	}
 
-	if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
-		if (einfo->invert & IPT_ECN_OP_MATCH_CWR)
+	if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
+		if (einfo->invert & XT_ECN_OP_MATCH_CWR)
 			printf(" !");
 		printf(" --ecn-tcp-cwr");
 	}
 
-	if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
-		if (einfo->invert & IPT_ECN_OP_MATCH_IP)
+	if (einfo->operation & XT_ECN_OP_MATCH_IP) {
+		if (einfo->invert & XT_ECN_OP_MATCH_IP)
 			printf(" !");
 		printf(" --ecn-ip-ect %d", einfo->ip_ect);
 	}
@@ -120,9 +121,9 @@
 static struct xtables_match ecn_mt_reg = {
 	.name          = "ecn",
 	.version       = XTABLES_VERSION,
-	.family        = NFPROTO_IPV4,
-	.size          = XT_ALIGN(sizeof(struct ipt_ecn_info)),
-	.userspacesize = XT_ALIGN(sizeof(struct ipt_ecn_info)),
+	.family        = NFPROTO_UNSPEC,
+	.size          = XT_ALIGN(sizeof(struct xt_ecn_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_ecn_info)),
 	.help          = ecn_help,
 	.print         = ecn_print,
 	.save          = ecn_save,
diff --git a/extensions/libxt_ecn.man b/extensions/libxt_ecn.man
new file mode 100644
index 0000000..31c0a3e
--- /dev/null
+++ b/extensions/libxt_ecn.man
@@ -0,0 +1,11 @@
+This allows you to match the ECN bits of the IPv4/IPv6 and TCP header.  ECN is the Explicit Congestion Notification mechanism as specified in RFC3168
+.TP
+[\fB!\fP] \fB\-\-ecn\-tcp\-cwr\fP
+This matches if the TCP ECN CWR (Congestion Window Received) bit is set.
+.TP
+[\fB!\fP] \fB\-\-ecn\-tcp\-ece\fP
+This matches if the TCP ECN ECE (ECN Echo) bit is set.
+.TP
+[\fB!\fP] \fB\-\-ecn\-ip\-ect\fP \fInum\fP
+This matches a particular IPv4/IPv6 ECT (ECN-Capable Transport). You have to specify
+a number between `0' and `3'.
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index e683f9a..c5b8d77 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -10,6 +10,9 @@
  * 
  * Error corections by nmalykh@bilim.com (22.01.2005)
  */
+#define _BSD_SOURCE 1
+#define _ISOC99_SOURCE 1
+#include <math.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -20,10 +23,17 @@
 #include <linux/netfilter/xt_hashlimit.h>
 
 #define XT_HASHLIMIT_BURST	5
+#define XT_HASHLIMIT_BURST_MAX	10000
+
+#define XT_HASHLIMIT_BYTE_EXPIRE	15
+#define XT_HASHLIMIT_BYTE_EXPIRE_BURST	60
 
 /* miliseconds */
 #define XT_HASHLIMIT_GCINTERVAL	1000
-#define XT_HASHLIMIT_EXPIRE	10000
+
+struct hashlimit_mt_udata {
+	uint32_t mult;
+};
 
 static void hashlimit_help(void)
 {
@@ -56,8 +66,10 @@
 	O_HTABLE_MAX,
 	O_HTABLE_GCINT,
 	O_HTABLE_EXPIRE,
-	F_UPTO  = 1 << O_UPTO,
-	F_ABOVE = 1 << O_ABOVE,
+	F_BURST         = 1 << O_BURST,
+	F_UPTO          = 1 << O_UPTO,
+	F_ABOVE         = 1 << O_ABOVE,
+	F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE,
 };
 
 static void hashlimit_mt_help(void)
@@ -84,9 +96,9 @@
 #define s struct xt_hashlimit_info
 static const struct xt_option_entry hashlimit_opts[] = {
 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
-	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	 .type = XTTYPE_STRING},
 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
-	 .min = 1, .max = 10000, .flags = XTOPT_PUT,
+	 .min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT,
 	 XTOPT_POINTER(s, cfg.burst)},
 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
@@ -118,9 +130,7 @@
 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
 	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
 	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
-	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
-	 .min = 1, .max = 10000, .flags = XTOPT_PUT,
-	 XTOPT_POINTER(s, cfg.burst)},
+	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
 	 XTOPT_POINTER(s, cfg.size)},
@@ -140,26 +150,102 @@
 };
 #undef s
 
+static uint32_t cost_to_bytes(uint32_t cost)
+{
+	uint32_t r;
+
+	r = cost ? UINT32_MAX / cost : UINT32_MAX;
+	r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
+	return r;
+}
+
+static uint64_t bytes_to_cost(uint32_t bytes)
+{
+	uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
+	return UINT32_MAX / (r+1);
+}
+
+static uint32_t get_factor(int chr)
+{
+	switch (chr) {
+	case 'm': return 1024 * 1024;
+	case 'k': return 1024;
+	}
+	return 1;
+}
+
+static void burst_error(void)
+{
+	xtables_error(PARAMETER_PROBLEM, "bad value for option "
+			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
+}
+
+static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
+{
+	uintmax_t v;
+	char *end;
+
+	if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
+	    (*end == 0 && v > XT_HASHLIMIT_BURST_MAX))
+		burst_error();
+
+	v *= get_factor(*end);
+	if (v > UINT32_MAX)
+		xtables_error(PARAMETER_PROBLEM, "bad value for option "
+			"\"--hashlimit-burst\", value \"%s\" too large "
+				"(max %umb).", burst, UINT32_MAX/1024/1024);
+	return v;
+}
+
+static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+{
+	unsigned int factor = 1;
+	uint64_t tmp;
+	int r;
+	const char *mode = strstr(rate, "b/s");
+	if (!mode || mode == rate)
+		return false;
+
+	mode--;
+	r = atoi(rate);
+	if (r == 0)
+		return false;
+
+	factor = get_factor(*mode);
+	tmp = (uint64_t) r * factor;
+	if (tmp > UINT32_MAX)
+		xtables_error(PARAMETER_PROBLEM,
+			"Rate value too large \"%llu\" (max %u)\n",
+					(unsigned long long)tmp, UINT32_MAX);
+
+	*val = bytes_to_cost(tmp);
+	if (*val == 0)
+		xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
+
+	ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
+	return true;
+}
+
 static
-int parse_rate(const char *rate, uint32_t *val)
+int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
 {
 	const char *delim;
 	uint32_t r;
-	uint32_t mult = 1;  /* Seconds by default. */
 
+	ud->mult = 1;  /* Seconds by default. */
 	delim = strchr(rate, '/');
 	if (delim) {
 		if (strlen(delim+1) == 0)
 			return 0;
 
 		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
-			mult = 1;
+			ud->mult = 1;
 		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
-			mult = 60;
+			ud->mult = 60;
 		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
-			mult = 60*60;
+			ud->mult = 60*60;
 		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
-			mult = 24*60*60;
+			ud->mult = 24*60*60;
 		else
 			return 0;
 	}
@@ -167,12 +253,13 @@
 	if (!r)
 		return 0;
 
-	/* This would get mapped to infinite (1/day is minimum they
-           can specify, so we're ok at that end). */
-	if (r / mult > XT_HASHLIMIT_SCALE)
+	*val = XT_HASHLIMIT_SCALE * ud->mult / r;
+	if (*val == 0)
+		/*
+		 * The rate maps to infinity. (1/day is the minimum they can
+		 * specify, so we are ok at that end).
+		 */
 		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
-
-	*val = XT_HASHLIMIT_SCALE * mult / r;
 	return 1;
 }
 
@@ -182,7 +269,6 @@
 
 	r->cfg.burst = XT_HASHLIMIT_BURST;
 	r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
-	r->cfg.expire = XT_HASHLIMIT_EXPIRE;
 
 }
 
@@ -193,7 +279,6 @@
 	info->cfg.mode        = 0;
 	info->cfg.burst       = XT_HASHLIMIT_BURST;
 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
-	info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
 	info->cfg.srcmask     = 32;
 	info->cfg.dstmask     = 32;
 }
@@ -205,7 +290,6 @@
 	info->cfg.mode        = 0;
 	info->cfg.burst       = XT_HASHLIMIT_BURST;
 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
-	info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
 	info->cfg.srcmask     = 128;
 	info->cfg.dstmask     = 128;
 }
@@ -246,19 +330,10 @@
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_UPTO:
-		if (cb->invert)
-			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (!parse_rate(cb->arg, &info->cfg.avg))
+		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-upto", cb->arg);
 		break;
-	case O_ABOVE:
-		if (!cb->invert)
-			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (!parse_rate(cb->arg, &info->cfg.avg))
-			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
-			          "--hashlimit-above", cb->arg);
-		break;
 	case O_MODE:
 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
@@ -273,17 +348,24 @@
 
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
+	case O_BURST:
+		info->cfg.burst = parse_burst(cb->arg, info);
+		break;
 	case O_UPTO:
 		if (cb->invert)
 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (!parse_rate(cb->arg, &info->cfg.avg))
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+			info->cfg.mode |= XT_HASHLIMIT_BYTES;
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-upto", cb->arg);
 		break;
 	case O_ABOVE:
 		if (!cb->invert)
 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (!parse_rate(cb->arg, &info->cfg.avg))
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+			info->cfg.mode |= XT_HASHLIMIT_BYTES;
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-above", cb->arg);
 		break;
@@ -303,9 +385,44 @@
 
 static void hashlimit_check(struct xt_fcheck_call *cb)
 {
+	const struct hashlimit_mt_udata *udata = cb->udata;
+	struct xt_hashlimit_info *info = cb->data;
+
 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
 		xtables_error(PARAMETER_PROBLEM,
 				"You have to specify --hashlimit");
+	if (!(cb->xflags & F_HTABLE_EXPIRE))
+		info->cfg.expire = udata->mult * 1000; /* from s to msec */
+}
+
+static void hashlimit_mt_check(struct xt_fcheck_call *cb)
+{
+	const struct hashlimit_mt_udata *udata = cb->udata;
+	struct xt_hashlimit_mtinfo1 *info = cb->data;
+
+	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
+		xtables_error(PARAMETER_PROBLEM,
+				"You have to specify --hashlimit");
+	if (!(cb->xflags & F_HTABLE_EXPIRE))
+		info->cfg.expire = udata->mult * 1000; /* from s to msec */
+
+	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+		uint32_t burst = 0;
+		if (cb->xflags & F_BURST) {
+			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
+				xtables_error(PARAMETER_PROBLEM,
+					"burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
+
+			burst = info->cfg.burst;
+			burst /= cost_to_bytes(info->cfg.avg);
+			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
+				burst++;
+			if (!(cb->xflags & F_HTABLE_EXPIRE))
+				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
+		}
+		info->cfg.burst = burst;
+	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
+		burst_error();
 }
 
 static const struct rates
@@ -317,16 +434,58 @@
 	      { "min", XT_HASHLIMIT_SCALE*60 },
 	      { "sec", XT_HASHLIMIT_SCALE } };
 
-static void print_rate(uint32_t period)
+static uint32_t print_rate(uint32_t period)
 {
 	unsigned int i;
 
+	if (period == 0) {
+		printf(" %f", INFINITY);
+		return 0;
+	}
+
 	for (i = 1; i < ARRAY_SIZE(rates); ++i)
 		if (period > rates[i].mult
             || rates[i].mult/period < rates[i].mult%period)
 			break;
 
 	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
+	/* return in msec */
+	return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
+}
+
+static const struct {
+	const char *name;
+	uint32_t thresh;
+} units[] = {
+	{ "m", 1024 * 1024 },
+	{ "k", 1024 },
+	{ "", 1 },
+};
+
+static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
+{
+	unsigned int i;
+	unsigned long long r;
+
+	r = cost_to_bytes(avg);
+
+	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+		if (r >= units[i].thresh &&
+		    bytes_to_cost(r & ~(units[i].thresh - 1)) == avg)
+			break;
+	printf(" %llu%sb/s", r/units[i].thresh, units[i].name);
+
+	if (burst == 0)
+		return XT_HASHLIMIT_BYTE_EXPIRE * 1000;
+
+	r *= burst;
+	printf(" %s", prefix);
+	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+		if (r >= units[i].thresh)
+			break;
+
+	printf("burst %llu%sb", r / units[i].thresh, units[i].name);
+	return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
 }
 
 static void print_mode(unsigned int mode, char separator)
@@ -361,7 +520,10 @@
                             const struct xt_entry_match *match, int numeric)
 {
 	const struct xt_hashlimit_info *r = (const void *)match->data;
-	fputs(" limit: avg", stdout); print_rate(r->cfg.avg);
+	uint32_t quantum;
+
+	fputs(" limit: avg", stdout);
+	quantum = print_rate(r->cfg.avg);
 	printf(" burst %u", r->cfg.burst);
 	fputs(" mode", stdout);
 	print_mode(r->cfg.mode, '-');
@@ -371,19 +533,26 @@
 		printf(" htable-max %u", r->cfg.max);
 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
 		printf(" htable-gcinterval %u", r->cfg.gc_interval);
-	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
+	if (r->cfg.expire != quantum)
 		printf(" htable-expire %u", r->cfg.expire);
 }
 
 static void
 hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
 {
+	uint32_t quantum;
+
 	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
 		fputs(" limit: above", stdout);
 	else
 		fputs(" limit: up to", stdout);
-	print_rate(info->cfg.avg);
-	printf(" burst %u", info->cfg.burst);
+
+	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
+	} else {
+		quantum = print_rate(info->cfg.avg);
+		printf(" burst %u", info->cfg.burst);
+	}
 	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
 		fputs(" mode", stdout);
@@ -395,7 +564,7 @@
 		printf(" htable-max %u", info->cfg.max);
 	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
 		printf(" htable-gcinterval %u", info->cfg.gc_interval);
-	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
+	if (info->cfg.expire != quantum)
 		printf(" htable-expire %u", info->cfg.expire);
 
 	if (info->cfg.srcmask != dmask)
@@ -425,13 +594,15 @@
 static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
 {
 	const struct xt_hashlimit_info *r = (const void *)match->data;
+	uint32_t quantum;
 
-	fputs(" --hashlimit", stdout); print_rate(r->cfg.avg);
+	fputs(" --hashlimit", stdout);
+	quantum = print_rate(r->cfg.avg);
 	printf(" --hashlimit-burst %u", r->cfg.burst);
 
 	fputs(" --hashlimit-mode", stdout);
 	print_mode(r->cfg.mode, ',');
-	
+
 	printf(" --hashlimit-name %s", r->name);
 
 	if (r->cfg.size)
@@ -440,19 +611,26 @@
 		printf(" --hashlimit-htable-max %u", r->cfg.max);
 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
 		printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
-	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
+	if (r->cfg.expire != quantum)
 		printf(" --hashlimit-htable-expire %u", r->cfg.expire);
 }
 
 static void
 hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
 {
+	uint32_t quantum;
+
 	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
 		fputs(" --hashlimit-above", stdout);
 	else
 		fputs(" --hashlimit-upto", stdout);
-	print_rate(info->cfg.avg);
-	printf(" --hashlimit-burst %u", info->cfg.burst);
+
+	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
+	} else {
+		quantum = print_rate(info->cfg.avg);
+		printf(" --hashlimit-burst %u", info->cfg.burst);
+	}
 
 	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
@@ -468,7 +646,7 @@
 		printf(" --hashlimit-htable-max %u", info->cfg.max);
 	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
 		printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
-	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
+	if (info->cfg.expire != quantum)
 		printf(" --hashlimit-htable-expire %u", info->cfg.expire);
 
 	if (info->cfg.srcmask != dmask)
@@ -507,7 +685,8 @@
 		.x6_fcheck     = hashlimit_check,
 		.print         = hashlimit_print,
 		.save          = hashlimit_save,
-		.x6_options    = hashlimit_mt_opts,
+		.x6_options    = hashlimit_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -519,10 +698,11 @@
 		.help          = hashlimit_mt_help,
 		.init          = hashlimit_mt4_init,
 		.x6_parse      = hashlimit_mt_parse,
-		.x6_fcheck     = hashlimit_check,
+		.x6_fcheck     = hashlimit_mt_check,
 		.print         = hashlimit_mt4_print,
 		.save          = hashlimit_mt4_save,
 		.x6_options    = hashlimit_mt_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -534,10 +714,11 @@
 		.help          = hashlimit_mt_help,
 		.init          = hashlimit_mt6_init,
 		.x6_parse      = hashlimit_mt_parse,
-		.x6_fcheck     = hashlimit_check,
+		.x6_fcheck     = hashlimit_mt_check,
 		.print         = hashlimit_mt6_print,
 		.save          = hashlimit_mt6_save,
 		.x6_options    = hashlimit_mt_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
 	},
 };
 
diff --git a/extensions/libxt_hashlimit.man b/extensions/libxt_hashlimit.man
index f90577e..17cb2b0 100644
--- a/extensions/libxt_hashlimit.man
+++ b/extensions/libxt_hashlimit.man
@@ -2,14 +2,15 @@
 \fBlimit\fP match) for a group of connections using a \fBsingle\fP iptables
 rule. Grouping can be done per-hostgroup (source and/or destination address)
 and/or per-port. It gives you the ability to express "\fIN\fP packets per time
-quantum per group" (see below for some examples).
+quantum per group" or "\fIN\fP bytes per seconds" (see below for some examples).
 .PP
 A hash limit option (\fB\-\-hashlimit\-upto\fP, \fB\-\-hashlimit\-above\fP) and
 \fB\-\-hashlimit\-name\fP are required.
 .TP
 \fB\-\-hashlimit\-upto\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP]
-Match if the rate is below or equal to \fIamount\fP/quantum. It is specified as
-a number, with an optional time quantum suffix; the default is 3/hour.
+Match if the rate is below or equal to \fIamount\fP/quantum. It is specified either as
+a number, with an optional time quantum suffix (the default is 3/hour), or as
+\fIamount\fPb/second (number of bytes per second).
 .TP
 \fB\-\-hashlimit\-above\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP]
 Match if the rate is above \fIamount\fP/quantum.
@@ -17,7 +18,9 @@
 \fB\-\-hashlimit\-burst\fP \fIamount\fP
 Maximum initial number of packets to match: this number gets recharged by one
 every time the limit specified above is not reached, up to this number; the
-default is 5.
+default is 5.  When byte-based rate matching is requested, this option specifies
+the amount of bytes that can exceed the given rate.  This option should be used
+with caution -- if the entry expires, the burst value is reset too.
 .TP
 \fB\-\-hashlimit\-mode\fP {\fBsrcip\fP|\fBsrcport\fP|\fBdstip\fP|\fBdstport\fP}\fB,\fP...
 A comma-separated list of objects to take into consideration. If no
@@ -63,3 +66,11 @@
 "10000 packets per minute for every /28 subnet (groups of 8 addresses)
 in 10.0.0.0/8" =>
 \-s 10.0.0.8 \-\-hashlimit\-mask 28 \-\-hashlimit\-upto 10000/min
+.TP
+matching bytes per second
+"flows exceeding 512kbyte/s" =>
+\-\-hashlimit-mode srcip,dstip,srcport,dstport \-\-hashlimit\-above 512kb/s
+.TP
+matching bytes per second
+"hosts that exceed 512kbyte/s, but permit up to 1Megabytes without matching"
+\-\-hashlimit-mode dstip \-\-hashlimit\-above 512kb/s \-\-hashlimit-burst 1mb
diff --git a/extensions/libxt_limit.c b/extensions/libxt_limit.c
index b15b02f..f75ef2f 100644
--- a/extensions/libxt_limit.c
+++ b/extensions/libxt_limit.c
@@ -3,6 +3,9 @@
  * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
  * Hervé Eychenne    <rv@wallfire.org>
  */
+#define _BSD_SOURCE 1
+#define _ISOC99_SOURCE 1
+#include <math.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -64,12 +67,13 @@
 	if (!r)
 		return 0;
 
-	/* This would get mapped to infinite (1/day is minimum they
-           can specify, so we're ok at that end). */
-	if (r / mult > XT_LIMIT_SCALE)
-		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
-
 	*val = XT_LIMIT_SCALE * mult / r;
+	if (*val == 0)
+		/*
+		 * The rate maps to infinity. (1/day is the minimum they can
+		 * specify, so we are ok at that end).
+		 */
+		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
 	return 1;
 }
 
@@ -118,6 +122,11 @@
 {
 	unsigned int i;
 
+	if (period == 0) {
+		printf(" %f", INFINITY);
+		return;
+	}
+
 	for (i = 1; i < ARRAY_SIZE(rates); ++i)
 		if (period > rates[i].mult
             || rates[i].mult/period < rates[i].mult%period)
diff --git a/extensions/libxt_multiport.man b/extensions/libxt_multiport.man
index caf5c56..7eb083e 100644
--- a/extensions/libxt_multiport.man
+++ b/extensions/libxt_multiport.man
@@ -1,9 +1,8 @@
 This module matches a set of source or destination ports.  Up to 15
 ports can be specified.  A port range (port:port) counts as two
-ports.  It can only be used in conjunction with
-\fB\-p tcp\fP
-or
-\fB\-p udp\fP.
+ports.  It can only be used in conjunction with one of the
+following protocols:
+\fBtcp\fP, \fBudp\fP, \fBudplite\fP, \fBdccp\fP and \fBsctp\fP.
 .TP
 [\fB!\fP] \fB\-\-source\-ports\fP,\fB\-\-sports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]...
 Match if the source port is one of the given ports.  The flag
diff --git a/extensions/libxt_nfacct.c b/extensions/libxt_nfacct.c
new file mode 100644
index 0000000..2ad59d5
--- /dev/null
+++ b/extensions/libxt_nfacct.c
@@ -0,0 +1,89 @@
+/*
+ * (C) 2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Intra2Net AG <http://www.intra2net.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 (or
+ * any later at your option) as published by the Free Software Foundation.
+ */
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <xtables.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_nfacct.h>
+
+enum {
+	O_NAME = 0,
+};
+
+#define s struct xt_nfacct_match_info
+static const struct xt_option_entry nfacct_opts[] = {
+	{.name = "nfacct-name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .min = 1, .flags = XTOPT_MAND|XTOPT_PUT, XTOPT_POINTER(s, name)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void nfacct_help(void)
+{
+	printf("nfacct match options:\n"
+	       " --nfacct-name STRING		Name of accouting area\n");
+}
+
+static void nfacct_parse(struct xt_option_call *cb)
+{
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_NAME:
+		if (strchr(cb->arg, '\n') != NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Newlines not allowed in --nfacct-name");
+		break;
+	}
+}
+
+static void
+nfacct_print_name(const struct xt_nfacct_match_info *info, char *name)
+{
+	printf(" %snfacct-name ", name);
+	xtables_save_string(info->name);
+}
+
+static void nfacct_print(const void *ip, const struct xt_entry_match *match,
+                        int numeric)
+{
+	const struct xt_nfacct_match_info *info =
+		(struct xt_nfacct_match_info *)match->data;
+
+	nfacct_print_name(info, "");
+}
+
+static void nfacct_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_nfacct_match_info *info =
+		(struct xt_nfacct_match_info *)match->data;
+
+	nfacct_print_name(info, "--");
+}
+
+static struct xtables_match nfacct_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "nfacct",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_nfacct_match_info)),
+	.userspacesize	= offsetof(struct xt_nfacct_match_info, nfacct),
+	.help		= nfacct_help,
+	.x6_parse	= nfacct_parse,
+	.print		= nfacct_print,
+	.save		= nfacct_save,
+	.x6_options	= nfacct_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&nfacct_match);
+}
diff --git a/extensions/libxt_nfacct.man b/extensions/libxt_nfacct.man
new file mode 100644
index 0000000..b755f97
--- /dev/null
+++ b/extensions/libxt_nfacct.man
@@ -0,0 +1,30 @@
+The nfacct match provides the extended accounting infrastructure for iptables.
+You have to use this match together with the standalone user-space utility
+.B nfacct(8)
+.PP
+The only option available for this match is the following:
+.TP
+\fB\-\-nfacct\-name\fP \fIname\fP
+This allows you to specify the existing object name that will be use for
+accounting the traffic that this rule-set is matching.
+.PP
+To use this extension, you have to create an accounting object:
+.IP
+nfacct add http\-traffic
+.PP
+Then, you have to attach it to the accounting object via iptables:
+.IP
+iptables \-I INPUT \-p tcp \-\-sport 80 \-m nfacct \-\-nfacct\-name http\-traffic
+.IP
+iptables \-I OUTPUT \-p tcp \-\-dport 80 \-m nfacct \-\-nfacct\-name http\-traffic
+.PP
+Then, you can check for the amount of traffic that the rules match:
+.IP
+nfacct get http\-traffic
+.IP
+{ pkts = 00000000000000000156, bytes = 00000000000000151786 } = http-traffic;
+.PP
+You can obtain
+.B nfacct(8)
+from http://www.netfilter.org or, alternatively, from the git.netfilter.org
+repository.
diff --git a/extensions/libxt_osf.c b/extensions/libxt_osf.c
index 88274a0..52dba47 100644
--- a/extensions/libxt_osf.c
+++ b/extensions/libxt_osf.c
@@ -92,7 +92,14 @@
 {
 	const struct xt_osf_info *info = (const struct xt_osf_info*) match->data;
 
-	printf(" --genre %s%s", (info->flags & XT_OSF_INVERT) ? "! ": "", info->genre);
+	if (info->flags & XT_OSF_INVERT)
+		printf(" !");
+
+	printf(" --genre %s", info->genre);
+	if (info->flags & XT_OSF_TTL)
+		printf(" --ttl %u", info->ttl);
+	if (info->flags & XT_OSF_LOG)
+		printf(" --log %u", info->loglevel);
 }
 
 static struct xtables_match osf_match = {
diff --git a/extensions/libxt_owner.c b/extensions/libxt_owner.c
index d2fdfa9..d9adc12 100644
--- a/extensions/libxt_owner.c
+++ b/extensions/libxt_owner.c
@@ -129,7 +129,8 @@
 	 .flags = XTOPT_INVERT},
 	{.name = "gid-owner", .id = O_GROUP, .type = XTTYPE_STRING,
 	 .flags = XTOPT_INVERT},
-	{.name = "socket-exists", .id = O_SOCK_EXISTS, .type = XTTYPE_NONE},
+	{.name = "socket-exists", .id = O_SOCK_EXISTS, .type = XTTYPE_NONE,
+	 .flags = XTOPT_INVERT},
 	XTOPT_TABLEEND,
 };
 
diff --git a/extensions/libxt_physdev.c b/extensions/libxt_physdev.c
index 8f57fe9..a11faf4 100644
--- a/extensions/libxt_physdev.c
+++ b/extensions/libxt_physdev.c
@@ -27,11 +27,12 @@
 	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, physindev)},
 	{.name = "physdev-out", .id = O_PHYSDEV_OUT, .type = XTTYPE_STRING,
 	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, physoutdev)},
-	{.name = "physdev-is-in", .id = O_PHYSDEV_IS_IN, .type = XTTYPE_NONE},
+	{.name = "physdev-is-in", .id = O_PHYSDEV_IS_IN, .type = XTTYPE_NONE,
+	 .flags = XTOPT_INVERT},
 	{.name = "physdev-is-out", .id = O_PHYSDEV_IS_OUT,
-	 .type = XTTYPE_NONE},
+	 .type = XTTYPE_NONE, .flags = XTOPT_INVERT},
 	{.name = "physdev-is-bridged", .id = O_PHYSDEV_IS_BRIDGED,
-	 .type = XTTYPE_NONE},
+	 .type = XTTYPE_NONE, .flags = XTOPT_INVERT},
 	XTOPT_TABLEEND,
 };
 #undef s
diff --git a/extensions/libxt_policy.c b/extensions/libxt_policy.c
index 97722d6..0a64a80 100644
--- a/extensions/libxt_policy.c
+++ b/extensions/libxt_policy.c
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) 2005-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -40,8 +44,7 @@
 }
 
 static const struct xt_option_entry policy_opts[] = {
-	{.name = "dir", .id = O_DIRECTION, .type = XTTYPE_STRING,
-	 .flags = XTOPT_INVERT},
+	{.name = "dir", .id = O_DIRECTION, .type = XTTYPE_STRING},
 	{.name = "pol", .id = O_POLICY, .type = XTTYPE_STRING},
 	{.name = "strict", .id = O_STRICT, .type = XTTYPE_NONE},
 	{.name = "reqid", .id = O_REQID, .type = XTTYPE_UINT32,
diff --git a/extensions/libxt_rateest.c b/extensions/libxt_rateest.c
index 509b3e3..fb24412 100644
--- a/extensions/libxt_rateest.c
+++ b/extensions/libxt_rateest.c
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) 2008-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
@@ -8,9 +12,6 @@
 #include <xtables.h>
 #include <linux/netfilter/xt_rateest.h>
 
-/* Ugly hack to pass info to final_check function. We should fix the API */
-static struct xt_rateest_match_info *rateest_info;
-
 static void rateest_help(void)
 {
 	printf(
@@ -115,11 +116,8 @@
 	struct xt_rateest_match_info *info = (void *)(*match)->data;
 	unsigned int val;
 
-	rateest_info = info;
-
 	switch (c) {
 	case OPT_RATEEST1:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest can't be inverted");
@@ -133,7 +131,6 @@
 		break;
 
 	case OPT_RATEEST2:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest can't be inverted");
@@ -148,7 +145,6 @@
 		break;
 
 	case OPT_RATEEST_BPS1:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest-bps can't be inverted");
@@ -172,7 +168,6 @@
 		break;
 
 	case OPT_RATEEST_PPS1:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest-pps can't be inverted");
@@ -197,7 +192,6 @@
 		break;
 
 	case OPT_RATEEST_BPS2:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest-bps can't be inverted");
@@ -221,7 +215,6 @@
 		break;
 
 	case OPT_RATEEST_PPS2:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest-pps can't be inverted");
@@ -246,7 +239,6 @@
 		break;
 
 	case OPT_RATEEST_DELTA:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: rateest-delta can't be inverted");
@@ -260,8 +252,6 @@
 		break;
 
 	case OPT_RATEEST_EQ:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-
 		if (*flags & (1 << c))
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: can't specify lt/gt/eq twice");
@@ -273,8 +263,6 @@
 		break;
 
 	case OPT_RATEEST_LT:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-
 		if (*flags & (1 << c))
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: can't specify lt/gt/eq twice");
@@ -286,8 +274,6 @@
 		break;
 
 	case OPT_RATEEST_GT:
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-
 		if (*flags & (1 << c))
 			xtables_error(PARAMETER_PROBLEM,
 				   "rateest: can't specify lt/gt/eq twice");
@@ -302,10 +288,9 @@
 	return 1;
 }
 
-static void
-rateest_final_check(unsigned int flags)
+static void rateest_final_check(struct xt_fcheck_call *cb)
 {
-	struct xt_rateest_match_info *info = rateest_info;
+	struct xt_rateest_match_info *info = cb->data;
 
 	if (info == NULL)
 		xtables_error(PARAMETER_PROBLEM, "rateest match: "
@@ -367,8 +352,8 @@
 		if (info->flags & XT_RATEEST_MATCH_DELTA)
 			rateest_print_rate(info->bps1, numeric);
 		if (info->flags & XT_RATEEST_MATCH_ABS) {
-			rateest_print_mode(info, "");
 			rateest_print_rate(info->bps2, numeric);
+			rateest_print_mode(info, "");
 		}
 	}
 	if (info->flags & XT_RATEEST_MATCH_PPS) {
@@ -385,8 +370,6 @@
 		rateest_print_mode(info, "");
 
 		printf(" %s", info->name2);
-		if (info->flags & XT_RATEEST_MATCH_DELTA)
-			printf(" delta");
 
 		if (info->flags & XT_RATEEST_MATCH_BPS) {
 			printf(" bps");
@@ -401,33 +384,48 @@
 	}
 }
 
+static void __rateest_save_rate(const struct xt_rateest_match_info *info,
+                                const char *name, uint32_t r1, uint32_t r2,
+                                int numeric)
+{
+	if (info->flags & XT_RATEEST_MATCH_DELTA) {
+		printf(" --rateest-%s1", name);
+		rateest_print_rate(r1, numeric);
+		rateest_print_mode(info, "--rateest-");
+		printf(" --rateest-%s2", name);
+	} else {
+		rateest_print_mode(info, "--rateest-");
+		printf(" --rateest-%s", name);
+	}
+
+	if (info->flags & (XT_RATEEST_MATCH_ABS|XT_RATEEST_MATCH_DELTA))
+		rateest_print_rate(r2, numeric);
+}
+
+static void rateest_save_rates(const struct xt_rateest_match_info *info)
+{
+	if (info->flags & XT_RATEEST_MATCH_BPS)
+		__rateest_save_rate(info, "bps", info->bps1, info->bps2, 0);
+	if (info->flags & XT_RATEEST_MATCH_PPS)
+		__rateest_save_rate(info, "pps", info->pps1, info->pps2, 1);
+}
+
+
 static void
 rateest_save(const void *ip, const struct xt_entry_match *match)
 {
 	const struct xt_rateest_match_info *info = (const void *)match->data;
 
+	if (info->flags & XT_RATEEST_MATCH_DELTA)
+		printf(" --rateest-delta");
+
 	if (info->flags & XT_RATEEST_MATCH_REL) {
 		printf(" --rateest1 %s", info->name1);
-		if (info->flags & XT_RATEEST_MATCH_BPS)
-			printf(" --rateest-bps");
-		if (info->flags & XT_RATEEST_MATCH_PPS)
-			printf(" --rateest-pps");
-		rateest_print_mode(info, " --rateest-");
+		rateest_save_rates(info);
 		printf(" --rateest2 %s", info->name2);
-	} else {
+	} else { /* XT_RATEEST_MATCH_ABS */
 		printf(" --rateest %s", info->name1);
-		if (info->flags & XT_RATEEST_MATCH_BPS) {
-			printf(" --rateest-bps1");
-			rateest_print_rate(info->bps1, 0);
-			printf(" --rateest-bps2");
-			rateest_print_rate(info->bps2, 0);
-			rateest_print_mode(info, "--rateest-");
-		}
-		if (info->flags & XT_RATEEST_MATCH_PPS) {
-			printf(" --rateest-pps");
-			rateest_print_mode(info, "--rateest-");
-			printf(" %u", info->pps2);
-		}
+		rateest_save_rates(info);
 	}
 }
 
@@ -439,7 +437,7 @@
 	.userspacesize	= XT_ALIGN(offsetof(struct xt_rateest_match_info, est1)),
 	.help		= rateest_help,
 	.parse		= rateest_parse,
-	.final_check	= rateest_final_check,
+	.x6_fcheck	= rateest_final_check,
 	.print		= rateest_print,
 	.save		= rateest_save,
 	.extra_opts	= rateest_opts,
diff --git a/extensions/libxt_recent.c b/extensions/libxt_recent.c
index 1e1a111..b3510d9 100644
--- a/extensions/libxt_recent.c
+++ b/extensions/libxt_recent.c
@@ -10,20 +10,23 @@
 	O_UPDATE,
 	O_REMOVE,
 	O_SECONDS,
+	O_REAP,
 	O_HITCOUNT,
 	O_RTTL,
 	O_NAME,
 	O_RSOURCE,
 	O_RDEST,
+	O_MASK,
 	F_SET    = 1 << O_SET,
 	F_RCHECK = 1 << O_RCHECK,
 	F_UPDATE = 1 << O_UPDATE,
 	F_REMOVE = 1 << O_REMOVE,
+	F_SECONDS = 1 << O_SECONDS,
 	F_ANY_OP = F_SET | F_RCHECK | F_UPDATE | F_REMOVE,
 };
 
 #define s struct xt_recent_mtinfo
-static const struct xt_option_entry recent_opts[] = {
+static const struct xt_option_entry recent_opts_v0[] = {
 	{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
 	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
 	{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
@@ -33,7 +36,9 @@
 	{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
 	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
 	{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
-	 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds)},
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1},
+	{.name = "reap", .id = O_REAP, .type = XTTYPE_NONE,
+	 .also = F_SECONDS },
 	{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
 	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
 	{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
@@ -46,6 +51,34 @@
 };
 #undef s
 
+#define s struct xt_recent_mtinfo_v1
+static const struct xt_option_entry recent_opts_v1[] = {
+	{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
+	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
+	{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
+	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
+	{.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE,
+	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
+	{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
+	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
+	{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1},
+	{.name = "reap", .id = O_REAP, .type = XTTYPE_NONE,
+	 .also = F_SECONDS },
+	{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
+	{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
+	 .excl = F_SET | F_REMOVE},
+	{.name = "name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, name)},
+	{.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE},
+	{.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE},
+	{.name = "mask", .id = O_MASK, .type = XTTYPE_HOST,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, mask)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
 static void recent_help(void)
 {
 	printf(
@@ -57,6 +90,8 @@
 "    --seconds seconds           For check and update commands above.\n"
 "                                Specifies that the match will only occur if source address last seen within\n"
 "                                the last 'seconds' seconds.\n"
+"    --reap                      Purge entries older then 'seconds'.\n"
+"                                Can only be used in conjunction with the seconds option.\n"
 "    --hitcount hits             For check and update commands above.\n"
 "                                Specifies that the match will only occur if source address seen hits times.\n"
 "                                May be used in conjunction with the seconds option.\n"
@@ -68,18 +103,28 @@
 "    --name name                 Name of the recent list to be used.  DEFAULT used if none given.\n"
 "    --rsource                   Match/Save the source address of each packet in the recent list table (default).\n"
 "    --rdest                     Match/Save the destination address of each packet in the recent list table.\n"
+"    --mask netmask              Netmask that will be applied to this recent list.\n"
 "xt_recent by: Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n");
 }
 
-static void recent_init(struct xt_entry_match *match)
+enum {
+	XT_RECENT_REV_0 = 0,
+	XT_RECENT_REV_1,
+};
+
+static void recent_init(struct xt_entry_match *match, unsigned int rev)
 {
-	struct xt_recent_mtinfo *info = (void *)(match)->data;
+	struct xt_recent_mtinfo *info = (struct xt_recent_mtinfo *)match->data;
+	struct xt_recent_mtinfo_v1 *info_v1 =
+		(struct xt_recent_mtinfo_v1 *)match->data;
 
 	strncpy(info->name,"DEFAULT", XT_RECENT_NAME_LEN);
 	/* even though XT_RECENT_NAME_LEN is currently defined as 200,
 	 * better be safe, than sorry */
 	info->name[XT_RECENT_NAME_LEN-1] = '\0';
 	info->side = XT_RECENT_SOURCE;
+	if (rev == XT_RECENT_REV_1)
+		memset(&info_v1->mask, 0xFF, sizeof(info_v1->mask));
 }
 
 static void recent_parse(struct xt_option_call *cb)
@@ -117,6 +162,9 @@
 	case O_RDEST:
 		info->side = XT_RECENT_DEST;
 		break;
+	case O_REAP:
+		info->check_set |= XT_RECENT_REAP;
+		break;
 	}
 }
 
@@ -129,9 +177,9 @@
 }
 
 static void recent_print(const void *ip, const struct xt_entry_match *match,
-                         int numeric)
+                         unsigned int family)
 {
-	const struct xt_recent_mtinfo *info = (const void *)match->data;
+	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
 
 	if (info->invert)
 		printf(" !");
@@ -146,6 +194,8 @@
 	if (info->check_set & XT_RECENT_REMOVE)
 		printf(" REMOVE");
 	if(info->seconds) printf(" seconds: %d", info->seconds);
+	if (info->check_set & XT_RECENT_REAP)
+		printf(" reap");
 	if(info->hit_count) printf(" hit_count: %d", info->hit_count);
 	if (info->check_set & XT_RECENT_TTL)
 		printf(" TTL-Match");
@@ -154,11 +204,23 @@
 		printf(" side: source");
 	if (info->side == XT_RECENT_DEST)
 		printf(" side: dest");
+
+	switch(family) {
+	case NFPROTO_IPV4:
+		printf(" mask: %s",
+			xtables_ipaddr_to_numeric(&info->mask.in));
+		break;
+	case NFPROTO_IPV6:
+		printf(" mask: %s",
+			xtables_ip6addr_to_numeric(&info->mask.in6));
+		break;
+	}
 }
 
-static void recent_save(const void *ip, const struct xt_entry_match *match)
+static void recent_save(const void *ip, const struct xt_entry_match *match,
+			unsigned int family)
 {
-	const struct xt_recent_mtinfo *info = (const void *)match->data;
+	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
 
 	if (info->invert)
 		printf(" !");
@@ -172,32 +234,122 @@
 	if (info->check_set & XT_RECENT_REMOVE)
 		printf(" --remove");
 	if(info->seconds) printf(" --seconds %d", info->seconds);
+	if (info->check_set & XT_RECENT_REAP)
+		printf(" --reap");
 	if(info->hit_count) printf(" --hitcount %d", info->hit_count);
 	if (info->check_set & XT_RECENT_TTL)
 		printf(" --rttl");
 	if(info->name) printf(" --name %s",info->name);
+
+	switch(family) {
+	case NFPROTO_IPV4:
+		printf(" --mask %s",
+			xtables_ipaddr_to_numeric(&info->mask.in));
+		break;
+	case NFPROTO_IPV6:
+		printf(" --mask %s",
+			xtables_ip6addr_to_numeric(&info->mask.in6));
+		break;
+	}
+
 	if (info->side == XT_RECENT_SOURCE)
 		printf(" --rsource");
 	if (info->side == XT_RECENT_DEST)
 		printf(" --rdest");
 }
 
-static struct xtables_match recent_mt_reg = {
-	.name          = "recent",
-	.version       = XTABLES_VERSION,
-	.family        = NFPROTO_UNSPEC,
-	.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
-	.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
-	.help          = recent_help,
-	.init          = recent_init,
-	.x6_parse      = recent_parse,
-	.x6_fcheck     = recent_check,
-	.print         = recent_print,
-	.save          = recent_save,
-	.x6_options    = recent_opts,
+static void recent_init_v0(struct xt_entry_match *match)
+{
+	recent_init(match, XT_RECENT_REV_0);
+}
+
+static void recent_init_v1(struct xt_entry_match *match)
+{
+	recent_init(match, XT_RECENT_REV_1);
+}
+
+static void recent_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+	recent_save(ip, match, NFPROTO_UNSPEC);
+}
+
+static void recent_save_v4(const void *ip, const struct xt_entry_match *match)
+{
+	recent_save(ip, match, NFPROTO_IPV4);
+}
+
+static void recent_save_v6(const void *ip, const struct xt_entry_match *match)
+{
+	recent_save(ip, match, NFPROTO_IPV6);
+}
+
+static void recent_print_v0(const void *ip, const struct xt_entry_match *match,
+			    int numeric)
+{
+	recent_print(ip, match, NFPROTO_UNSPEC);
+}
+
+static void recent_print_v4(const void *ip, const struct xt_entry_match *match,
+                         int numeric)
+{
+	recent_print(ip, match, NFPROTO_IPV4);
+}
+
+static void recent_print_v6(const void *ip, const struct xt_entry_match *match,
+                         int numeric)
+{
+	recent_print(ip, match, NFPROTO_IPV6);
+}
+
+static struct xtables_match recent_mt_reg[] = {
+	{
+		.name          = "recent",
+		.version       = XTABLES_VERSION,
+		.revision      = 0,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
+		.help          = recent_help,
+		.init          = recent_init_v0,
+		.x6_parse      = recent_parse,
+		.x6_fcheck     = recent_check,
+		.print         = recent_print_v0,
+		.save          = recent_save_v0,
+		.x6_options    = recent_opts_v0,
+	},
+	{
+		.name          = "recent",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
+		.help          = recent_help,
+		.init          = recent_init_v1,
+		.x6_parse      = recent_parse,
+		.x6_fcheck     = recent_check,
+		.print         = recent_print_v4,
+		.save          = recent_save_v4,
+		.x6_options    = recent_opts_v1,
+	},
+	{
+		.name          = "recent",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
+		.help          = recent_help,
+		.init          = recent_init_v1,
+		.x6_parse      = recent_parse,
+		.x6_fcheck     = recent_check,
+		.print         = recent_print_v6,
+		.save          = recent_save_v6,
+		.x6_options    = recent_opts_v1,
+	},
 };
 
 void _init(void)
 {
-	xtables_register_match(&recent_mt_reg);
+	xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
 }
diff --git a/extensions/libxt_recent.man b/extensions/libxt_recent.man
index 0392c2c..d9bd5d2 100644
--- a/extensions/libxt_recent.man
+++ b/extensions/libxt_recent.man
@@ -24,6 +24,9 @@
 \fB\-\-rdest\fP
 Match/save the destination address of each packet in the recent list table.
 .TP
+\fB\-\-mask\fP \fInetmask\fP
+Netmask that will be applied to this recent list.
+.TP
 [\fB!\fP] \fB\-\-rcheck\fP
 Check if the source address of the packet is currently in the list.
 .TP
@@ -41,6 +44,11 @@
 \fB\-\-update\fP. When used, this will narrow the match to only happen when the
 address is in the list and was seen within the last given number of seconds.
 .TP
+\fB\-\-reap\fP
+This option can only be used in conjunction with \fB\-\-seconds\fP.
+When used, this will cause entries older than the last given number of seconds
+to be purged.
+.TP
 \fB\-\-hitcount\fP \fIhits\fP
 This option must be used in conjunction with one of \fB\-\-rcheck\fP or
 \fB\-\-update\fP. When used, this will narrow the match to only happen when the
diff --git a/extensions/libxt_rpfilter.c b/extensions/libxt_rpfilter.c
new file mode 100644
index 0000000..168e703
--- /dev/null
+++ b/extensions/libxt_rpfilter.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_rpfilter.h>
+
+enum {
+	O_RPF_LOOSE = 0,
+	O_RPF_VMARK = 1,
+	O_RPF_ACCEPT_LOCAL = 2,
+	O_RPF_INVERT = 3,
+};
+
+static void rpfilter_help(void)
+{
+	printf(
+"rpfilter match options:\n"
+"    --loose          permit reverse path via any interface\n"
+"    --validmark      use skb nfmark when performing route lookup\n"
+"    --accept-local   do not reject packets with a local source address\n"
+"    --invert         match packets that failed the reverse path test\n"
+	);
+}
+
+static const struct xt_option_entry rpfilter_opts[] = {
+	{.name = "loose", .id = O_RPF_LOOSE, .type = XTTYPE_NONE, },
+	{.name = "validmark", .id = O_RPF_VMARK, .type = XTTYPE_NONE, },
+	{.name = "accept-local", .id = O_RPF_ACCEPT_LOCAL, .type = XTTYPE_NONE, },
+	{.name = "invert", .id = O_RPF_INVERT, .type = XTTYPE_NONE, },
+	XTOPT_TABLEEND,
+};
+
+static void rpfilter_parse(struct xt_option_call *cb)
+{
+	struct xt_rpfilter_info *rpfinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_RPF_LOOSE:
+		rpfinfo->flags |= XT_RPFILTER_LOOSE;
+		break;
+	case O_RPF_VMARK:
+		rpfinfo->flags |= XT_RPFILTER_VALID_MARK;
+		break;
+	case O_RPF_ACCEPT_LOCAL:
+		rpfinfo->flags |= XT_RPFILTER_ACCEPT_LOCAL;
+		break;
+	case O_RPF_INVERT:
+		rpfinfo->flags |= XT_RPFILTER_INVERT;
+		break;
+	}
+}
+
+static void
+rpfilter_print_prefix(const void *ip, const void *matchinfo,
+			const char *prefix)
+{
+	const struct xt_rpfilter_info *info = matchinfo;
+	if (info->flags & XT_RPFILTER_LOOSE)
+		printf(" %s%s", prefix, rpfilter_opts[O_RPF_LOOSE].name);
+	if (info->flags & XT_RPFILTER_VALID_MARK)
+		printf(" %s%s", prefix, rpfilter_opts[O_RPF_VMARK].name);
+	if (info->flags & XT_RPFILTER_ACCEPT_LOCAL)
+		printf(" %s%s", prefix, rpfilter_opts[O_RPF_ACCEPT_LOCAL].name);
+	if (info->flags & XT_RPFILTER_INVERT)
+		printf(" %s%s", prefix, rpfilter_opts[O_RPF_INVERT].name);
+}
+
+
+static void
+rpfilter_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	printf(" rpfilter");
+	return rpfilter_print_prefix(ip, match->data, "");
+}
+
+static void rpfilter_save(const void *ip, const struct xt_entry_match *match)
+{
+	return rpfilter_print_prefix(ip, match->data, "--");
+}
+
+static struct xtables_match rpfilter_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "rpfilter",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_rpfilter_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_rpfilter_info)),
+	.help		= rpfilter_help,
+	.print		= rpfilter_print,
+	.save		= rpfilter_save,
+	.x6_parse	= rpfilter_parse,
+	.x6_options	= rpfilter_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&rpfilter_match);
+}
diff --git a/extensions/libxt_rpfilter.man b/extensions/libxt_rpfilter.man
new file mode 100644
index 0000000..f7f56d2
--- /dev/null
+++ b/extensions/libxt_rpfilter.man
@@ -0,0 +1,39 @@
+Performs a reverse path filter test on a packet.
+If a reply to the packet would be sent via the same interface
+that the packet arrived on, the packet will match.
+Note that, unlike the in-kernel rp_filter, packets protected
+by IPSec are not treated specially.  Combine this match with
+the policy match if you want this.
+Also, packets arriving via the loopback interface are always permitted.
+This match can only be used in the PREROUTING chain of the raw or mangle table.
+.TP
+\fB\-\-loose\fP
+Used to specifiy that the reverse path filter test should match
+even if the selected output device is not the expected one.
+.TP
+\fB\-\-validmark\fP
+Also use the packets' nfmark value when performing the reverse path route lookup.
+.TP
+\fB\-\-accept\-local\fP
+This will permit packets arriving from the network with a source address that is also
+assigned to the local machine.
+.TP
+\fB\-\-invert\fP
+This will invert the sense of the match.  Instead of matching packets that passed the
+reverse path filter test, match those that have failed it.
+.PP
+Example to log and drop packets failing the reverse path filter test:
+
+iptables \-t raw \-N RPFILTER
+
+iptables \-t raw \-A RPFILTER \-m rpfilter \-j RETURN
+
+iptables \-t raw \-A RPFILTER \-m limit \-\-limit 10/minute \-j NFLOG \-\-nflog\-prefix "rpfilter drop"
+
+iptables \-t raw \-A RPFILTER \-j DROP
+
+iptables \-t raw \-A PREROUTING \-j RPFILTER
+
+Example to drop failed packets, without logging:
+
+iptables \-t raw \-A RPFILTER \-m rpfilter \-\-invert \-j DROP
diff --git a/extensions/libxt_sctp.c b/extensions/libxt_sctp.c
index 5dbc36f..56a4cdf 100644
--- a/extensions/libxt_sctp.c
+++ b/extensions/libxt_sctp.c
@@ -257,7 +257,6 @@
 			xtables_error(PARAMETER_PROBLEM,
 			           "Only one `--source-port' allowed");
 		einfo->flags |= XT_SCTP_SRC_PORTS;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		parse_sctp_ports(optarg, einfo->spts);
 		if (invert)
 			einfo->invflags |= XT_SCTP_SRC_PORTS;
@@ -269,7 +268,6 @@
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one `--destination-port' allowed");
 		einfo->flags |= XT_SCTP_DEST_PORTS;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		parse_sctp_ports(optarg, einfo->dpts);
 		if (invert)
 			einfo->invflags |= XT_SCTP_DEST_PORTS;
@@ -280,8 +278,6 @@
 		if (*flags & XT_SCTP_CHUNK_TYPES)
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one `--chunk-types' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-
 		if (!argv[optind] 
 		    || argv[optind][0] == '-' || argv[optind][0] == '!')
 			xtables_error(PARAMETER_PROBLEM,
diff --git a/extensions/libxt_set.c b/extensions/libxt_set.c
index da722c7..2cb9e78 100644
--- a/extensions/libxt_set.c
+++ b/extensions/libxt_set.c
@@ -64,8 +64,6 @@
 		if (info->u.flags[0])
 			xtables_error(PARAMETER_PROBLEM,
 				      "--match-set can be specified only once");
-
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			info->u.flags[0] |= IPSET_MATCH_INV;
 
@@ -130,11 +128,6 @@
 }
 
 /* Revision 1 */
-
-#define set_help_v1	set_help_v0
-#define set_opts_v1	set_opts_v0
-#define set_check_v1	set_check_v0
-
 static int
 set_parse_v1(int c, char **argv, int invert, unsigned int *flags,
 	     const void *entry, struct xt_entry_match **match)
@@ -151,8 +144,6 @@
 		if (info->dim)
 			xtables_error(PARAMETER_PROBLEM,
 				      "--match-set can be specified only once");
-
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		if (invert)
 			info->flags |= IPSET_INV_MATCH;
 
@@ -214,6 +205,298 @@
 	print_match("--match-set", &info->match_set);
 }
 
+/* Revision 2 */
+static void
+set_help_v2(void)
+{
+	printf("set match options:\n"
+	       " [!] --match-set name flags [--return-nomatch]\n"
+	       "		 'name' is the set name from to match,\n" 
+	       "		 'flags' are the comma separated list of\n"
+	       "		 'src' and 'dst' specifications.\n");
+}
+
+static const struct option set_opts_v2[] = {
+	{.name = "match-set",		.has_arg = true,	.val = '1'},
+	{.name = "set",			.has_arg = true,	.val = '2'},
+	{.name = "return-nomatch",	.has_arg = false,	.val = '3'},
+	XT_GETOPT_TABLEEND,
+};
+
+static int
+set_parse_v2(int c, char **argv, int invert, unsigned int *flags,
+	     const void *entry, struct xt_entry_match **match)
+{
+	struct xt_set_info_match_v1 *myinfo = 
+		(struct xt_set_info_match_v1 *) (*match)->data;
+	struct xt_set_info *info = &myinfo->match_set;
+
+	switch (c) {
+	case '3':
+		info->flags |= IPSET_RETURN_NOMATCH;
+		break;
+	case '2':
+		fprintf(stderr,
+			"--set option deprecated, please use --match-set\n");
+	case '1':		/* --match-set <set> <flag>[,<flag> */
+		if (info->dim)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--match-set can be specified only once");
+		if (invert)
+			info->flags |= IPSET_INV_MATCH;
+
+		if (!argv[optind]
+		    || argv[optind][0] == '-'
+		    || argv[optind][0] == '!')
+			xtables_error(PARAMETER_PROBLEM,
+				      "--match-set requires two args.");
+
+		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "setname `%s' too long, max %d characters.",
+				      optarg, IPSET_MAXNAMELEN - 1);
+
+		get_set_byname(optarg, info);
+		parse_dirs(argv[optind], info);
+		DEBUGP("parse: set index %u\n", info->index);
+		optind++;
+		
+		*flags = 1;
+		break;
+	}
+
+	return 1;
+}
+
+/* Prints out the matchinfo. */
+static void
+set_print_v2(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_set_info_match_v1 *info = (const void *)match->data;
+
+	print_match("match-set", &info->match_set);
+	if (info->match_set.flags & IPSET_RETURN_NOMATCH)
+		printf(" return-nomatch");
+}
+
+static void
+set_save_v2(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_set_info_match_v1 *info = (const void *)match->data;
+
+	print_match("--match-set", &info->match_set);
+	if (info->match_set.flags & IPSET_RETURN_NOMATCH)
+		printf(" --return-nomatch");
+}
+
+/* Revision 3 */
+static void
+set_help_v3(void)
+{
+	printf("set match options:\n"
+	       " [!] --match-set name flags [--return-nomatch]\n"
+	       "   [! --update-counters] [! --update-subcounters]\n"
+	       "   [[!] --packets-eq value | --packets-lt value | --packets-gt value\n"
+	       "   [[!] --bytes-eq value | --bytes-lt value | --bytes-gt value\n"
+	       "		 'name' is the set name from to match,\n" 
+	       "		 'flags' are the comma separated list of\n"
+	       "		 'src' and 'dst' specifications.\n");
+}
+
+static const struct option set_opts_v3[] = {
+	{.name = "match-set",		.has_arg = true,	.val = '1'},
+	{.name = "set",			.has_arg = true,	.val = '2'},
+	{.name = "return-nomatch",	.has_arg = false,	.val = '3'},
+	{.name = "update-counters",	.has_arg = false,	.val = '4'},
+	{.name = "packets-eq",		.has_arg = true,	.val = '5'},
+	{.name = "packets-lt",		.has_arg = true,	.val = '6'},
+	{.name = "packets-gt",		.has_arg = true,	.val = '7'},
+	{.name = "bytes-eq",		.has_arg = true,	.val = '8'},
+	{.name = "bytes-lt",		.has_arg = true,	.val = '9'},
+	{.name = "bytes-gt",		.has_arg = true,	.val = '0'},
+	{.name = "update-subcounters",	.has_arg = false,	.val = 'a'},
+	XT_GETOPT_TABLEEND,
+};
+
+static uint64_t
+parse_counter(const char *opt)
+{
+	uintmax_t value;
+
+	if (!xtables_strtoul(opt, NULL, &value, 0, UINT64_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			      "Cannot parse %s as a counter value\n",
+			      opt);
+	return (uint64_t)value;
+}
+
+static int
+set_parse_v3(int c, char **argv, int invert, unsigned int *flags,
+	     const void *entry, struct xt_entry_match **match)
+{
+	struct xt_set_info_match_v3 *info = 
+		(struct xt_set_info_match_v3 *) (*match)->data;
+
+	switch (c) {
+	case 'a':
+		if (invert)
+			info->flags |= IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE;
+		break;
+	case '0':
+		if (info->bytes.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --bytes-[eq|lt|gt]"
+				      " is allowed\n");
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--bytes-gt option cannot be inverted\n");
+		info->bytes.op = IPSET_COUNTER_GT;
+		info->bytes.value = parse_counter(optarg);
+		break;
+	case '9':
+		if (info->bytes.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --bytes-[eq|lt|gt]"
+				      " is allowed\n");
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--bytes-lt option cannot be inverted\n");
+		info->bytes.op = IPSET_COUNTER_LT;
+		info->bytes.value = parse_counter(optarg);
+		break;
+	case '8':
+		if (info->bytes.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --bytes-[eq|lt|gt]"
+				      " is allowed\n");
+		info->bytes.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
+		info->bytes.value = parse_counter(optarg);
+		break;
+	case '7':
+		if (info->packets.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --packets-[eq|lt|gt]"
+				      " is allowed\n");
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--packets-gt option cannot be inverted\n");
+		info->packets.op = IPSET_COUNTER_GT;
+		info->packets.value = parse_counter(optarg);
+		break;
+	case '6':
+		if (info->packets.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --packets-[eq|lt|gt]"
+				      " is allowed\n");
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--packets-lt option cannot be inverted\n");
+		info->packets.op = IPSET_COUNTER_LT;
+		info->packets.value = parse_counter(optarg);
+		break;
+	case '5':
+		if (info->packets.op != IPSET_COUNTER_NONE)
+			xtables_error(PARAMETER_PROBLEM,
+				      "only one of the --packets-[eq|lt|gt]"
+				      " is allowed\n");
+		info->packets.op = invert ? IPSET_COUNTER_NE : IPSET_COUNTER_EQ;
+		info->packets.value = parse_counter(optarg);
+		break;
+	case '4':
+		if (invert)
+			info->flags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
+		break;
+	case '3':
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--return-nomatch flag cannot be inverted\n");
+		info->flags |= IPSET_FLAG_RETURN_NOMATCH;
+		break;
+	case '2':
+		fprintf(stderr,
+			"--set option deprecated, please use --match-set\n");
+	case '1':		/* --match-set <set> <flag>[,<flag> */
+		if (info->match_set.dim)
+			xtables_error(PARAMETER_PROBLEM,
+				      "--match-set can be specified only once");
+		if (invert)
+			info->match_set.flags |= IPSET_INV_MATCH;
+
+		if (!argv[optind]
+		    || argv[optind][0] == '-'
+		    || argv[optind][0] == '!')
+			xtables_error(PARAMETER_PROBLEM,
+				      "--match-set requires two args.");
+
+		if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "setname `%s' too long, max %d characters.",
+				      optarg, IPSET_MAXNAMELEN - 1);
+
+		get_set_byname(optarg, &info->match_set);
+		parse_dirs(argv[optind], &info->match_set);
+		DEBUGP("parse: set index %u\n", info->match_set.index);
+		optind++;
+		
+		*flags = 1;
+		break;
+	}
+
+	return 1;
+}
+
+static void
+set_printv3_counter(const struct ip_set_counter_match *c, const char *name,
+		    const char *sep)
+{
+	switch (c->op) {
+	case IPSET_COUNTER_EQ:
+		printf(" %s%s-eq %llu", sep, name, c->value);
+		break;
+	case IPSET_COUNTER_NE:
+		printf(" ! %s%s-eq %llu", sep, name, c->value);
+		break;
+	case IPSET_COUNTER_LT:
+		printf(" %s%s-lt %llu", sep, name, c->value);
+		break;
+	case IPSET_COUNTER_GT:
+		printf(" %s%s-gt %llu", sep, name, c->value);
+		break;
+	}
+}
+
+static void
+set_print_v3_matchinfo(const struct xt_set_info_match_v3 *info,
+		       const char *opt, const char *sep)
+{
+	print_match(opt, &info->match_set);
+	if (info->flags & IPSET_FLAG_RETURN_NOMATCH)
+		printf(" %sreturn-nomatch", sep);
+	if ((info->flags & IPSET_FLAG_SKIP_COUNTER_UPDATE))
+		printf(" ! %supdate-counters", sep);
+	if ((info->flags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE))
+		printf(" ! %supdate-subcounters", sep);
+	set_printv3_counter(&info->packets, "packets", sep);
+	set_printv3_counter(&info->bytes, "bytes", sep);
+}
+
+/* Prints out the matchinfo. */
+static void
+set_print_v3(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_set_info_match_v3 *info = (const void *)match->data;
+
+	set_print_v3_matchinfo(info, "match-set", "");
+}
+
+static void
+set_save_v3(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_set_info_match_v3 *info = (const void *)match->data;
+
+	set_print_v3_matchinfo(info, "--match-set", "--");
+}
+
 static struct xtables_match set_mt_reg[] = {
 	{
 		.name		= "set",
@@ -236,12 +519,40 @@
 		.family		= NFPROTO_UNSPEC,
 		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
-		.help		= set_help_v1,
+		.help		= set_help_v0,
 		.parse		= set_parse_v1,
-		.final_check	= set_check_v1,
+		.final_check	= set_check_v0,
 		.print		= set_print_v1,
 		.save		= set_save_v1,
-		.extra_opts	= set_opts_v1,
+		.extra_opts	= set_opts_v0,
+	},
+	{
+		.name		= "set",
+		.revision	= 2,
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_UNSPEC,
+		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
+		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v1)),
+		.help		= set_help_v2,
+		.parse		= set_parse_v2,
+		.final_check	= set_check_v0,
+		.print		= set_print_v2,
+		.save		= set_save_v2,
+		.extra_opts	= set_opts_v2,
+	},
+	{
+		.name		= "set",
+		.revision	= 3,
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_UNSPEC,
+		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
+		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v3)),
+		.help		= set_help_v3,
+		.parse		= set_parse_v3,
+		.final_check	= set_check_v0,
+		.print		= set_print_v3,
+		.save		= set_save_v3,
+		.extra_opts	= set_opts_v3,
 	},
 };
 
diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h
index 4ac84fa..47c3f5b 100644
--- a/extensions/libxt_set.h
+++ b/extensions/libxt_set.h
@@ -2,6 +2,7 @@
 #define _LIBXT_SET_H
 
 #include <unistd.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <errno.h>
@@ -23,6 +24,12 @@
 		xtables_error(OTHER_PROBLEM,
 			      "Can't open socket to ipset.\n");
 
+	if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
+		xtables_error(OTHER_PROBLEM,
+			      "Could not set close on exec: %s\n",
+			      strerror(errno));
+	}
+
 	req_version.op = IP_SET_OP_VERSION;
 	res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
 	if (res != 0)
diff --git a/extensions/libxt_set.man b/extensions/libxt_set.man
index 01b115f..7012ef2 100644
--- a/extensions/libxt_set.man
+++ b/extensions/libxt_set.man
@@ -14,10 +14,52 @@
 the set type of the specified set is single dimension (for example ipmap),
 then the command will match packets for which the source address can be
 found in the specified set. 
+.TP
+\fB\-\-return\-nomatch\fP
+If the \fB\-\-return\-nomatch\fP option is specified and the set type
+supports the \fBnomatch\fP flag, then the matching is reversed: a match
+with an element flagged with \fBnomatch\fP returns \fBtrue\fP, while a
+match with a plain element returns \fBfalse\fP.
+.TP
+\fB!\fP \fB\-\-update\-counters\fP
+If the \fB\-\-update\-counters\fP flag is negated, then the packet and
+byte counters of the matching element in the set won't be updated. Default
+the packet and byte counters are updated.
+.TP
+\fB!\fP \fB\-\-update\-subcounters\fP
+If the \fB\-\-update\-subcounters\fP flag is negated, then the packet and
+byte counters of the matching element in the member set of a list type of
+set won't be updated. Default the packet and byte counters are updated.
+.TP
+[\fB!\fP] \fB\-\-packets\-eq\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+packet counter of the element matches the given value too.
+.TP
+\fB\-\-packets\-lt\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+packet counter of the element is less than the given value as well.
+.TP
+\fB\-\-packets\-gt\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+packet counter of the element is greater than the given value as well.
+.TP
+[\fB!\fP] \fB\-bytes\-eq\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+byte counter of the element matches the given value too.
+.TP
+\fB\-\-bytes\-lt\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+byte counter of the element is less than the given value as well.
+.TP
+\fB\-\-bytes\-gt\fP \fIvalue\fP
+If the packet is matched an element in the set, match only if the
+byte counter of the element is greater than the given value as well.
+.PP
+The packet and byte counters related options and flags are ignored
+when the set was defined without counter support.
 .PP
 The option \fB\-\-match\-set\fP can be replaced by \fB\-\-set\fP if that does 
 not clash with an option of other extensions.
 .PP
-Use of -m set requires that ipset kernel support is provided. As standard
-kernels do not ship this currently, the ipset or Xtables-addons package needs
-to be installed.
+Use of -m set requires that ipset kernel support is provided, which, for
+standard kernels, is the case since Linux 2.6.39.
diff --git a/extensions/libxt_state.c b/extensions/libxt_state.c
deleted file mode 100644
index 3fc747d..0000000
--- a/extensions/libxt_state.c
+++ /dev/null
@@ -1,137 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <xtables.h>
-#include <linux/netfilter/nf_conntrack_common.h>
-#include <linux/netfilter/xt_state.h>
-
-#ifndef XT_STATE_UNTRACKED
-#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
-#endif
-
-enum {
-	O_STATE = 0,
-};
-
-static void
-state_help(void)
-{
-	printf(
-"state match options:\n"
-" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
-"				State(s) to match\n");
-}
-
-static const struct xt_option_entry state_opts[] = {
-	{.name = "state", .id = O_STATE, .type = XTTYPE_STRING,
-	 .flags = XTOPT_MAND},
-	XTOPT_TABLEEND,
-};
-
-static int
-state_parse_state(const char *state, size_t len, struct xt_state_info *sinfo)
-{
-	if (strncasecmp(state, "INVALID", len) == 0)
-		sinfo->statemask |= XT_STATE_INVALID;
-	else if (strncasecmp(state, "NEW", len) == 0)
-		sinfo->statemask |= XT_STATE_BIT(IP_CT_NEW);
-	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
-		sinfo->statemask |= XT_STATE_BIT(IP_CT_ESTABLISHED);
-	else if (strncasecmp(state, "RELATED", len) == 0)
-		sinfo->statemask |= XT_STATE_BIT(IP_CT_RELATED);
-	else if (strncasecmp(state, "UNTRACKED", len) == 0)
-		sinfo->statemask |= XT_STATE_UNTRACKED;
-	else
-		return 0;
-	return 1;
-}
-
-static void
-state_parse_states(const char *arg, struct xt_state_info *sinfo)
-{
-	const char *comma;
-
-	while ((comma = strchr(arg, ',')) != NULL) {
-		if (comma == arg || !state_parse_state(arg, comma-arg, sinfo))
-			xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
-		arg = comma+1;
-	}
-	if (!*arg)
-		xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of "
-					      "states with no spaces, e.g. "
-					      "ESTABLISHED,RELATED");
-	if (strlen(arg) == 0 || !state_parse_state(arg, strlen(arg), sinfo))
-		xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg);
-}
-
-static void state_parse(struct xt_option_call *cb)
-{
-	struct xt_state_info *sinfo = cb->data;
-
-	xtables_option_parse(cb);
-	state_parse_states(cb->arg, sinfo);
-	if (cb->invert)
-		sinfo->statemask = ~sinfo->statemask;
-}
-
-static void state_print_state(unsigned int statemask)
-{
-	const char *sep = "";
-
-	if (statemask & XT_STATE_INVALID) {
-		printf("%sINVALID", sep);
-		sep = ",";
-	}
-	if (statemask & XT_STATE_BIT(IP_CT_NEW)) {
-		printf("%sNEW", sep);
-		sep = ",";
-	}
-	if (statemask & XT_STATE_BIT(IP_CT_RELATED)) {
-		printf("%sRELATED", sep);
-		sep = ",";
-	}
-	if (statemask & XT_STATE_BIT(IP_CT_ESTABLISHED)) {
-		printf("%sESTABLISHED", sep);
-		sep = ",";
-	}
-	if (statemask & XT_STATE_UNTRACKED) {
-		printf("%sUNTRACKED", sep);
-		sep = ",";
-	}
-}
-
-static void
-state_print(const void *ip,
-      const struct xt_entry_match *match,
-      int numeric)
-{
-	const struct xt_state_info *sinfo = (const void *)match->data;
-
-	printf(" state ");
-	state_print_state(sinfo->statemask);
-}
-
-static void state_save(const void *ip, const struct xt_entry_match *match)
-{
-	const struct xt_state_info *sinfo = (const void *)match->data;
-
-	printf(" --state ");
-	state_print_state(sinfo->statemask);
-}
-
-static struct xtables_match state_match = { 
-	.family		= NFPROTO_UNSPEC,
-	.name		= "state",
-	.version	= XTABLES_VERSION,
-	.size		= XT_ALIGN(sizeof(struct xt_state_info)),
-	.userspacesize	= XT_ALIGN(sizeof(struct xt_state_info)),
-	.help		= state_help,
-	.print		= state_print,
-	.save		= state_save,
-	.x6_parse	= state_parse,
-	.x6_options	= state_opts,
-};
-
-void _init(void)
-{
-	xtables_register_match(&state_match);
-}
diff --git a/extensions/libxt_state.man b/extensions/libxt_state.man
index 37d095b..ec096ca 100644
--- a/extensions/libxt_state.man
+++ b/extensions/libxt_state.man
@@ -1,24 +1,8 @@
-This module, when combined with connection tracking, allows access to
-the connection tracking state for this packet.
+The "state" extension is a subset of the "conntrack" module.
+"state" allows access to the connection tracking state for this packet.
 .TP
 [\fB!\fP] \fB\-\-state\fP \fIstate\fP
-Where state is a comma separated list of the connection states to
-match.  Possible states are
-.B INVALID
-meaning that the packet could not be identified for some reason which
-includes running out of memory and ICMP errors which don't correspond to any
-known connection,
-.B ESTABLISHED
-meaning that the packet is associated with a connection which has seen
-packets in both directions,
-.B NEW
-meaning that the packet has started a new connection, or otherwise
-associated with a connection which has not seen packets in both
-directions, and
-.B RELATED
-meaning that the packet is starting a new connection, but is
-associated with an existing connection, such as an FTP data transfer,
-or an ICMP error.
-.B UNTRACKED
-meaning that the packet is not tracked at all, which happens if you use
-the NOTRACK target in raw table.
+Where state is a comma separated list of the connection states to match. Only a
+subset of the states unterstood by "conntrack" are recognized: \fBINVALID\fP,
+\fBESTABLISHED\fP, \fBNEW\fP, \fBRELATED\fP or \fBUNTRACKED\fP. For their
+description, see the "conntrack" heading in this manpage.
diff --git a/extensions/libxt_statistic.c b/extensions/libxt_statistic.c
index 12a83dd..b6ae5f5 100644
--- a/extensions/libxt_statistic.c
+++ b/extensions/libxt_statistic.c
@@ -1,3 +1,7 @@
+/*
+ * Copyright (c) 2006-2013 Patrick McHardy <kaber@trash.net>
+ */
+
 #include <math.h>
 #include <stdio.h>
 #include <string.h>
@@ -107,7 +111,7 @@
 		       (info->flags & XT_STATISTIC_INVERT) ? " !" : "",
 		       prefix,
 		       info->u.nth.every + 1);
-		if (info->u.nth.packet)
+		if (info->u.nth.packet || *prefix)
 			printf(" %spacket %u", prefix, info->u.nth.packet);
 		break;
 	}
diff --git a/extensions/libxt_string.c b/extensions/libxt_string.c
index eef0b08..fb15980 100644
--- a/extensions/libxt_string.c
+++ b/extensions/libxt_string.c
@@ -20,6 +20,7 @@
  *             updated to work with slightly modified
  *             ipt_string_info.
  */
+#define _GNU_SOURCE 1 /* strnlen for older glibcs */
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -217,7 +218,7 @@
 		if (! isprint(str[i]))
 			return 1;  /* string contains at least one non-printable char */
 	/* use hex output if the last char is a "\" */
-	if ((unsigned char) str[len-1] == 0x5c)
+	if (str[len-1] == '\\')
 		return 1;
 	return 0;
 }
@@ -228,16 +229,11 @@
 {
 	unsigned int i;
 	/* start hex block */
-	printf("\"|");
-	for (i=0; i < len; i++) {
-		/* see if we need to prepend a zero */
-		if ((unsigned char) str[i] <= 0x0F)
-			printf("0%x", (unsigned char) str[i]);
-		else
-			printf("%x", (unsigned char) str[i]);
-	}
+	printf(" \"|");
+	for (i=0; i < len; i++)
+		printf("%02x", (unsigned char)str[i]);
 	/* close hex block */
-	printf("|\" ");
+	printf("|\"");
 }
 
 static void
@@ -246,8 +242,8 @@
 	unsigned int i;
 	printf(" \"");
 	for (i=0; i < len; i++) {
-		if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
-			printf("%c", 0x5c);
+		if (str[i] == '\"' || str[i] == '\\')
+			putchar('\\');
 		printf("%c", (unsigned char) str[i]);
 	}
 	printf("\"");  /* closing quote */
diff --git a/extensions/libxt_tcp.c b/extensions/libxt_tcp.c
index 4d914e3..bbdec45 100644
--- a/extensions/libxt_tcp.c
+++ b/extensions/libxt_tcp.c
@@ -148,7 +148,6 @@
 		if (*flags & TCP_SRC_PORTS)
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one `--source-port' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		parse_tcp_ports(optarg, tcpinfo->spts);
 		if (invert)
 			tcpinfo->invflags |= XT_TCP_INV_SRCPT;
@@ -159,7 +158,6 @@
 		if (*flags & TCP_DST_PORTS)
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one `--destination-port' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		parse_tcp_ports(optarg, tcpinfo->dpts);
 		if (invert)
 			tcpinfo->invflags |= XT_TCP_INV_DSTPT;
@@ -180,8 +178,6 @@
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one of `--syn' or `--tcp-flags' "
 				   " allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-
 		if (!argv[optind]
 		    || argv[optind][0] == '-' || argv[optind][0] == '!')
 			xtables_error(PARAMETER_PROBLEM,
@@ -197,7 +193,6 @@
 		if (*flags & TCP_OPTION)
 			xtables_error(PARAMETER_PROBLEM,
 				   "Only one `--tcp-option' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
 		parse_tcp_option(optarg, &tcpinfo->option);
 		if (invert)
 			tcpinfo->invflags |= XT_TCP_INV_OPTION;
@@ -283,11 +278,10 @@
 print_flags(uint8_t mask, uint8_t cmp, int invert, int numeric)
 {
 	if (mask || invert) {
-		printf("flags:%s", invert ? "!" : "");
+		printf(" flags:%s", invert ? "!" : "");
 		if (numeric)
-			printf(" 0x%02X/0x%02X", mask, cmp);
+			printf("0x%02X/0x%02X", mask, cmp);
 		else {
-			printf(" ");
 			print_tcpf(mask);
 			printf("/");
 			print_tcpf(cmp);
@@ -362,9 +356,7 @@
 		if (tcpinfo->invflags & XT_TCP_INV_FLAGS)
 			printf(" !");
 		printf(" --tcp-flags ");
-		if (tcpinfo->flg_mask != 0xFF) {
-			print_tcpf(tcpinfo->flg_mask);
-		}
+		print_tcpf(tcpinfo->flg_mask);
 		printf(" ");
 		print_tcpf(tcpinfo->flg_cmp);
 	}
diff --git a/extensions/libxt_time.c b/extensions/libxt_time.c
index 44c05b8..9c5bda8 100644
--- a/extensions/libxt_time.c
+++ b/extensions/libxt_time.c
@@ -22,6 +22,7 @@
 	O_DATE_STOP,
 	O_TIME_START,
 	O_TIME_STOP,
+	O_TIME_CONTIGUOUS,
 	O_MONTHDAYS,
 	O_WEEKDAYS,
 	O_LOCAL_TZ,
@@ -30,6 +31,7 @@
 	F_LOCAL_TZ  = 1 << O_LOCAL_TZ,
 	F_UTC       = 1 << O_UTC,
 	F_KERNEL_TZ = 1 << O_KERNEL_TZ,
+	F_TIME_CONTIGUOUS = 1 << O_TIME_CONTIGUOUS,
 };
 
 static const char *const week_days[] = {
@@ -41,6 +43,7 @@
 	{.name = "datestop", .id = O_DATE_STOP, .type = XTTYPE_STRING},
 	{.name = "timestart", .id = O_TIME_START, .type = XTTYPE_STRING},
 	{.name = "timestop", .id = O_TIME_STOP, .type = XTTYPE_STRING},
+	{.name = "contiguous", .id = O_TIME_CONTIGUOUS, .type = XTTYPE_NONE},
 	{.name = "weekdays", .id = O_WEEKDAYS, .type = XTTYPE_STRING,
 	 .flags = XTOPT_INVERT},
 	{.name = "monthdays", .id = O_MONTHDAYS, .type = XTTYPE_STRING,
@@ -273,6 +276,9 @@
 	case O_TIME_STOP:
 		info->daytime_stop = time_parse_minutes(cb->arg);
 		break;
+	case O_TIME_CONTIGUOUS:
+		info->flags |= XT_TIME_CONTIGUOUS;
+		break;
 	case O_LOCAL_TZ:
 		fprintf(stderr, "WARNING: --localtz is being replaced by "
 		        "--kerneltz, since \"local\" is ambiguous. Note the "
@@ -403,6 +409,8 @@
 	}
 	if (!(info->flags & XT_TIME_LOCAL_TZ))
 		printf(" UTC");
+	if (info->flags & XT_TIME_CONTIGUOUS)
+		printf(" contiguous");
 }
 
 static void time_save(const void *ip, const struct xt_entry_match *match)
@@ -429,6 +437,17 @@
 	time_print_date(info->date_stop, "--datestop");
 	if (info->flags & XT_TIME_LOCAL_TZ)
 		printf(" --kerneltz");
+	if (info->flags & XT_TIME_CONTIGUOUS)
+		printf(" --contiguous");
+}
+
+static void time_check(struct xt_fcheck_call *cb)
+{
+	const struct xt_time_info *info = (const void *) cb->data;
+	if ((cb->xflags & F_TIME_CONTIGUOUS) &&
+	     info->daytime_start < info->daytime_stop)
+		xtables_error(PARAMETER_PROBLEM,
+			"time: --contiguous only makes sense when stoptime is smaller than starttime");
 }
 
 static struct xtables_match time_match = {
@@ -442,6 +461,7 @@
 	.print         = time_print,
 	.save          = time_save,
 	.x6_parse      = time_parse,
+	.x6_fcheck     = time_check,
 	.x6_options    = time_opts,
 };
 
diff --git a/extensions/libxt_time.man b/extensions/libxt_time.man
index 1d677b9..4c0cae0 100644
--- a/extensions/libxt_time.man
+++ b/extensions/libxt_time.man
@@ -30,6 +30,10 @@
 to \fB7\fP, respectively. You may also use two-character variants (\fBMo\fP,
 \fBTu\fP, etc.).
 .TP
+\fB\-\-contiguous\fP
+When \fB\-\-timestop\fP is smaller than \fB\-\-timestart\fP value, match
+this as a single time period instead distinct intervals.  See EXAMPLES.
+.TP
 \fB\-\-kerneltz\fP
 Use the kernel timezone instead of UTC to determine whether a packet meets the
 time regulations.
@@ -84,3 +88,11 @@
 (Note that this exploits a certain mathematical property. It is not possible to
 say "fourth Thursday OR fourth Friday" in one rule. It is possible with
 multiple rules, though.)
+.PP
+Matching across days might not do what is expected.  For instance,
+.IP
+\-m time \-\-weekdays Mo \-\-timestart 23:00  \-\-timestop 01:00
+Will match Monday, for one hour from midnight to 1 a.m., and then
+again for another hour from 23:00 onwards.  If this is unwanted, e.g. if you
+would like 'match for two hours from Montay 23:00 onwards' you need to also specify
+the \-\-contiguous option in the example above.
diff --git a/extensions/libxt_u32.c b/extensions/libxt_u32.c
index 774d5ea..2a7f5d8 100644
--- a/extensions/libxt_u32.c
+++ b/extensions/libxt_u32.c
@@ -24,7 +24,7 @@
 
 static const struct xt_option_entry u32_opts[] = {
 	{.name = "u32", .id = O_U32, .type = XTTYPE_STRING,
-	 .flags = XTOPT_MAND},
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
 	XTOPT_TABLEEND,
 };
 
@@ -88,17 +88,13 @@
 /* string_to_number() is not quite what we need here ... */
 static uint32_t parse_number(const char **s, int pos)
 {
-	uint32_t number;
+	unsigned int number;
 	char *end;
 
-	errno  = 0;
-	number = strtoul(*s, &end, 0);
-	if (end == *s)
+	if (!xtables_strtoui(*s, &end, &number, 0, UINT32_MAX) ||
+	    end == *s)
 		xtables_error(PARAMETER_PROBLEM,
-			   "u32: at char %d: expected number", pos);
-	if (errno != 0)
-		xtables_error(PARAMETER_PROBLEM,
-			   "u32: at char %d: error reading number", pos);
+			"u32: at char %d: not a number or out of range", pos);
 	*s = end;
 	return number;
 }
diff --git a/include/Makefile.am b/include/Makefile.am
index 0a1abea..e695120 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,7 +1,7 @@
 # -*- Makefile -*-
 
 include_HEADERS =
-nobase_include_HEADERS = xtables.h
+nobase_include_HEADERS = xtables.h xtables-version.h
 
 if ENABLE_LIBIPQ
 include_HEADERS += libipq/libipq.h
@@ -9,4 +9,4 @@
 
 nobase_include_HEADERS += \
 	libiptc/ipt_kernel_headers.h libiptc/libiptc.h \
-	libiptc/libip6tc.h libiptc/libxtc.h
+	libiptc/libip6tc.h libiptc/libxtc.h libiptc/xtcshared.h
diff --git a/include/ip6tables.h b/include/ip6tables.h
index e976361..5f1c5b6 100644
--- a/include/ip6tables.h
+++ b/include/ip6tables.h
@@ -8,12 +8,12 @@
 
 /* Your shared library should call one of these. */
 extern int do_command6(int argc, char *argv[], char **table,
-		       struct ip6tc_handle **handle);
+		       struct xtc_handle **handle, bool restore);
 
-extern int for_each_chain6(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), int verbose, int builtinstoo, struct ip6tc_handle *handle);
-extern int flush_entries6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle);
-extern int delete_chain6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle);
-void print_rule6(const struct ip6t_entry *e, struct ip6tc_handle *h, const char *chain, int counters);
+extern int for_each_chain6(int (*fn)(const xt_chainlabel, int, struct xtc_handle *), int verbose, int builtinstoo, struct xtc_handle *handle);
+extern int flush_entries6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle);
+extern int delete_chain6(const xt_chainlabel chain, int verbose, struct xtc_handle *handle);
+void print_rule6(const struct ip6t_entry *e, struct xtc_handle *h, const char *chain, int counters);
 
 extern struct xtables_globals ip6tables_globals;
 
diff --git a/include/iptables.h b/include/iptables.h
index 65b3290..ac9dc0e 100644
--- a/include/iptables.h
+++ b/include/iptables.h
@@ -8,23 +8,15 @@
 
 /* Your shared library should call one of these. */
 extern int do_command4(int argc, char *argv[], char **table,
-		      struct iptc_handle **handle);
-extern int delete_chain4(const ipt_chainlabel chain, int verbose,
-			struct iptc_handle *handle);
-extern int flush_entries4(const ipt_chainlabel chain, int verbose, 
-			struct iptc_handle *handle);
-extern int for_each_chain4(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *),
-		int verbose, int builtinstoo, struct iptc_handle *handle);
+		      struct xtc_handle **handle, bool restore);
+extern int delete_chain4(const xt_chainlabel chain, int verbose,
+			struct xtc_handle *handle);
+extern int flush_entries4(const xt_chainlabel chain, int verbose, 
+			struct xtc_handle *handle);
+extern int for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *),
+		int verbose, int builtinstoo, struct xtc_handle *handle);
 extern void print_rule4(const struct ipt_entry *e,
-		struct iptc_handle *handle, const char *chain, int counters);
-
-/* kernel revision handling */
-extern int kernel_version;
-extern void get_kernel_version(void);
-#define LINUX_VERSION(x,y,z)	(0x10000*(x) + 0x100*(y) + z)
-#define LINUX_VERSION_MAJOR(x)	(((x)>>16) & 0xFF)
-#define LINUX_VERSION_MINOR(x)	(((x)>> 8) & 0xFF)
-#define LINUX_VERSION_PATCH(x)	( (x)      & 0xFF)
+		struct xtc_handle *handle, const char *chain, int counters);
 
 extern struct xtables_globals iptables_globals;
 
diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h
index 4f2d1f8..9aed80a 100644
--- a/include/libiptc/libip6tc.h
+++ b/include/libiptc/libip6tc.h
@@ -10,10 +10,10 @@
 #	include <limits.h> /* INT_MAX in ip6_tables.h */
 #endif
 #include <linux/netfilter_ipv6/ip6_tables.h>
+#include <libiptc/xtcshared.h>
 
-struct ip6tc_handle;
-
-typedef char ip6t_chainlabel[32];
+#define ip6tc_handle xtc_handle
+#define ip6t_chainlabel xt_chainlabel
 
 #define IP6TC_LABEL_ACCEPT "ACCEPT"
 #define IP6TC_LABEL_DROP "DROP"
@@ -21,132 +21,132 @@
 #define IP6TC_LABEL_RETURN "RETURN"
 
 /* Does this chain exist? */
-int ip6tc_is_chain(const char *chain, struct ip6tc_handle *const handle);
+int ip6tc_is_chain(const char *chain, struct xtc_handle *const handle);
 
 /* Take a snapshot of the rules. Returns NULL on error. */
-struct ip6tc_handle *ip6tc_init(const char *tablename);
+struct xtc_handle *ip6tc_init(const char *tablename);
 
 /* Cleanup after ip6tc_init(). */
-void ip6tc_free(struct ip6tc_handle *h);
+void ip6tc_free(struct xtc_handle *h);
 
 /* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *ip6tc_first_chain(struct ip6tc_handle *handle);
-const char *ip6tc_next_chain(struct ip6tc_handle *handle);
+const char *ip6tc_first_chain(struct xtc_handle *handle);
+const char *ip6tc_next_chain(struct xtc_handle *handle);
 
 /* Get first rule in the given chain: NULL for empty chain. */
 const struct ip6t_entry *ip6tc_first_rule(const char *chain,
-					  struct ip6tc_handle *handle);
+					  struct xtc_handle *handle);
 
 /* Returns NULL when rules run out. */
 const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
-					 struct ip6tc_handle *handle);
+					 struct xtc_handle *handle);
 
 /* Returns a pointer to the target name of this position. */
 const char *ip6tc_get_target(const struct ip6t_entry *e,
-			     struct ip6tc_handle *handle);
+			     struct xtc_handle *handle);
 
 /* Is this a built-in chain? */
-int ip6tc_builtin(const char *chain, struct ip6tc_handle *const handle);
+int ip6tc_builtin(const char *chain, struct xtc_handle *const handle);
 
 /* Get the policy of a given built-in chain */
 const char *ip6tc_get_policy(const char *chain,
-			     struct ip6t_counters *counters,
-			     struct ip6tc_handle *handle);
+			     struct xt_counters *counters,
+			     struct xtc_handle *handle);
 
 /* These functions return TRUE for OK or 0 and set errno. If errno ==
    0, it means there was a version error (ie. upgrade libiptc). */
 /* Rule numbers start at 1 for the first rule. */
 
 /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
-int ip6tc_insert_entry(const ip6t_chainlabel chain,
+int ip6tc_insert_entry(const xt_chainlabel chain,
 		       const struct ip6t_entry *e,
 		       unsigned int rulenum,
-		       struct ip6tc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
-int ip6tc_replace_entry(const ip6t_chainlabel chain,
+int ip6tc_replace_entry(const xt_chainlabel chain,
 			const struct ip6t_entry *e,
 			unsigned int rulenum,
-			struct ip6tc_handle *handle);
+			struct xtc_handle *handle);
 
 /* Append entry `fw' to chain `chain'. Equivalent to insert with
    rulenum = length of chain. */
-int ip6tc_append_entry(const ip6t_chainlabel chain,
+int ip6tc_append_entry(const xt_chainlabel chain,
 		       const struct ip6t_entry *e,
-		       struct ip6tc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* Check whether a matching rule exists */
-int ip6tc_check_entry(const ip6t_chainlabel chain,
+int ip6tc_check_entry(const xt_chainlabel chain,
 		       const struct ip6t_entry *origfw,
 		       unsigned char *matchmask,
-		       struct ip6tc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* Delete the first rule in `chain' which matches `fw'. */
-int ip6tc_delete_entry(const ip6t_chainlabel chain,
+int ip6tc_delete_entry(const xt_chainlabel chain,
 		       const struct ip6t_entry *origfw,
 		       unsigned char *matchmask,
-		       struct ip6tc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* Delete the rule in position `rulenum' in `chain'. */
-int ip6tc_delete_num_entry(const ip6t_chainlabel chain,
+int ip6tc_delete_num_entry(const xt_chainlabel chain,
 			   unsigned int rulenum,
-			   struct ip6tc_handle *handle);
+			   struct xtc_handle *handle);
 
 /* Check the packet `fw' on chain `chain'. Returns the verdict, or
    NULL and sets errno. */
-const char *ip6tc_check_packet(const ip6t_chainlabel chain,
+const char *ip6tc_check_packet(const xt_chainlabel chain,
 			       struct ip6t_entry *,
-			       struct ip6tc_handle *handle);
+			       struct xtc_handle *handle);
 
 /* Flushes the entries in the given chain (ie. empties chain). */
-int ip6tc_flush_entries(const ip6t_chainlabel chain,
-			struct ip6tc_handle *handle);
+int ip6tc_flush_entries(const xt_chainlabel chain,
+			struct xtc_handle *handle);
 
 /* Zeroes the counters in a chain. */
-int ip6tc_zero_entries(const ip6t_chainlabel chain,
-		       struct ip6tc_handle *handle);
+int ip6tc_zero_entries(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
 
 /* Creates a new chain. */
-int ip6tc_create_chain(const ip6t_chainlabel chain,
-		       struct ip6tc_handle *handle);
+int ip6tc_create_chain(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
 
 /* Deletes a chain. */
-int ip6tc_delete_chain(const ip6t_chainlabel chain,
-		       struct ip6tc_handle *handle);
+int ip6tc_delete_chain(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
 
 /* Renames a chain. */
-int ip6tc_rename_chain(const ip6t_chainlabel oldname,
-		       const ip6t_chainlabel newname,
-		       struct ip6tc_handle *handle);
+int ip6tc_rename_chain(const xt_chainlabel oldname,
+		       const xt_chainlabel newname,
+		       struct xtc_handle *handle);
 
 /* Sets the policy on a built-in chain. */
-int ip6tc_set_policy(const ip6t_chainlabel chain,
-		     const ip6t_chainlabel policy,
-		     struct ip6t_counters *counters,
-		     struct ip6tc_handle *handle);
+int ip6tc_set_policy(const xt_chainlabel chain,
+		     const xt_chainlabel policy,
+		     struct xt_counters *counters,
+		     struct xtc_handle *handle);
 
 /* Get the number of references to this chain */
-int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain,
-			 struct ip6tc_handle *handle);
+int ip6tc_get_references(unsigned int *ref, const xt_chainlabel chain,
+			 struct xtc_handle *handle);
 
 /* read packet and byte counters for a specific rule */
-struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain,
+struct xt_counters *ip6tc_read_counter(const xt_chainlabel chain,
 					unsigned int rulenum,
-					struct ip6tc_handle *handle);
+					struct xtc_handle *handle);
 
 /* zero packet and byte counters for a specific rule */
-int ip6tc_zero_counter(const ip6t_chainlabel chain,
+int ip6tc_zero_counter(const xt_chainlabel chain,
 		       unsigned int rulenum,
-		       struct ip6tc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* set packet and byte counters for a specific rule */
-int ip6tc_set_counter(const ip6t_chainlabel chain,
+int ip6tc_set_counter(const xt_chainlabel chain,
 		      unsigned int rulenum,
-		      struct ip6t_counters *counters,
-		      struct ip6tc_handle *handle);
+		      struct xt_counters *counters,
+		      struct xtc_handle *handle);
 
 /* Makes the actual changes. */
-int ip6tc_commit(struct ip6tc_handle *handle);
+int ip6tc_commit(struct xtc_handle *handle);
 
 /* Get raw socket. */
 int ip6tc_get_raw_socket(void);
@@ -154,9 +154,8 @@
 /* Translates errno numbers into more human-readable form than strerror. */
 const char *ip6tc_strerror(int err);
 
-/* Return prefix length, or -1 if not contiguous */
-int ipv6_prefix_length(const struct in6_addr *a);
+extern void dump_entries6(struct xtc_handle *const);
 
-extern void dump_entries6(struct ip6tc_handle *const);
+extern const struct xtc_ops ip6tc_ops;
 
 #endif /* _LIBIP6TC_H */
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
index 3497d6a..24cdbdb 100644
--- a/include/libiptc/libiptc.h
+++ b/include/libiptc/libiptc.h
@@ -10,14 +10,14 @@
 #	include <limits.h> /* INT_MAX in ip_tables.h */
 #endif
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <libiptc/xtcshared.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-struct iptc_handle;
-
-typedef char ipt_chainlabel[32];
+#define iptc_handle xtc_handle
+#define ipt_chainlabel xt_chainlabel
 
 #define IPTC_LABEL_ACCEPT  "ACCEPT"
 #define IPTC_LABEL_DROP    "DROP"
@@ -25,134 +25,134 @@
 #define IPTC_LABEL_RETURN  "RETURN"
 
 /* Does this chain exist? */
-int iptc_is_chain(const char *chain, struct iptc_handle *const handle);
+int iptc_is_chain(const char *chain, struct xtc_handle *const handle);
 
 /* Take a snapshot of the rules.  Returns NULL on error. */
-struct iptc_handle *iptc_init(const char *tablename);
+struct xtc_handle *iptc_init(const char *tablename);
 
 /* Cleanup after iptc_init(). */
-void iptc_free(struct iptc_handle *h);
+void iptc_free(struct xtc_handle *h);
 
 /* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *iptc_first_chain(struct iptc_handle *handle);
-const char *iptc_next_chain(struct iptc_handle *handle);
+const char *iptc_first_chain(struct xtc_handle *handle);
+const char *iptc_next_chain(struct xtc_handle *handle);
 
 /* Get first rule in the given chain: NULL for empty chain. */
 const struct ipt_entry *iptc_first_rule(const char *chain,
-					struct iptc_handle *handle);
+					struct xtc_handle *handle);
 
 /* Returns NULL when rules run out. */
 const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
-				       struct iptc_handle *handle);
+				       struct xtc_handle *handle);
 
 /* Returns a pointer to the target name of this entry. */
 const char *iptc_get_target(const struct ipt_entry *e,
-			    struct iptc_handle *handle);
+			    struct xtc_handle *handle);
 
 /* Is this a built-in chain? */
-int iptc_builtin(const char *chain, struct iptc_handle *const handle);
+int iptc_builtin(const char *chain, struct xtc_handle *const handle);
 
 /* Get the policy of a given built-in chain */
 const char *iptc_get_policy(const char *chain,
-			    struct ipt_counters *counter,
-			    struct iptc_handle *handle);
+			    struct xt_counters *counter,
+			    struct xtc_handle *handle);
 
 /* These functions return TRUE for OK or 0 and set errno.  If errno ==
    0, it means there was a version error (ie. upgrade libiptc). */
 /* Rule numbers start at 1 for the first rule. */
 
 /* Insert the entry `e' in chain `chain' into position `rulenum'. */
-int iptc_insert_entry(const ipt_chainlabel chain,
+int iptc_insert_entry(const xt_chainlabel chain,
 		      const struct ipt_entry *e,
 		      unsigned int rulenum,
-		      struct iptc_handle *handle);
+		      struct xtc_handle *handle);
 
 /* Atomically replace rule `rulenum' in `chain' with `e'. */
-int iptc_replace_entry(const ipt_chainlabel chain,
+int iptc_replace_entry(const xt_chainlabel chain,
 		       const struct ipt_entry *e,
 		       unsigned int rulenum,
-		       struct iptc_handle *handle);
+		       struct xtc_handle *handle);
 
 /* Append entry `e' to chain `chain'.  Equivalent to insert with
    rulenum = length of chain. */
-int iptc_append_entry(const ipt_chainlabel chain,
+int iptc_append_entry(const xt_chainlabel chain,
 		      const struct ipt_entry *e,
-		      struct iptc_handle *handle);
+		      struct xtc_handle *handle);
 
 /* Check whether a mathching rule exists */
-int iptc_check_entry(const ipt_chainlabel chain,
+int iptc_check_entry(const xt_chainlabel chain,
 		      const struct ipt_entry *origfw,
 		      unsigned char *matchmask,
-		      struct iptc_handle *handle);
+		      struct xtc_handle *handle);
 
 /* Delete the first rule in `chain' which matches `e', subject to
    matchmask (array of length == origfw) */
-int iptc_delete_entry(const ipt_chainlabel chain,
+int iptc_delete_entry(const xt_chainlabel chain,
 		      const struct ipt_entry *origfw,
 		      unsigned char *matchmask,
-		      struct iptc_handle *handle);
+		      struct xtc_handle *handle);
 
 /* Delete the rule in position `rulenum' in `chain'. */
-int iptc_delete_num_entry(const ipt_chainlabel chain,
+int iptc_delete_num_entry(const xt_chainlabel chain,
 			  unsigned int rulenum,
-			  struct iptc_handle *handle);
+			  struct xtc_handle *handle);
 
 /* Check the packet `e' on chain `chain'.  Returns the verdict, or
    NULL and sets errno. */
-const char *iptc_check_packet(const ipt_chainlabel chain,
+const char *iptc_check_packet(const xt_chainlabel chain,
 			      struct ipt_entry *entry,
-			      struct iptc_handle *handle);
+			      struct xtc_handle *handle);
 
 /* Flushes the entries in the given chain (ie. empties chain). */
-int iptc_flush_entries(const ipt_chainlabel chain,
-		       struct iptc_handle *handle);
+int iptc_flush_entries(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
 
 /* Zeroes the counters in a chain. */
-int iptc_zero_entries(const ipt_chainlabel chain,
-		      struct iptc_handle *handle);
+int iptc_zero_entries(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
 
 /* Creates a new chain. */
-int iptc_create_chain(const ipt_chainlabel chain,
-		      struct iptc_handle *handle);
+int iptc_create_chain(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
 
 /* Deletes a chain. */
-int iptc_delete_chain(const ipt_chainlabel chain,
-		      struct iptc_handle *handle);
+int iptc_delete_chain(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
 
 /* Renames a chain. */
-int iptc_rename_chain(const ipt_chainlabel oldname,
-		      const ipt_chainlabel newname,
-		      struct iptc_handle *handle);
+int iptc_rename_chain(const xt_chainlabel oldname,
+		      const xt_chainlabel newname,
+		      struct xtc_handle *handle);
 
 /* Sets the policy on a built-in chain. */
-int iptc_set_policy(const ipt_chainlabel chain,
-		    const ipt_chainlabel policy,
-		    struct ipt_counters *counters,
-		    struct iptc_handle *handle);
+int iptc_set_policy(const xt_chainlabel chain,
+		    const xt_chainlabel policy,
+		    struct xt_counters *counters,
+		    struct xtc_handle *handle);
 
 /* Get the number of references to this chain */
 int iptc_get_references(unsigned int *ref,
-			const ipt_chainlabel chain,
-			struct iptc_handle *handle);
+			const xt_chainlabel chain,
+			struct xtc_handle *handle);
 
 /* read packet and byte counters for a specific rule */
-struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
+struct xt_counters *iptc_read_counter(const xt_chainlabel chain,
 				       unsigned int rulenum,
-				       struct iptc_handle *handle);
+				       struct xtc_handle *handle);
 
 /* zero packet and byte counters for a specific rule */
-int iptc_zero_counter(const ipt_chainlabel chain,
+int iptc_zero_counter(const xt_chainlabel chain,
 		      unsigned int rulenum,
-		      struct iptc_handle *handle);
+		      struct xtc_handle *handle);
 
 /* set packet and byte counters for a specific rule */
-int iptc_set_counter(const ipt_chainlabel chain,
+int iptc_set_counter(const xt_chainlabel chain,
 		     unsigned int rulenum,
-		     struct ipt_counters *counters,
-		     struct iptc_handle *handle);
+		     struct xt_counters *counters,
+		     struct xtc_handle *handle);
 
 /* Makes the actual changes. */
-int iptc_commit(struct iptc_handle *handle);
+int iptc_commit(struct xtc_handle *handle);
 
 /* Get raw socket. */
 int iptc_get_raw_socket(void);
@@ -160,7 +160,9 @@
 /* Translates errno numbers into more human-readable form than strerror. */
 const char *iptc_strerror(int err);
 
-extern void dump_entries(struct iptc_handle *const);
+extern void dump_entries(struct xtc_handle *const);
+
+extern const struct xtc_ops iptc_ops;
 
 #ifdef __cplusplus
 }
diff --git a/include/libiptc/xtcshared.h b/include/libiptc/xtcshared.h
new file mode 100644
index 0000000..773ebc4
--- /dev/null
+++ b/include/libiptc/xtcshared.h
@@ -0,0 +1,20 @@
+#ifndef _LIBXTC_SHARED_H
+#define _LIBXTC_SHARED_H 1
+
+typedef char xt_chainlabel[32];
+struct xtc_handle;
+struct xt_counters;
+
+struct xtc_ops {
+	int (*commit)(struct xtc_handle *);
+	void (*free)(struct xtc_handle *);
+	int (*builtin)(const char *, struct xtc_handle *const);
+	int (*is_chain)(const char *, struct xtc_handle *const);
+	int (*flush_entries)(const xt_chainlabel, struct xtc_handle *);
+	int (*create_chain)(const xt_chainlabel, struct xtc_handle *);
+	int (*set_policy)(const xt_chainlabel, const xt_chainlabel,
+			  struct xt_counters *, struct xtc_handle *);
+	const char *(*strerror)(int);
+};
+
+#endif /* _LIBXTC_SHARED_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d1671a0..d4c59f6 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -8,7 +8,6 @@
 #define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))
 
 
-
 #define SI_LOAD_SHIFT	16
 struct sysinfo {
 	long uptime;			/* Seconds since boot */
@@ -27,36 +26,4 @@
 	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
 };
 
-/* Force a compilation error if condition is true */
-#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
-
-/* Force a compilation error if condition is constant and true */
-#define MAYBE_BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)]))
-
-/* Force a compilation error if a constant expression is not a power of 2 */
-#define BUILD_BUG_ON_NOT_POWER_OF_2(n)			\
-	BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0))
-
-/* Force a compilation error if condition is true, but also produce a
-   result (of value 0 and type size_t), so the expression can be used
-   e.g. in a structure initializer (or where-ever else comma expressions
-   aren't permitted). */
-#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
-#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
-
-/* Trap pasters of __FUNCTION__ at compile-time */
-#define __FUNCTION__ (__func__)
-
-/* This helps us to avoid #ifdef CONFIG_NUMA */
-#ifdef CONFIG_NUMA
-#define NUMA_BUILD 1
-#else
-#define NUMA_BUILD 0
-#endif
-
-/* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */
-#ifdef CONFIG_FTRACE_MCOUNT_RECORD
-# define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD
-#endif
-
 #endif
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 2eb00b6..5477131 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -3,6 +3,7 @@
 
 #include <linux/types.h>
 
+#include <linux/sysctl.h>
 
 /* Responses from hook functions. */
 #define NF_DROP 0
@@ -14,14 +15,20 @@
 #define NF_MAX_VERDICT NF_STOP
 
 /* we overload the higher bits for encoding auxiliary data such as the queue
- * number. Not nice, but better than additional function arguments. */
-#define NF_VERDICT_MASK 0x0000ffff
-#define NF_VERDICT_BITS 16
+ * number or errno values. Not nice, but better than additional function
+ * arguments. */
+#define NF_VERDICT_MASK 0x000000ff
 
+/* extra verdict flags have mask 0x0000ff00 */
+#define NF_VERDICT_FLAG_QUEUE_BYPASS	0x00008000
+
+/* queue number (NF_QUEUE) or errno (NF_DROP) */
 #define NF_VERDICT_QMASK 0xffff0000
 #define NF_VERDICT_QBITS 16
 
-#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+#define NF_QUEUE_NR(x) ((((x) << 16) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+#define NF_DROP_ERR(x) (((-x) << 16) | NF_DROP)
 
 /* only for userspace compatibility */
 /* Generic cache responses from hook functions.
@@ -29,6 +36,9 @@
 #define NFC_UNKNOWN 0x4000
 #define NFC_ALTERED 0x8000
 
+/* NF_VERDICT_BITS should be 8 now, but userspace might break if this changes */
+#define NF_VERDICT_BITS 16
+
 enum nf_inet_hooks {
 	NF_INET_PRE_ROUTING,
 	NF_INET_LOCAL_IN,
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
new file mode 100644
index 0000000..eb9123e
--- /dev/null
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -0,0 +1,259 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _UAPI_IP_SET_H
+#define _UAPI_IP_SET_H
+
+
+#include <linux/types.h>
+
+/* The protocol version */
+#define IPSET_PROTOCOL		6
+
+/* The max length of strings including NUL: set and type identifiers */
+#define IPSET_MAXNAMELEN	32
+
+/* Message types and commands */
+enum ipset_cmd {
+	IPSET_CMD_NONE,
+	IPSET_CMD_PROTOCOL,	/* 1: Return protocol version */
+	IPSET_CMD_CREATE,	/* 2: Create a new (empty) set */
+	IPSET_CMD_DESTROY,	/* 3: Destroy a (empty) set */
+	IPSET_CMD_FLUSH,	/* 4: Remove all elements from a set */
+	IPSET_CMD_RENAME,	/* 5: Rename a set */
+	IPSET_CMD_SWAP,		/* 6: Swap two sets */
+	IPSET_CMD_LIST,		/* 7: List sets */
+	IPSET_CMD_SAVE,		/* 8: Save sets */
+	IPSET_CMD_ADD,		/* 9: Add an element to a set */
+	IPSET_CMD_DEL,		/* 10: Delete an element from a set */
+	IPSET_CMD_TEST,		/* 11: Test an element in a set */
+	IPSET_CMD_HEADER,	/* 12: Get set header data only */
+	IPSET_CMD_TYPE,		/* 13: Get set type */
+	IPSET_MSG_MAX,		/* Netlink message commands */
+
+	/* Commands in userspace: */
+	IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
+	IPSET_CMD_HELP,		/* 15: Get help */
+	IPSET_CMD_VERSION,	/* 16: Get program version */
+	IPSET_CMD_QUIT,		/* 17: Quit from interactive mode */
+
+	IPSET_CMD_MAX,
+
+	IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
+};
+
+/* Attributes at command level */
+enum {
+	IPSET_ATTR_UNSPEC,
+	IPSET_ATTR_PROTOCOL,	/* 1: Protocol version */
+	IPSET_ATTR_SETNAME,	/* 2: Name of the set */
+	IPSET_ATTR_TYPENAME,	/* 3: Typename */
+	IPSET_ATTR_SETNAME2 = IPSET_ATTR_TYPENAME, /* Setname at rename/swap */
+	IPSET_ATTR_REVISION,	/* 4: Settype revision */
+	IPSET_ATTR_FAMILY,	/* 5: Settype family */
+	IPSET_ATTR_FLAGS,	/* 6: Flags at command level */
+	IPSET_ATTR_DATA,	/* 7: Nested attributes */
+	IPSET_ATTR_ADT,		/* 8: Multiple data containers */
+	IPSET_ATTR_LINENO,	/* 9: Restore lineno */
+	IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
+	IPSET_ATTR_REVISION_MIN	= IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
+	__IPSET_ATTR_CMD_MAX,
+};
+#define IPSET_ATTR_CMD_MAX	(__IPSET_ATTR_CMD_MAX - 1)
+
+/* CADT specific attributes */
+enum {
+	IPSET_ATTR_IP = IPSET_ATTR_UNSPEC + 1,
+	IPSET_ATTR_IP_FROM = IPSET_ATTR_IP,
+	IPSET_ATTR_IP_TO,	/* 2 */
+	IPSET_ATTR_CIDR,	/* 3 */
+	IPSET_ATTR_PORT,	/* 4 */
+	IPSET_ATTR_PORT_FROM = IPSET_ATTR_PORT,
+	IPSET_ATTR_PORT_TO,	/* 5 */
+	IPSET_ATTR_TIMEOUT,	/* 6 */
+	IPSET_ATTR_PROTO,	/* 7 */
+	IPSET_ATTR_CADT_FLAGS,	/* 8 */
+	IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,	/* 9 */
+	/* Reserve empty slots */
+	IPSET_ATTR_CADT_MAX = 16,
+	/* Create-only specific attributes */
+	IPSET_ATTR_GC,
+	IPSET_ATTR_HASHSIZE,
+	IPSET_ATTR_MAXELEM,
+	IPSET_ATTR_NETMASK,
+	IPSET_ATTR_PROBES,
+	IPSET_ATTR_RESIZE,
+	IPSET_ATTR_SIZE,
+	/* Kernel-only */
+	IPSET_ATTR_ELEMENTS,
+	IPSET_ATTR_REFERENCES,
+	IPSET_ATTR_MEMSIZE,
+
+	__IPSET_ATTR_CREATE_MAX,
+};
+#define IPSET_ATTR_CREATE_MAX	(__IPSET_ATTR_CREATE_MAX - 1)
+
+/* ADT specific attributes */
+enum {
+	IPSET_ATTR_ETHER = IPSET_ATTR_CADT_MAX + 1,
+	IPSET_ATTR_NAME,
+	IPSET_ATTR_NAMEREF,
+	IPSET_ATTR_IP2,
+	IPSET_ATTR_CIDR2,
+	IPSET_ATTR_IP2_TO,
+	IPSET_ATTR_IFACE,
+	IPSET_ATTR_BYTES,
+	IPSET_ATTR_PACKETS,
+	__IPSET_ATTR_ADT_MAX,
+};
+#define IPSET_ATTR_ADT_MAX	(__IPSET_ATTR_ADT_MAX - 1)
+
+/* IP specific attributes */
+enum {
+	IPSET_ATTR_IPADDR_IPV4 = IPSET_ATTR_UNSPEC + 1,
+	IPSET_ATTR_IPADDR_IPV6,
+	__IPSET_ATTR_IPADDR_MAX,
+};
+#define IPSET_ATTR_IPADDR_MAX	(__IPSET_ATTR_IPADDR_MAX - 1)
+
+/* Error codes */
+enum ipset_errno {
+	IPSET_ERR_PRIVATE = 4096,
+	IPSET_ERR_PROTOCOL,
+	IPSET_ERR_FIND_TYPE,
+	IPSET_ERR_MAX_SETS,
+	IPSET_ERR_BUSY,
+	IPSET_ERR_EXIST_SETNAME2,
+	IPSET_ERR_TYPE_MISMATCH,
+	IPSET_ERR_EXIST,
+	IPSET_ERR_INVALID_CIDR,
+	IPSET_ERR_INVALID_NETMASK,
+	IPSET_ERR_INVALID_FAMILY,
+	IPSET_ERR_TIMEOUT,
+	IPSET_ERR_REFERENCED,
+	IPSET_ERR_IPADDR_IPV4,
+	IPSET_ERR_IPADDR_IPV6,
+	IPSET_ERR_COUNTER,
+
+	/* Type specific error codes */
+	IPSET_ERR_TYPE_SPECIFIC = 4352,
+};
+
+/* Flags at command level or match/target flags, lower half of cmdattrs */
+enum ipset_cmd_flags {
+	IPSET_FLAG_BIT_EXIST	= 0,
+	IPSET_FLAG_EXIST	= (1 << IPSET_FLAG_BIT_EXIST),
+	IPSET_FLAG_BIT_LIST_SETNAME = 1,
+	IPSET_FLAG_LIST_SETNAME	= (1 << IPSET_FLAG_BIT_LIST_SETNAME),
+	IPSET_FLAG_BIT_LIST_HEADER = 2,
+	IPSET_FLAG_LIST_HEADER	= (1 << IPSET_FLAG_BIT_LIST_HEADER),
+	IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE = 3,
+	IPSET_FLAG_SKIP_COUNTER_UPDATE =
+		(1 << IPSET_FLAG_BIT_SKIP_COUNTER_UPDATE),
+	IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE = 4,
+	IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE =
+		(1 << IPSET_FLAG_BIT_SKIP_SUBCOUNTER_UPDATE),
+	IPSET_FLAG_BIT_MATCH_COUNTERS = 5,
+	IPSET_FLAG_MATCH_COUNTERS = (1 << IPSET_FLAG_BIT_MATCH_COUNTERS),
+	IPSET_FLAG_BIT_RETURN_NOMATCH = 7,
+	IPSET_FLAG_RETURN_NOMATCH = (1 << IPSET_FLAG_BIT_RETURN_NOMATCH),
+	IPSET_FLAG_CMD_MAX = 15,
+};
+
+/* Flags at CADT attribute level, upper half of cmdattrs */
+enum ipset_cadt_flags {
+	IPSET_FLAG_BIT_BEFORE	= 0,
+	IPSET_FLAG_BEFORE	= (1 << IPSET_FLAG_BIT_BEFORE),
+	IPSET_FLAG_BIT_PHYSDEV	= 1,
+	IPSET_FLAG_PHYSDEV	= (1 << IPSET_FLAG_BIT_PHYSDEV),
+	IPSET_FLAG_BIT_NOMATCH	= 2,
+	IPSET_FLAG_NOMATCH	= (1 << IPSET_FLAG_BIT_NOMATCH),
+	IPSET_FLAG_BIT_WITH_COUNTERS = 3,
+	IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS),
+	IPSET_FLAG_CADT_MAX	= 15,
+};
+
+/* Commands with settype-specific attributes */
+enum ipset_adt {
+	IPSET_ADD,
+	IPSET_DEL,
+	IPSET_TEST,
+	IPSET_ADT_MAX,
+	IPSET_CREATE = IPSET_ADT_MAX,
+	IPSET_CADT_MAX,
+};
+
+/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
+ * and IPSET_INVALID_ID if you want to increase the max number of sets.
+ */
+typedef __u16 ip_set_id_t;
+
+#define IPSET_INVALID_ID		65535
+
+enum ip_set_dim {
+	IPSET_DIM_ZERO = 0,
+	IPSET_DIM_ONE,
+	IPSET_DIM_TWO,
+	IPSET_DIM_THREE,
+	/* Max dimension in elements.
+	 * If changed, new revision of iptables match/target is required.
+	 */
+	IPSET_DIM_MAX = 6,
+	/* Backward compatibility: set match revision 2 */
+	IPSET_BIT_RETURN_NOMATCH = 7,
+};
+
+/* Option flags for kernel operations */
+enum ip_set_kopt {
+	IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
+	IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
+	IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
+	IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
+	IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
+};
+
+enum {
+	IPSET_COUNTER_NONE = 0,
+	IPSET_COUNTER_EQ,
+	IPSET_COUNTER_NE,
+	IPSET_COUNTER_LT,
+	IPSET_COUNTER_GT,
+};
+
+struct ip_set_counter_match {
+	__u8 op;
+	__u64 value;
+};
+
+/* Interface to iptables/ip6tables */
+
+#define SO_IP_SET		83
+
+union ip_set_name_index {
+	char name[IPSET_MAXNAMELEN];
+	ip_set_id_t index;
+};
+
+#define IP_SET_OP_GET_BYNAME	0x00000006	/* Get set index by name */
+struct ip_set_req_get_set {
+	unsigned int op;
+	unsigned int version;
+	union ip_set_name_index set;
+};
+
+#define IP_SET_OP_GET_BYINDEX	0x00000007	/* Get set name by index */
+/* Uses ip_set_req_get_set */
+
+#define IP_SET_OP_VERSION	0x00000100	/* Ask kernel version */
+struct ip_set_req_version {
+	unsigned int op;
+	unsigned int version;
+};
+
+#endif /* _UAPI_IP_SET_H */
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index 34a7fc6..38aa52d 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -18,6 +18,9 @@
 	/* >= this indicates reply direction */
 	IP_CT_IS_REPLY,
 
+	IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
+	IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
+	IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY,	
 	/* Number of distinct IP_CT types (no NEW in reply dirn). */
 	IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
 };
@@ -76,6 +79,10 @@
 	/* Conntrack is a template */
 	IPS_TEMPLATE_BIT = 11,
 	IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
+
+	/* Conntrack is a fake untracked entry */
+	IPS_UNTRACKED_BIT = 12,
+	IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
 };
 
 /* Connection tracking event types */
@@ -94,6 +101,13 @@
 
 enum ip_conntrack_expect_events {
 	IPEXP_NEW,		/* new expectation */
+	IPEXP_DESTROY,		/* destroyed expectation */
 };
 
+/* expectation flags */
+#define NF_CT_EXPECT_PERMANENT		0x1
+#define NF_CT_EXPECT_INACTIVE		0x2
+#define NF_CT_EXPECT_USERSPACE		0x4
+
+
 #endif /* _NF_CONNTRACK_COMMON_H */
diff --git a/include/linux/netfilter/nf_conntrack_tuple_common.h b/include/linux/netfilter/nf_conntrack_tuple_common.h
index 8e145f0..2f6bbc5 100644
--- a/include/linux/netfilter/nf_conntrack_tuple_common.h
+++ b/include/linux/netfilter/nf_conntrack_tuple_common.h
@@ -1,13 +1,39 @@
 #ifndef _NF_CONNTRACK_TUPLE_COMMON_H
 #define _NF_CONNTRACK_TUPLE_COMMON_H
 
-enum ip_conntrack_dir
-{
+enum ip_conntrack_dir {
 	IP_CT_DIR_ORIGINAL,
 	IP_CT_DIR_REPLY,
 	IP_CT_DIR_MAX
 };
 
+/* The protocol-specific manipulable parts of the tuple: always in
+ * network order
+ */
+union nf_conntrack_man_proto {
+	/* Add other protocols here. */
+	__be16 all;
+
+	struct {
+		__be16 port;
+	} tcp;
+	struct {
+		__be16 port;
+	} udp;
+	struct {
+		__be16 id;
+	} icmp;
+	struct {
+		__be16 port;
+	} dccp;
+	struct {
+		__be16 port;
+	} sctp;
+	struct {
+		__be16 key;	/* GRE key is 32bit, PPtP only uses 16bit */
+	} gre;
+};
+
 #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
 
 #endif /* _NF_CONNTRACK_TUPLE_COMMON_H */
diff --git a/include/linux/netfilter/nf_nat.h b/include/linux/netfilter/nf_nat.h
new file mode 100644
index 0000000..bf0cc37
--- /dev/null
+++ b/include/linux/netfilter/nf_nat.h
@@ -0,0 +1,33 @@
+#ifndef _NETFILTER_NF_NAT_H
+#define _NETFILTER_NF_NAT_H
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+
+#define NF_NAT_RANGE_MAP_IPS		1
+#define NF_NAT_RANGE_PROTO_SPECIFIED	2
+#define NF_NAT_RANGE_PROTO_RANDOM	4
+#define NF_NAT_RANGE_PERSISTENT		8
+
+struct nf_nat_ipv4_range {
+	unsigned int			flags;
+	__be32				min_ip;
+	__be32				max_ip;
+	union nf_conntrack_man_proto	min;
+	union nf_conntrack_man_proto	max;
+};
+
+struct nf_nat_ipv4_multi_range_compat {
+	unsigned int			rangesize;
+	struct nf_nat_ipv4_range	range[1];
+};
+
+struct nf_nat_range {
+	unsigned int			flags;
+	union nf_inet_addr		min_addr;
+	union nf_inet_addr		max_addr;
+	union nf_conntrack_man_proto	min_proto;
+	union nf_conntrack_man_proto	max_proto;
+};
+
+#endif /* _NETFILTER_NF_NAT_H */
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index fa2d957..4120970 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -66,6 +66,11 @@
 	int verdict;
 };
 
+struct xt_error_target {
+	struct xt_entry_target target;
+	char errorname[XT_FUNCTION_MAXNAMELEN];
+};
+
 /* The argument to IPT_SO_GET_REVISION_*.  Returns highest revision
  * kernel supports, if >= revision. */
 struct xt_get_revision {
diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h
index fbf4c56..54528fd 100644
--- a/include/linux/netfilter/xt_CT.h
+++ b/include/linux/netfilter/xt_CT.h
@@ -1,7 +1,12 @@
 #ifndef _XT_CT_H
 #define _XT_CT_H
 
-#define XT_CT_NOTRACK	0x1
+#include <linux/types.h>
+
+enum {
+	XT_CT_NOTRACK		= 1 << 0,
+	XT_CT_NOTRACK_ALIAS	= 1 << 1,
+};
 
 struct xt_ct_target_info {
 	__u16 flags;
@@ -14,4 +19,16 @@
 	struct nf_conn	*ct __attribute__((aligned(8)));
 };
 
+struct xt_ct_target_info_v1 {
+	__u16 flags;
+	__u16 zone;
+	__u32 ct_events;
+	__u32 exp_events;
+	char helper[16];
+	char timeout[32];
+
+	/* Used internally by the kernel */
+	struct nf_conn	*ct __attribute__((aligned(8)));
+};
+
 #endif /* _XT_CT_H */
diff --git a/include/linux/netfilter/xt_HMARK.h b/include/linux/netfilter/xt_HMARK.h
new file mode 100644
index 0000000..826fc58
--- /dev/null
+++ b/include/linux/netfilter/xt_HMARK.h
@@ -0,0 +1,50 @@
+#ifndef XT_HMARK_H_
+#define XT_HMARK_H_
+
+#include <linux/types.h>
+
+enum {
+	XT_HMARK_SADDR_MASK,
+	XT_HMARK_DADDR_MASK,
+	XT_HMARK_SPI,
+	XT_HMARK_SPI_MASK,
+	XT_HMARK_SPORT,
+	XT_HMARK_DPORT,
+	XT_HMARK_SPORT_MASK,
+	XT_HMARK_DPORT_MASK,
+	XT_HMARK_PROTO_MASK,
+	XT_HMARK_RND,
+	XT_HMARK_MODULUS,
+	XT_HMARK_OFFSET,
+	XT_HMARK_CT,
+	XT_HMARK_METHOD_L3,
+	XT_HMARK_METHOD_L3_4,
+};
+#define XT_HMARK_FLAG(flag)	(1 << flag)
+
+union hmark_ports {
+	struct {
+		__u16	src;
+		__u16	dst;
+	} p16;
+	struct {
+		__be16	src;
+		__be16	dst;
+	} b16;
+	__u32	v32;
+	__be32	b32;
+};
+
+struct xt_hmark_info {
+	union nf_inet_addr	src_mask;
+	union nf_inet_addr	dst_mask;
+	union hmark_ports	port_mask;
+	union hmark_ports	port_set;
+	__u32			flags;
+	__u16			proto_mask;
+	__u32			hashrnd;
+	__u32			hmodulus;
+	__u32			hoffset;	/* Mark offset to start from */
+};
+
+#endif /* XT_HMARK_H_ */
diff --git a/include/linux/netfilter/xt_NFQUEUE.h b/include/linux/netfilter/xt_NFQUEUE.h
index 9eafdbb..8bb5fe6 100644
--- a/include/linux/netfilter/xt_NFQUEUE.h
+++ b/include/linux/netfilter/xt_NFQUEUE.h
@@ -26,4 +26,13 @@
 	__u16 bypass;
 };
 
+struct xt_NFQ_info_v3 {
+	__u16 queuenum;
+	__u16 queues_total;
+	__u16 flags;
+#define NFQ_FLAG_BYPASS		0x01 /* for compatibility with v2 */
+#define NFQ_FLAG_CPU_FANOUT	0x02 /* use current CPU (no hashing) */
+#define NFQ_FLAG_MASK		0x03
+};
+
 #endif /* _XT_NFQ_TARGET_H */
diff --git a/include/linux/netfilter/xt_TCPOPTSTRIP.h b/include/linux/netfilter/xt_TCPOPTSTRIP.h
index 342ef14..7157318 100644
--- a/include/linux/netfilter/xt_TCPOPTSTRIP.h
+++ b/include/linux/netfilter/xt_TCPOPTSTRIP.h
@@ -1,6 +1,8 @@
 #ifndef _XT_TCPOPTSTRIP_H
 #define _XT_TCPOPTSTRIP_H
 
+#include <linux/types.h>
+
 #define tcpoptstrip_set_bit(bmap, idx) \
 	(bmap[(idx) >> 5] |= 1U << (idx & 31))
 #define tcpoptstrip_test_bit(bmap, idx) \
diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h
index 8097e0b..902043c 100644
--- a/include/linux/netfilter/xt_TPROXY.h
+++ b/include/linux/netfilter/xt_TPROXY.h
@@ -1,6 +1,8 @@
 #ifndef _XT_TPROXY_H
 #define _XT_TPROXY_H
 
+#include <linux/types.h>
+
 /* TPROXY target is capable of marking the packet to perform
  * redirection. We can get rid of that whenever we get support for
  * mutliple targets in the same rule. */
diff --git a/include/linux/netfilter/xt_addrtype.h b/include/linux/netfilter/xt_addrtype.h
new file mode 100644
index 0000000..b156baa
--- /dev/null
+++ b/include/linux/netfilter/xt_addrtype.h
@@ -0,0 +1,44 @@
+#ifndef _XT_ADDRTYPE_H
+#define _XT_ADDRTYPE_H
+
+#include <linux/types.h>
+
+enum {
+	XT_ADDRTYPE_INVERT_SOURCE	= 0x0001,
+	XT_ADDRTYPE_INVERT_DEST		= 0x0002,
+	XT_ADDRTYPE_LIMIT_IFACE_IN	= 0x0004,
+	XT_ADDRTYPE_LIMIT_IFACE_OUT	= 0x0008,
+};
+
+
+/* rtn_type enum values from rtnetlink.h, but shifted */
+enum {
+	XT_ADDRTYPE_UNSPEC = 1 << 0,
+	XT_ADDRTYPE_UNICAST = 1 << 1,	/* 1 << RTN_UNICAST */
+	XT_ADDRTYPE_LOCAL  = 1 << 2,	/* 1 << RTN_LOCAL, etc */
+	XT_ADDRTYPE_BROADCAST = 1 << 3,
+	XT_ADDRTYPE_ANYCAST = 1 << 4,
+	XT_ADDRTYPE_MULTICAST = 1 << 5,
+	XT_ADDRTYPE_BLACKHOLE = 1 << 6,
+	XT_ADDRTYPE_UNREACHABLE = 1 << 7,
+	XT_ADDRTYPE_PROHIBIT = 1 << 8,
+	XT_ADDRTYPE_THROW = 1 << 9,
+	XT_ADDRTYPE_NAT = 1 << 10,
+	XT_ADDRTYPE_XRESOLVE = 1 << 11,
+};
+
+struct xt_addrtype_info_v1 {
+	__u16	source;		/* source-type mask */
+	__u16	dest;		/* dest-type mask */
+	__u32	flags;
+};
+
+/* revision 0 */
+struct xt_addrtype_info {
+	__u16	source;		/* source-type mask */
+	__u16	dest;		/* dest-type mask */
+	__u32	invert_source;
+	__u32	invert_dest;
+};
+
+#endif
diff --git a/include/linux/netfilter/xt_bpf.h b/include/linux/netfilter/xt_bpf.h
new file mode 100644
index 0000000..5dda450
--- /dev/null
+++ b/include/linux/netfilter/xt_bpf.h
@@ -0,0 +1,17 @@
+#ifndef _XT_BPF_H
+#define _XT_BPF_H
+
+#include <linux/filter.h>
+#include <linux/types.h>
+
+#define XT_BPF_MAX_NUM_INSTR	64
+
+struct xt_bpf_info {
+	__u16 bpf_program_num_elem;
+	struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
+
+	/* only used in the kernel */
+	struct sk_filter *filter __attribute__((aligned(8)));
+};
+
+#endif /*_XT_BPF_H */
diff --git a/include/linux/netfilter/xt_cluster.h b/include/linux/netfilter/xt_cluster.h
index 66cfa3c..9b883c8 100644
--- a/include/linux/netfilter/xt_cluster.h
+++ b/include/linux/netfilter/xt_cluster.h
@@ -1,6 +1,8 @@
 #ifndef _XT_CLUSTER_MATCH_H
 #define _XT_CLUSTER_MATCH_H
 
+#include <linux/types.h>
+
 enum xt_cluster_flags {
 	XT_CLUSTER_F_INV	= (1 << 0)
 };
diff --git a/include/linux/netfilter/xt_connbytes.h b/include/linux/netfilter/xt_connbytes.h
index 92fcbb0..f1d6c15 100644
--- a/include/linux/netfilter/xt_connbytes.h
+++ b/include/linux/netfilter/xt_connbytes.h
@@ -17,8 +17,8 @@
 
 struct xt_connbytes_info {
 	struct {
-		aligned_u64 from;	/* count to be matched */
-		aligned_u64 to;		/* count to be matched */
+		__aligned_u64 from;	/* count to be matched */
+		__aligned_u64 to;	/* count to be matched */
 	} count;
 	__u8 what;		/* ipt_connbytes_what */
 	__u8 direction;	/* ipt_connbytes_direction */
diff --git a/include/linux/netfilter/xt_connlabel.h b/include/linux/netfilter/xt_connlabel.h
new file mode 100644
index 0000000..c4bc9ee
--- /dev/null
+++ b/include/linux/netfilter/xt_connlabel.h
@@ -0,0 +1,12 @@
+#include <linux/types.h>
+
+#define XT_CONNLABEL_MAXBIT 127
+enum xt_connlabel_mtopts {
+	XT_CONNLABEL_OP_INVERT = 1 << 0,
+	XT_CONNLABEL_OP_SET    = 1 << 1,
+};
+
+struct xt_connlabel_mtinfo {
+	__u16 bit;
+	__u16 options;
+};
diff --git a/include/linux/netfilter/xt_connlimit.h b/include/linux/netfilter/xt_connlimit.h
index ba774d3..f9e8c67 100644
--- a/include/linux/netfilter/xt_connlimit.h
+++ b/include/linux/netfilter/xt_connlimit.h
@@ -1,6 +1,8 @@
 #ifndef _XT_CONNLIMIT_H
 #define _XT_CONNLIMIT_H
 
+#include <linux/types.h>
+
 struct xt_connlimit_data;
 
 enum {
diff --git a/include/linux/netfilter/xt_conntrack.h b/include/linux/netfilter/xt_conntrack.h
index 74b904d..e971501 100644
--- a/include/linux/netfilter/xt_conntrack.h
+++ b/include/linux/netfilter/xt_conntrack.h
@@ -30,6 +30,7 @@
 	XT_CONNTRACK_REPLSRC_PORT = 1 << 10,
 	XT_CONNTRACK_REPLDST_PORT = 1 << 11,
 	XT_CONNTRACK_DIRECTION    = 1 << 12,
+	XT_CONNTRACK_STATE_ALIAS  = 1 << 13,
 };
 
 struct xt_conntrack_mtinfo1 {
diff --git a/include/linux/netfilter/xt_ecn.h b/include/linux/netfilter/xt_ecn.h
new file mode 100644
index 0000000..c21cc28
--- /dev/null
+++ b/include/linux/netfilter/xt_ecn.h
@@ -0,0 +1,33 @@
+/* iptables module for matching the ECN header in IPv4 and TCP header
+ *
+ * (C) 2002 Harald Welte <laforge@netfilter.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+*/
+#ifndef _XT_ECN_H
+#define _XT_ECN_H
+
+#include <linux/types.h>
+#include <linux/netfilter/xt_dscp.h>
+
+#define XT_ECN_IP_MASK	(~XT_DSCP_MASK)
+
+#define XT_ECN_OP_MATCH_IP	0x01
+#define XT_ECN_OP_MATCH_ECE	0x10
+#define XT_ECN_OP_MATCH_CWR	0x20
+
+#define XT_ECN_OP_MATCH_MASK	0xce
+
+/* match info */
+struct xt_ecn_info {
+	__u8 operation;
+	__u8 invert;
+	__u8 ip_ect;
+	union {
+		struct {
+			__u8 ect;
+		} tcp;
+	} proto;
+};
+
+#endif /* _XT_ECN_H */
diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h
index b1925b5..141efbd 100644
--- a/include/linux/netfilter/xt_hashlimit.h
+++ b/include/linux/netfilter/xt_hashlimit.h
@@ -6,7 +6,10 @@
 /* timings are in milliseconds. */
 #define XT_HASHLIMIT_SCALE 10000
 /* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
-   seconds, or one every 59 hours. */
+   seconds, or one packet every 59 hours. */
+
+/* packet length accounting is done in 16-byte steps */
+#define XT_HASHLIMIT_BYTE_SHIFT 4
 
 /* details of this structure hidden by the implementation */
 struct xt_hashlimit_htable;
@@ -17,6 +20,7 @@
 	XT_HASHLIMIT_HASH_SIP = 1 << 2,
 	XT_HASHLIMIT_HASH_SPT = 1 << 3,
 	XT_HASHLIMIT_INVERT   = 1 << 4,
+	XT_HASHLIMIT_BYTES    = 1 << 5,
 };
 
 struct hashlimit_cfg {
diff --git a/include/linux/netfilter/xt_nfacct.h b/include/linux/netfilter/xt_nfacct.h
new file mode 100644
index 0000000..59ab00d
--- /dev/null
+++ b/include/linux/netfilter/xt_nfacct.h
@@ -0,0 +1,17 @@
+#ifndef _XT_NFACCT_MATCH_H
+#define _XT_NFACCT_MATCH_H
+
+#include <linux/types.h>
+
+#ifndef NFACCT_NAME_MAX
+#define NFACCT_NAME_MAX 32
+#endif
+
+struct nf_acct;
+
+struct xt_nfacct_match_info {
+	char		name[NFACCT_NAME_MAX];
+	struct nf_acct	*nfacct;
+};
+
+#endif /* _XT_NFACCT_MATCH_H */
diff --git a/include/linux/netfilter/xt_physdev.h b/include/linux/netfilter/xt_physdev.h
index 8555e39..7d53660 100644
--- a/include/linux/netfilter/xt_physdev.h
+++ b/include/linux/netfilter/xt_physdev.h
@@ -3,9 +3,6 @@
 
 #include <linux/types.h>
 
-#ifdef __KERNEL__
-#include <linux/if.h>
-#endif
 
 #define XT_PHYSDEV_OP_IN		0x01
 #define XT_PHYSDEV_OP_OUT		0x02
diff --git a/include/linux/netfilter/xt_policy.h b/include/linux/netfilter/xt_policy.h
index be8ead0..d246eac 100644
--- a/include/linux/netfilter/xt_policy.h
+++ b/include/linux/netfilter/xt_policy.h
@@ -26,30 +26,19 @@
 			reqid:1;
 };
 
-#ifndef __KERNEL__
 union xt_policy_addr {
 	struct in_addr	a4;
 	struct in6_addr	a6;
 };
-#endif
 
 struct xt_policy_elem {
 	union {
-#ifdef __KERNEL__
-		struct {
-			union nf_inet_addr saddr;
-			union nf_inet_addr smask;
-			union nf_inet_addr daddr;
-			union nf_inet_addr dmask;
-		};
-#else
 		struct {
 			union xt_policy_addr saddr;
 			union xt_policy_addr smask;
 			union xt_policy_addr daddr;
 			union xt_policy_addr dmask;
 		};
-#endif
 	};
 	__be32			spi;
 	__u32		reqid;
diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h
index 8bda65f..9314723 100644
--- a/include/linux/netfilter/xt_quota.h
+++ b/include/linux/netfilter/xt_quota.h
@@ -1,6 +1,8 @@
 #ifndef _XT_QUOTA_H
 #define _XT_QUOTA_H
 
+#include <linux/types.h>
+
 enum xt_quota_flags {
 	XT_QUOTA_INVERT		= 0x1,
 };
@@ -11,7 +13,7 @@
 struct xt_quota_info {
 	__u32 flags;
 	__u32 pad;
-	aligned_u64 quota;
+	__aligned_u64 quota;
 
 	/* Used internally by the kernel */
 	struct xt_quota_priv	*master;
diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h
index 83318e0..6ef36c1 100644
--- a/include/linux/netfilter/xt_recent.h
+++ b/include/linux/netfilter/xt_recent.h
@@ -32,4 +32,14 @@
 	__u8 side;
 };
 
+struct xt_recent_mtinfo_v1 {
+	__u32 seconds;
+	__u32 hit_count;
+	__u8 check_set;
+	__u8 invert;
+	char name[XT_RECENT_NAME_LEN];
+	__u8 side;
+	union nf_inet_addr mask;
+};
+
 #endif /* _LINUX_NETFILTER_XT_RECENT_H */
diff --git a/include/linux/netfilter/xt_rpfilter.h b/include/linux/netfilter/xt_rpfilter.h
new file mode 100644
index 0000000..672b605
--- /dev/null
+++ b/include/linux/netfilter/xt_rpfilter.h
@@ -0,0 +1,17 @@
+#ifndef _XT_RPATH_H
+#define _XT_RPATH_H
+
+#include <linux/types.h>
+
+enum {
+	XT_RPFILTER_LOOSE = 1 << 0,
+	XT_RPFILTER_VALID_MARK = 1 << 1,
+	XT_RPFILTER_ACCEPT_LOCAL = 1 << 2,
+	XT_RPFILTER_INVERT = 1 << 3,
+};
+
+struct xt_rpfilter_info {
+	__u8 flags;
+};
+
+#endif
diff --git a/include/linux/netfilter/xt_sctp.h b/include/linux/netfilter/xt_sctp.h
index 29287be..a501e61 100644
--- a/include/linux/netfilter/xt_sctp.h
+++ b/include/linux/netfilter/xt_sctp.h
@@ -66,7 +66,7 @@
 
 #define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \
 	__sctp_chunkmap_is_clear((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static __inline__ bool
 __sctp_chunkmap_is_clear(const __u32 *chunkmap, unsigned int n)
 {
 	unsigned int i;
@@ -78,7 +78,7 @@
 
 #define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \
 	__sctp_chunkmap_is_all_set((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static __inline__ bool
 __sctp_chunkmap_is_all_set(const __u32 *chunkmap, unsigned int n)
 {
 	unsigned int i;
diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h
index 4379ce9..964d3d4 100644
--- a/include/linux/netfilter/xt_set.h
+++ b/include/linux/netfilter/xt_set.h
@@ -1,62 +1,8 @@
 #ifndef _XT_SET_H
 #define _XT_SET_H
 
-/* The protocol version */
-#define IPSET_PROTOCOL		5
-
-/* The max length of strings including NUL: set and type identifiers */
-#define IPSET_MAXNAMELEN	32
-
-/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
- * and IPSET_INVALID_ID if you want to increase the max number of sets.
- */
-typedef uint16_t ip_set_id_t;
-
-#define IPSET_INVALID_ID	65535
-
-enum ip_set_dim {
-	IPSET_DIM_ZERO = 0,
-	IPSET_DIM_ONE,
-	IPSET_DIM_TWO,
-	IPSET_DIM_THREE,
-	/* Max dimension in elements.
-	 * If changed, new revision of iptables match/target is required.
-	 */
-	IPSET_DIM_MAX = 6,
-};
-
-/* Option flags for kernel operations */
-enum ip_set_kopt {
-	IPSET_INV_MATCH = (1 << IPSET_DIM_ZERO),
-	IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
-	IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
-	IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
-};
-
-/* Interface to iptables/ip6tables */
-
-#define SO_IP_SET 		83
-
-union ip_set_name_index {
-	char name[IPSET_MAXNAMELEN];
-	ip_set_id_t index;
-};
-
-#define IP_SET_OP_GET_BYNAME	0x00000006	/* Get set index by name */
-struct ip_set_req_get_set {
-	unsigned op;
-	unsigned version;
-	union ip_set_name_index set;
-};
-
-#define IP_SET_OP_GET_BYINDEX	0x00000007	/* Get set name by index */
-/* Uses ip_set_req_get_set */
-
-#define IP_SET_OP_VERSION	0x00000100	/* Ask kernel version */
-struct ip_set_req_version {
-	unsigned op;
-	unsigned version;
-};
+#include <linux/types.h>
+#include <linux/netfilter/ipset/ip_set.h>
 
 /* Revision 0 interface: backward compatible with netfilter/iptables */
 
@@ -70,11 +16,11 @@
 struct xt_set_info_v0 {
 	ip_set_id_t index;
 	union {
-		u_int32_t flags[IPSET_DIM_MAX + 1];
+		__u32 flags[IPSET_DIM_MAX + 1];
 		struct {
-			u_int32_t __flags[IPSET_DIM_MAX];
-			u_int8_t dim;
-			u_int8_t flags;
+			__u32 __flags[IPSET_DIM_MAX];
+			__u8 dim;
+			__u8 flags;
 		} compat;
 	} u;
 };
@@ -89,12 +35,12 @@
 	struct xt_set_info_v0 del_set;
 };
 
-/* Revision 1 match and target */
+/* Revision 1  match and target */
 
 struct xt_set_info {
 	ip_set_id_t index;
-	u_int8_t dim;
-	u_int8_t flags;
+	__u8 dim;
+	__u8 flags;
 };
 
 /* match and target infos */
@@ -109,16 +55,20 @@
 
 /* Revision 2 target */
 
-enum ipset_cmd_flags {
-	IPSET_FLAG_BIT_EXIST	= 0,
-	IPSET_FLAG_EXIST	= (1 << IPSET_FLAG_BIT_EXIST),
-};
-
 struct xt_set_info_target_v2 {
 	struct xt_set_info add_set;
 	struct xt_set_info del_set;
-	u_int32_t flags;
-	u_int32_t timeout;
+	__u32 flags;
+	__u32 timeout;
+};
+
+/* Revision 3 match */
+
+struct xt_set_info_match_v3 {
+	struct xt_set_info match_set;
+	struct ip_set_counter_match packets;
+	struct ip_set_counter_match bytes;
+	__u32 flags;
 };
 
 #endif /*_XT_SET_H*/
diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h
index 6f475b8..26d7217 100644
--- a/include/linux/netfilter/xt_socket.h
+++ b/include/linux/netfilter/xt_socket.h
@@ -1,6 +1,8 @@
 #ifndef _XT_SOCKET_H
 #define _XT_SOCKET_H
 
+#include <linux/types.h>
+
 enum {
 	XT_SOCKET_TRANSPARENT = 1 << 0,
 };
diff --git a/include/linux/netfilter/xt_time.h b/include/linux/netfilter/xt_time.h
index b8bd456..a21d5bf 100644
--- a/include/linux/netfilter/xt_time.h
+++ b/include/linux/netfilter/xt_time.h
@@ -1,6 +1,8 @@
 #ifndef _XT_TIME_H
 #define _XT_TIME_H 1
 
+#include <linux/types.h>
+
 struct xt_time_info {
 	__u32 date_start;
 	__u32 date_stop;
@@ -14,6 +16,7 @@
 enum {
 	/* Match against local time (instead of UTC) */
 	XT_TIME_LOCAL_TZ = 1 << 0,
+	XT_TIME_CONTIGUOUS = 1 << 1,
 
 	/* Shortcuts */
 	XT_TIME_ALL_MONTHDAYS = 0xFFFFFFFE,
diff --git a/include/linux/netfilter/xt_u32.h b/include/linux/netfilter/xt_u32.h
index e8c3d87..04d1bfe 100644
--- a/include/linux/netfilter/xt_u32.h
+++ b/include/linux/netfilter/xt_u32.h
@@ -1,6 +1,8 @@
 #ifndef _XT_U32_H
 #define _XT_U32_H 1
 
+#include <linux/types.h>
+
 enum xt_u32_ops {
 	XT_U32_AND,
 	XT_U32_LEFTSH,
diff --git a/include/linux/netfilter_ipv4/ip_queue.h b/include/linux/netfilter_ipv4/ip_queue.h
new file mode 100644
index 0000000..a03507f
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_queue.h
@@ -0,0 +1,72 @@
+/*
+ * This is a module which is used for queueing IPv4 packets and
+ * communicating with userspace via netlink.
+ *
+ * (C) 2000 James Morris, this code is GPL.
+ */
+#ifndef _IP_QUEUE_H
+#define _IP_QUEUE_H
+
+#ifdef __KERNEL__
+#ifdef DEBUG_IPQ
+#define QDEBUG(x...) printk(KERN_DEBUG ## x)
+#else
+#define QDEBUG(x...)
+#endif  /* DEBUG_IPQ */
+#else
+#include <net/if.h>
+#endif	/* ! __KERNEL__ */
+
+/* Messages sent from kernel */
+typedef struct ipq_packet_msg {
+	unsigned long packet_id;	/* ID of queued packet */
+	unsigned long mark;		/* Netfilter mark value */
+	long timestamp_sec;		/* Packet arrival time (seconds) */
+	long timestamp_usec;		/* Packet arrvial time (+useconds) */
+	unsigned int hook;		/* Netfilter hook we rode in on */
+	char indev_name[IFNAMSIZ];	/* Name of incoming interface */
+	char outdev_name[IFNAMSIZ];	/* Name of outgoing interface */
+	__be16 hw_protocol;		/* Hardware protocol (network order) */
+	unsigned short hw_type;		/* Hardware type */
+	unsigned char hw_addrlen;	/* Hardware address length */
+	unsigned char hw_addr[8];	/* Hardware address */
+	size_t data_len;		/* Length of packet data */
+	unsigned char payload[0];	/* Optional packet data */
+} ipq_packet_msg_t;
+
+/* Messages sent from userspace */
+typedef struct ipq_mode_msg {
+	unsigned char value;		/* Requested mode */
+	size_t range;			/* Optional range of packet requested */
+} ipq_mode_msg_t;
+
+typedef struct ipq_verdict_msg {
+	unsigned int value;		/* Verdict to hand to netfilter */
+	unsigned long id;		/* Packet ID for this verdict */
+	size_t data_len;		/* Length of replacement data */
+	unsigned char payload[0];	/* Optional replacement packet */
+} ipq_verdict_msg_t;
+
+typedef struct ipq_peer_msg {
+	union {
+		ipq_verdict_msg_t verdict;
+		ipq_mode_msg_t mode;
+	} msg;
+} ipq_peer_msg_t;
+
+/* Packet delivery modes */
+enum {
+	IPQ_COPY_NONE,		/* Initial mode, packets are dropped */
+	IPQ_COPY_META,		/* Copy metadata */
+	IPQ_COPY_PACKET		/* Copy metadata + packet (range) */
+};
+#define IPQ_COPY_MAX IPQ_COPY_PACKET
+
+/* Types of messages */
+#define IPQM_BASE	0x10	/* standard netlink messages below this */
+#define IPQM_MODE	(IPQM_BASE + 1)		/* Mode request from peer */
+#define IPQM_VERDICT	(IPQM_BASE + 2)		/* Verdict from peer */ 
+#define IPQM_PACKET	(IPQM_BASE + 3)		/* Packet from kernel */
+#define IPQM_MAX	(IPQM_BASE + 4)
+
+#endif /*_IP_QUEUE_H*/
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index 735f4b1..57fd82a 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -27,6 +27,41 @@
 #define ipt_target xt_target
 #define ipt_table xt_table
 #define ipt_get_revision xt_get_revision
+#define ipt_entry_match xt_entry_match
+#define ipt_entry_target xt_entry_target
+#define ipt_standard_target xt_standard_target
+#define ipt_error_target xt_error_target
+#define ipt_counters xt_counters
+#define IPT_CONTINUE XT_CONTINUE
+#define IPT_RETURN XT_RETURN
+
+/* This group is older than old (iptables < v1.4.0-rc1~89) */
+#include <linux/netfilter/xt_tcpudp.h>
+#define ipt_udp xt_udp
+#define ipt_tcp xt_tcp
+#define IPT_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
+#define IPT_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
+#define IPT_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
+#define IPT_TCP_INV_OPTION	XT_TCP_INV_OPTION
+#define IPT_TCP_INV_MASK	XT_TCP_INV_MASK
+#define IPT_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
+#define IPT_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
+#define IPT_UDP_INV_MASK	XT_UDP_INV_MASK
+
+/* The argument to IPT_SO_ADD_COUNTERS. */
+#define ipt_counters_info xt_counters_info
+/* Standard return verdict, or do jump. */
+#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
+/* Error verdict. */
+#define IPT_ERROR_TARGET XT_ERROR_TARGET
+
+/* fn returns 0 to continue iteration */
+#define IPT_MATCH_ITERATE(e, fn, args...) \
+	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
+
+/* fn returns 0 to continue iteration */
+#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
+	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
 
 /* Yes, Virginia, you have to zero the padding. */
 struct ipt_ip {
@@ -46,12 +81,6 @@
 	u_int8_t invflags;
 };
 
-#define ipt_entry_match xt_entry_match
-#define ipt_entry_target xt_entry_target
-#define ipt_standard_target xt_standard_target
-
-#define ipt_counters xt_counters
-
 /* Values for "flag" field in struct ipt_ip (general ip structure). */
 #define IPT_F_FRAG		0x01	/* Set if rule is a fragment rule */
 #define IPT_F_GOTO		0x02	/* Set if jump is a goto */
@@ -110,23 +139,6 @@
 #define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
 #define IPT_SO_GET_MAX			IPT_SO_GET_REVISION_TARGET
 
-#define IPT_CONTINUE XT_CONTINUE
-#define IPT_RETURN XT_RETURN
-
-#include <linux/netfilter/xt_tcpudp.h>
-#define ipt_udp xt_udp
-#define ipt_tcp xt_tcp
-
-#define IPT_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
-#define IPT_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
-#define IPT_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
-#define IPT_TCP_INV_OPTION	XT_TCP_INV_OPTION
-#define IPT_TCP_INV_MASK	XT_TCP_INV_MASK
-
-#define IPT_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
-#define IPT_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
-#define IPT_UDP_INV_MASK	XT_UDP_INV_MASK
-
 /* ICMP matching stuff */
 struct ipt_icmp {
 	u_int8_t type;				/* type to match */
@@ -140,7 +152,7 @@
 /* The argument to IPT_SO_GET_INFO */
 struct ipt_getinfo {
 	/* Which table: caller fills this in. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Kernel fills these in. */
 	/* Which hook entry points are valid: bitmask */
@@ -162,7 +174,7 @@
 /* The argument to IPT_SO_SET_REPLACE. */
 struct ipt_replace {
 	/* Which table. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Which hook entry points are valid: bitmask.  You can't
            change this. */
@@ -190,13 +202,10 @@
 	struct ipt_entry entries[0];
 };
 
-/* The argument to IPT_SO_ADD_COUNTERS. */
-#define ipt_counters_info xt_counters_info
-
 /* The argument to IPT_SO_GET_ENTRIES. */
 struct ipt_get_entries {
 	/* Which table: user fills this in. */
-	char name[IPT_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* User fills this in: total entry size. */
 	unsigned int size;
@@ -205,26 +214,13 @@
 	struct ipt_entry entrytable[0];
 };
 
-/* Standard return verdict, or do jump. */
-#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
-/* Error verdict. */
-#define IPT_ERROR_TARGET XT_ERROR_TARGET
-
 /* Helper functions */
-static __inline__ struct ipt_entry_target *
+static __inline__ struct xt_entry_target *
 ipt_get_target(struct ipt_entry *e)
 {
 	return (void *)e + e->target_offset;
 }
 
-/* fn returns 0 to continue iteration */
-#define IPT_MATCH_ITERATE(e, fn, args...) \
-	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
-
-/* fn returns 0 to continue iteration */
-#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
-	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
-
 /*
  *	Main firewall chains definitions and global var's definitions.
  */
diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
index e5a3687..c6a204c 100644
--- a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
+++ b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
@@ -1,6 +1,8 @@
 #ifndef _IPT_CLUSTERIP_H_target
 #define _IPT_CLUSTERIP_H_target
 
+#include <linux/types.h>
+
 enum clusterip_hashmode {
     CLUSTERIP_HASHMODE_SIP = 0,
     CLUSTERIP_HASHMODE_SIP_SPT,
@@ -17,15 +19,15 @@
 
 struct ipt_clusterip_tgt_info {
 
-	u_int32_t flags;
+	__u32 flags;
 
 	/* only relevant for new ones */
-	u_int8_t clustermac[6];
-	u_int16_t num_total_nodes;
-	u_int16_t num_local_nodes;
-	u_int16_t local_nodes[CLUSTERIP_MAX_NODES];
-	u_int32_t hash_mode;
-	u_int32_t hash_initval;
+	__u8 clustermac[6];
+	__u16 num_total_nodes;
+	__u16 num_local_nodes;
+	__u16 local_nodes[CLUSTERIP_MAX_NODES];
+	__u32 hash_mode;
+	__u32 hash_initval;
 
 	/* Used internally by the kernel */
 	struct clusterip_config *config;
diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h
index 7ca4591..bb88d53 100644
--- a/include/linux/netfilter_ipv4/ipt_ECN.h
+++ b/include/linux/netfilter_ipv4/ipt_ECN.h
@@ -8,6 +8,8 @@
 */
 #ifndef _IPT_ECN_TARGET_H
 #define _IPT_ECN_TARGET_H
+
+#include <linux/types.h>
 #include <linux/netfilter/xt_DSCP.h>
 
 #define IPT_ECN_IP_MASK	(~XT_DSCP_MASK)
@@ -19,11 +21,11 @@
 #define IPT_ECN_OP_MASK		0xce
 
 struct ipt_ECN_info {
-	u_int8_t operation;	/* bitset of operations */
-	u_int8_t ip_ect;	/* ECT codepoint of IPv4 header, pre-shifted */
+	__u8 operation;	/* bitset of operations */
+	__u8 ip_ect;	/* ECT codepoint of IPv4 header, pre-shifted */
 	union {
 		struct {
-			u_int8_t ece:1, cwr:1; /* TCP ECT bits */
+			__u8 ece:1, cwr:1; /* TCP ECT bits */
 		} tcp;
 	} proto;
 };
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
index 2529660..a855167 100644
--- a/include/linux/netfilter_ipv4/ipt_SAME.h
+++ b/include/linux/netfilter_ipv4/ipt_SAME.h
@@ -1,18 +1,20 @@
 #ifndef _IPT_SAME_H
 #define _IPT_SAME_H
 
+#include <linux/types.h>
+
 #define IPT_SAME_MAX_RANGE	10
 
 #define IPT_SAME_NODST		0x01
 
 struct ipt_same_info {
 	unsigned char info;
-	u_int32_t rangesize;
-	u_int32_t ipnum;
-	u_int32_t *iparray;
+	__u32 rangesize;
+	__u32 ipnum;
+	__u32 *iparray;
 
 	/* hangs off end. */
-	struct nf_nat_range range[IPT_SAME_MAX_RANGE];
+	struct nf_nat_ipv4_range range[IPT_SAME_MAX_RANGE];
 };
 
 #endif /*_IPT_SAME_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h
index ee6611e..f6ac169 100644
--- a/include/linux/netfilter_ipv4/ipt_TTL.h
+++ b/include/linux/netfilter_ipv4/ipt_TTL.h
@@ -4,6 +4,8 @@
 #ifndef _IPT_TTL_H
 #define _IPT_TTL_H
 
+#include <linux/types.h>
+
 enum {
 	IPT_TTL_SET = 0,
 	IPT_TTL_INC,
@@ -13,8 +15,8 @@
 #define IPT_TTL_MAXMODE	IPT_TTL_DEC
 
 struct ipt_TTL_info {
-	u_int8_t	mode;
-	u_int8_t	ttl;
+	__u8	mode;
+	__u8	ttl;
 };
 
 
diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h
index 446de6a..0da4223 100644
--- a/include/linux/netfilter_ipv4/ipt_addrtype.h
+++ b/include/linux/netfilter_ipv4/ipt_addrtype.h
@@ -1,6 +1,8 @@
 #ifndef _IPT_ADDRTYPE_H
 #define _IPT_ADDRTYPE_H
 
+#include <linux/types.h>
+
 enum {
 	IPT_ADDRTYPE_INVERT_SOURCE	= 0x0001,
 	IPT_ADDRTYPE_INVERT_DEST	= 0x0002,
@@ -9,17 +11,17 @@
 };
 
 struct ipt_addrtype_info_v1 {
-	u_int16_t	source;		/* source-type mask */
-	u_int16_t	dest;		/* dest-type mask */
-	u_int32_t	flags;
+	__u16	source;		/* source-type mask */
+	__u16	dest;		/* dest-type mask */
+	__u32	flags;
 };
 
 /* revision 0 */
 struct ipt_addrtype_info {
-	u_int16_t	source;		/* source-type mask */
-	u_int16_t	dest;		/* dest-type mask */
-	u_int32_t	invert_source;
-	u_int32_t	invert_dest;
+	__u16	source;		/* source-type mask */
+	__u16	dest;		/* dest-type mask */
+	__u32	invert_source;
+	__u32	invert_dest;
 };
 
 #endif
diff --git a/include/linux/netfilter_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h
index 2e555b4..4e02bb0 100644
--- a/include/linux/netfilter_ipv4/ipt_ah.h
+++ b/include/linux/netfilter_ipv4/ipt_ah.h
@@ -1,9 +1,11 @@
 #ifndef _IPT_AH_H
 #define _IPT_AH_H
 
+#include <linux/types.h>
+
 struct ipt_ah {
-	u_int32_t spis[2];			/* Security Parameter Index */
-	u_int8_t  invflags;			/* Inverse flags */
+	__u32 spis[2];			/* Security Parameter Index */
+	__u8  invflags;			/* Inverse flags */
 };
 
 
diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h
deleted file mode 100644
index 9945baa..0000000
--- a/include/linux/netfilter_ipv4/ipt_ecn.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* iptables module for matching the ECN header in IPv4 and TCP header
- *
- * (C) 2002 Harald Welte <laforge@gnumonks.org>
- *
- * This software is distributed under GNU GPL v2, 1991
- * 
- * ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp
-*/
-#ifndef _IPT_ECN_H
-#define _IPT_ECN_H
-#include <linux/netfilter/xt_dscp.h>
-
-#define IPT_ECN_IP_MASK	(~XT_DSCP_MASK)
-
-#define IPT_ECN_OP_MATCH_IP	0x01
-#define IPT_ECN_OP_MATCH_ECE	0x10
-#define IPT_ECN_OP_MATCH_CWR	0x20
-
-#define IPT_ECN_OP_MATCH_MASK	0xce
-
-/* match info */
-struct ipt_ecn_info {
-	u_int8_t operation;
-	u_int8_t invert;
-	u_int8_t ip_ect;
-	union {
-		struct {
-			u_int8_t ect;
-		} tcp;
-	} proto;
-};
-
-#endif /* _IPT_ECN_H */
diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h
index ee24fd8..37bee44 100644
--- a/include/linux/netfilter_ipv4/ipt_ttl.h
+++ b/include/linux/netfilter_ipv4/ipt_ttl.h
@@ -4,6 +4,8 @@
 #ifndef _IPT_TTL_H
 #define _IPT_TTL_H
 
+#include <linux/types.h>
+
 enum {
 	IPT_TTL_EQ = 0,		/* equals */
 	IPT_TTL_NE,		/* not equals */
@@ -13,8 +15,8 @@
 
 
 struct ipt_ttl_info {
-	u_int8_t	mode;
-	u_int8_t	ttl;
+	__u8	mode;
+	__u8	ttl;
 };
 
 
diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h
index 6179032..3f19a97 100644
--- a/include/linux/netfilter_ipv6/ip6_tables.h
+++ b/include/linux/netfilter_ipv6/ip6_tables.h
@@ -23,11 +23,38 @@
 
 #define IP6T_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
 #define IP6T_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
-
 #define ip6t_match xt_match
 #define ip6t_target xt_target
 #define ip6t_table xt_table
 #define ip6t_get_revision xt_get_revision
+#define ip6t_entry_match xt_entry_match
+#define ip6t_entry_target xt_entry_target
+#define ip6t_standard_target xt_standard_target
+#define ip6t_error_target xt_error_target
+#define ip6t_counters xt_counters
+#define IP6T_CONTINUE XT_CONTINUE
+#define IP6T_RETURN XT_RETURN
+
+/* Pre-iptables-1.4.0 */
+#include <linux/netfilter/xt_tcpudp.h>
+#define ip6t_tcp xt_tcp
+#define ip6t_udp xt_udp
+#define IP6T_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
+#define IP6T_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
+#define IP6T_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
+#define IP6T_TCP_INV_OPTION	XT_TCP_INV_OPTION
+#define IP6T_TCP_INV_MASK	XT_TCP_INV_MASK
+#define IP6T_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
+#define IP6T_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
+#define IP6T_UDP_INV_MASK	XT_UDP_INV_MASK
+
+#define ip6t_counters_info xt_counters_info
+#define IP6T_STANDARD_TARGET XT_STANDARD_TARGET
+#define IP6T_ERROR_TARGET XT_ERROR_TARGET
+#define IP6T_MATCH_ITERATE(e, fn, args...) \
+	XT_MATCH_ITERATE(struct ip6t_entry, e, fn, ## args)
+#define IP6T_ENTRY_ITERATE(entries, size, fn, args...) \
+	XT_ENTRY_ITERATE(struct ip6t_entry, entries, size, fn, ## args)
 
 /* Yes, Virginia, you have to zero the padding. */
 struct ip6t_ip6 {
@@ -56,12 +83,6 @@
 	u_int8_t invflags;
 };
 
-#define ip6t_entry_match xt_entry_match
-#define ip6t_entry_target xt_entry_target
-#define ip6t_standard_target xt_standard_target
-
-#define ip6t_counters	xt_counters
-
 /* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */
 #define IP6T_F_PROTO		0x01	/* Set if rule cares about upper 
 					   protocols */
@@ -106,17 +127,12 @@
 /* Standard entry */
 struct ip6t_standard {
 	struct ip6t_entry entry;
-	struct ip6t_standard_target target;
-};
-
-struct ip6t_error_target {
-	struct ip6t_entry_target target;
-	char errorname[IP6T_FUNCTION_MAXNAMELEN];
+	struct xt_standard_target target;
 };
 
 struct ip6t_error {
 	struct ip6t_entry entry;
-	struct ip6t_error_target target;
+	struct xt_error_target target;
 };
 
 #define IP6T_ENTRY_INIT(__size)						       \
@@ -128,16 +144,16 @@
 #define IP6T_STANDARD_INIT(__verdict)					       \
 {									       \
 	.entry		= IP6T_ENTRY_INIT(sizeof(struct ip6t_standard)),       \
-	.target		= XT_TARGET_INIT(IP6T_STANDARD_TARGET,		       \
-					 sizeof(struct ip6t_standard_target)), \
+	.target		= XT_TARGET_INIT(XT_STANDARD_TARGET,		       \
+					 sizeof(struct xt_standard_target)),   \
 	.target.verdict	= -(__verdict) - 1,				       \
 }
 
 #define IP6T_ERROR_INIT							       \
 {									       \
 	.entry		= IP6T_ENTRY_INIT(sizeof(struct ip6t_error)),	       \
-	.target		= XT_TARGET_INIT(IP6T_ERROR_TARGET,		       \
-					 sizeof(struct ip6t_error_target)),    \
+	.target		= XT_TARGET_INIT(XT_ERROR_TARGET,		       \
+					 sizeof(struct xt_error_target)),      \
 	.target.errorname = "ERROR",					       \
 }
 
@@ -160,30 +176,6 @@
 #define IP6T_SO_GET_REVISION_TARGET	(IP6T_BASE_CTL + 5)
 #define IP6T_SO_GET_MAX			IP6T_SO_GET_REVISION_TARGET
 
-/* CONTINUE verdict for targets */
-#define IP6T_CONTINUE XT_CONTINUE
-
-/* For standard target */
-#define IP6T_RETURN XT_RETURN
-
-/* TCP/UDP matching stuff */
-#include <linux/netfilter/xt_tcpudp.h>
-
-#define ip6t_tcp xt_tcp
-#define ip6t_udp xt_udp
-
-/* Values for "inv" field in struct ipt_tcp. */
-#define IP6T_TCP_INV_SRCPT	XT_TCP_INV_SRCPT
-#define IP6T_TCP_INV_DSTPT	XT_TCP_INV_DSTPT
-#define IP6T_TCP_INV_FLAGS	XT_TCP_INV_FLAGS
-#define IP6T_TCP_INV_OPTION	XT_TCP_INV_OPTION
-#define IP6T_TCP_INV_MASK	XT_TCP_INV_MASK
-
-/* Values for "invflags" field in struct ipt_udp. */
-#define IP6T_UDP_INV_SRCPT	XT_UDP_INV_SRCPT
-#define IP6T_UDP_INV_DSTPT	XT_UDP_INV_DSTPT
-#define IP6T_UDP_INV_MASK	XT_UDP_INV_MASK
-
 /* ICMP matching stuff */
 struct ip6t_icmp {
 	u_int8_t type;				/* type to match */
@@ -197,7 +189,7 @@
 /* The argument to IP6T_SO_GET_INFO */
 struct ip6t_getinfo {
 	/* Which table: caller fills this in. */
-	char name[IP6T_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Kernel fills these in. */
 	/* Which hook entry points are valid: bitmask */
@@ -219,7 +211,7 @@
 /* The argument to IP6T_SO_SET_REPLACE. */
 struct ip6t_replace {
 	/* Which table. */
-	char name[IP6T_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* Which hook entry points are valid: bitmask.  You can't
            change this. */
@@ -247,13 +239,10 @@
 	struct ip6t_entry entries[0];
 };
 
-/* The argument to IP6T_SO_ADD_COUNTERS. */
-#define ip6t_counters_info xt_counters_info
-
 /* The argument to IP6T_SO_GET_ENTRIES. */
 struct ip6t_get_entries {
 	/* Which table: user fills this in. */
-	char name[IP6T_TABLE_MAXNAMELEN];
+	char name[XT_TABLE_MAXNAMELEN];
 
 	/* User fills this in: total entry size. */
 	unsigned int size;
@@ -262,26 +251,13 @@
 	struct ip6t_entry entrytable[0];
 };
 
-/* Standard return verdict, or do jump. */
-#define IP6T_STANDARD_TARGET XT_STANDARD_TARGET
-/* Error verdict. */
-#define IP6T_ERROR_TARGET XT_ERROR_TARGET
-
 /* Helper functions */
-static __inline__ struct ip6t_entry_target *
+static __inline__ struct xt_entry_target *
 ip6t_get_target(struct ip6t_entry *e)
 {
 	return (void *)e + e->target_offset;
 }
 
-/* fn returns 0 to continue iteration */
-#define IP6T_MATCH_ITERATE(e, fn, args...) \
-	XT_MATCH_ITERATE(struct ip6t_entry, e, fn, ## args)
-
-/* fn returns 0 to continue iteration */
-#define IP6T_ENTRY_ITERATE(entries, size, fn, args...) \
-	XT_ENTRY_ITERATE(struct ip6t_entry, entries, size, fn, ## args)
-
 /*
  *	Main firewall chains definitions and global var's definitions.
  */
diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h
index afb7813..ebd8ead 100644
--- a/include/linux/netfilter_ipv6/ip6t_HL.h
+++ b/include/linux/netfilter_ipv6/ip6t_HL.h
@@ -5,6 +5,8 @@
 #ifndef _IP6T_HL_H
 #define _IP6T_HL_H
 
+#include <linux/types.h>
+
 enum {
 	IP6T_HL_SET = 0,
 	IP6T_HL_INC,
@@ -14,8 +16,8 @@
 #define IP6T_HL_MAXMODE	IP6T_HL_DEC
 
 struct ip6t_HL_info {
-	u_int8_t	mode;
-	u_int8_t	hop_limit;
+	__u8	mode;
+	__u8	hop_limit;
 };
 
 
diff --git a/include/linux/netfilter_ipv6/ip6t_NPT.h b/include/linux/netfilter_ipv6/ip6t_NPT.h
new file mode 100644
index 0000000..f763355
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_NPT.h
@@ -0,0 +1,16 @@
+#ifndef __NETFILTER_IP6T_NPT
+#define __NETFILTER_IP6T_NPT
+
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+struct ip6t_npt_tginfo {
+	union nf_inet_addr	src_pfx;
+	union nf_inet_addr	dst_pfx;
+	__u8			src_pfx_len;
+	__u8			dst_pfx_len;
+	/* Used internally by the kernel */
+	__sum16			adjustment;
+};
+
+#endif /* __NETFILTER_IP6T_NPT */
diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h
index 6be6504..205ed62 100644
--- a/include/linux/netfilter_ipv6/ip6t_REJECT.h
+++ b/include/linux/netfilter_ipv6/ip6t_REJECT.h
@@ -1,6 +1,8 @@
 #ifndef _IP6T_REJECT_H
 #define _IP6T_REJECT_H
 
+#include <linux/types.h>
+
 enum ip6t_reject_with {
 	IP6T_ICMP6_NO_ROUTE,
 	IP6T_ICMP6_ADM_PROHIBITED,
@@ -12,7 +14,7 @@
 };
 
 struct ip6t_reject_info {
-	u_int32_t	with;	/* reject type */
+	__u32	with;	/* reject type */
 };
 
 #endif /*_IP6T_REJECT_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h
index 17a745c..5da2b65 100644
--- a/include/linux/netfilter_ipv6/ip6t_ah.h
+++ b/include/linux/netfilter_ipv6/ip6t_ah.h
@@ -1,11 +1,13 @@
 #ifndef _IP6T_AH_H
 #define _IP6T_AH_H
 
+#include <linux/types.h>
+
 struct ip6t_ah {
-	u_int32_t spis[2];			/* Security Parameter Index */
-	u_int32_t hdrlen;			/* Header Length */
-	u_int8_t  hdrres;			/* Test of the Reserved Filed */
-	u_int8_t  invflags;			/* Inverse flags */
+	__u32 spis[2];			/* Security Parameter Index */
+	__u32 hdrlen;			/* Header Length */
+	__u8  hdrres;			/* Test of the Reserved Filed */
+	__u8  invflags;			/* Inverse flags */
 };
 
 #define IP6T_AH_SPI 0x01
diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h
index 3724d08..b47f61b 100644
--- a/include/linux/netfilter_ipv6/ip6t_frag.h
+++ b/include/linux/netfilter_ipv6/ip6t_frag.h
@@ -1,11 +1,13 @@
 #ifndef _IP6T_FRAG_H
 #define _IP6T_FRAG_H
 
+#include <linux/types.h>
+
 struct ip6t_frag {
-	u_int32_t ids[2];			/* Security Parameter Index */
-	u_int32_t hdrlen;			/* Header Length */
-	u_int8_t  flags;			/*  */
-	u_int8_t  invflags;			/* Inverse flags */
+	__u32 ids[2];			/* Security Parameter Index */
+	__u32 hdrlen;			/* Header Length */
+	__u8  flags;			/*  */
+	__u8  invflags;			/* Inverse flags */
 };
 
 #define IP6T_FRAG_IDS 		0x01
diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h
index 5ef91b8..6e76dbc 100644
--- a/include/linux/netfilter_ipv6/ip6t_hl.h
+++ b/include/linux/netfilter_ipv6/ip6t_hl.h
@@ -5,6 +5,8 @@
 #ifndef _IP6T_HL_H
 #define _IP6T_HL_H
 
+#include <linux/types.h>
+
 enum {
 	IP6T_HL_EQ = 0,		/* equals */
 	IP6T_HL_NE,		/* not equals */
@@ -14,8 +16,8 @@
 
 
 struct ip6t_hl_info {
-	u_int8_t	mode;
-	u_int8_t	hop_limit;
+	__u8	mode;
+	__u8	hop_limit;
 };
 
 
diff --git a/include/linux/netfilter_ipv6/ip6t_ipv6header.h b/include/linux/netfilter_ipv6/ip6t_ipv6header.h
index 01dfd44..efae3a2 100644
--- a/include/linux/netfilter_ipv6/ip6t_ipv6header.h
+++ b/include/linux/netfilter_ipv6/ip6t_ipv6header.h
@@ -8,10 +8,12 @@
 #ifndef __IPV6HEADER_H
 #define __IPV6HEADER_H
 
+#include <linux/types.h>
+
 struct ip6t_ipv6header_info {
-	u_int8_t matchflags;
-	u_int8_t invflags;
-	u_int8_t modeflag;
+	__u8 matchflags;
+	__u8 invflags;
+	__u8 modeflag;
 };
 
 #define MASK_HOPOPTS    128
diff --git a/include/linux/netfilter_ipv6/ip6t_mh.h b/include/linux/netfilter_ipv6/ip6t_mh.h
index 18549bc..a7729a5 100644
--- a/include/linux/netfilter_ipv6/ip6t_mh.h
+++ b/include/linux/netfilter_ipv6/ip6t_mh.h
@@ -1,10 +1,12 @@
 #ifndef _IP6T_MH_H
 #define _IP6T_MH_H
 
+#include <linux/types.h>
+
 /* MH matching stuff */
 struct ip6t_mh {
-	u_int8_t types[2];	/* MH type range */
-	u_int8_t invflags;	/* Inverse flags */
+	__u8 types[2];	/* MH type range */
+	__u8 invflags;	/* Inverse flags */
 };
 
 /* Values for "invflags" field in struct ip6t_mh. */
diff --git a/include/linux/netfilter_ipv6/ip6t_opts.h b/include/linux/netfilter_ipv6/ip6t_opts.h
index 62d89bc..17d419a 100644
--- a/include/linux/netfilter_ipv6/ip6t_opts.h
+++ b/include/linux/netfilter_ipv6/ip6t_opts.h
@@ -1,14 +1,16 @@
 #ifndef _IP6T_OPTS_H
 #define _IP6T_OPTS_H
 
+#include <linux/types.h>
+
 #define IP6T_OPTS_OPTSNR 16
 
 struct ip6t_opts {
-	u_int32_t hdrlen;			/* Header Length */
-	u_int8_t flags;				/*  */
-	u_int8_t invflags;			/* Inverse flags */
-	u_int16_t opts[IP6T_OPTS_OPTSNR];	/* opts */
-	u_int8_t optsnr;			/* Nr of OPts */
+	__u32 hdrlen;			/* Header Length */
+	__u8 flags;				/*  */
+	__u8 invflags;			/* Inverse flags */
+	__u16 opts[IP6T_OPTS_OPTSNR];	/* opts */
+	__u8 optsnr;			/* Nr of OPts */
 };
 
 #define IP6T_OPTS_LEN 		0x01
diff --git a/include/linux/netfilter_ipv6/ip6t_rt.h b/include/linux/netfilter_ipv6/ip6t_rt.h
index ab91bfd..7605a5f 100644
--- a/include/linux/netfilter_ipv6/ip6t_rt.h
+++ b/include/linux/netfilter_ipv6/ip6t_rt.h
@@ -1,18 +1,19 @@
 #ifndef _IP6T_RT_H
 #define _IP6T_RT_H
 
+#include <linux/types.h>
 /*#include <linux/in6.h>*/
 
 #define IP6T_RT_HOPS 16
 
 struct ip6t_rt {
-	u_int32_t rt_type;			/* Routing Type */
-	u_int32_t segsleft[2];			/* Segments Left */
-	u_int32_t hdrlen;			/* Header Length */
-	u_int8_t  flags;			/*  */
-	u_int8_t  invflags;			/* Inverse flags */
+	__u32 rt_type;			/* Routing Type */
+	__u32 segsleft[2];			/* Segments Left */
+	__u32 hdrlen;			/* Header Length */
+	__u8  flags;			/*  */
+	__u8  invflags;			/* Inverse flags */
 	struct in6_addr addrs[IP6T_RT_HOPS];	/* Hops */
-	u_int8_t addrnr;			/* Nr of Addresses */
+	__u8 addrnr;			/* Nr of Addresses */
 };
 
 #define IP6T_RT_TYP 		0x01
diff --git a/include/linux/types.h b/include/linux/types.h
new file mode 100644
index 0000000..630cd3b
--- /dev/null
+++ b/include/linux/types.h
@@ -0,0 +1,51 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <asm/types.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/posix_types.h>
+
+
+/*
+ * Below are truly Linux-specific types that should never collide with
+ * any application/library that wants linux/types.h.
+ */
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+/*
+ * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid
+ * common 32/64-bit compat problems.
+ * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other
+ * architectures) and to 8-byte boundaries on 64-bit architetures.  The new
+ * aligned_64 type enforces 8-byte alignment so that structs containing
+ * aligned_64 values have the same alignment on 32-bit and 64-bit architectures.
+ * No conversions are necessary between 32-bit user-space and a 64-bit kernel.
+ */
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#define __aligned_be64 __be64 __attribute__((aligned(8)))
+#define __aligned_le64 __le64 __attribute__((aligned(8)))
+
+#endif /*  __ASSEMBLY__ */
+#endif /* _LINUX_TYPES_H */
diff --git a/include/net/netfilter/nf_conntrack_tuple.h b/include/net/netfilter/nf_conntrack_tuple.h
deleted file mode 100644
index c40e0b4..0000000
--- a/include/net/netfilter/nf_conntrack_tuple.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* This file was manually copied from the Linux kernel source
- * and manually stripped from __KERNEL__ sections and unused functions.
- */
-
-/*
- * Definitions and Declarations for tuple.
- *
- * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *	- generalize L3 protocol dependent part.
- *
- * Derived from include/linux/netfiter_ipv4/ip_conntrack_tuple.h
- */
-
-#ifndef _NF_CONNTRACK_TUPLE_H
-#define _NF_CONNTRACK_TUPLE_H
-
-#include <linux/netfilter/x_tables.h>
-#include <linux/netfilter/nf_conntrack_tuple_common.h>
-
-/* A `tuple' is a structure containing the information to uniquely
-  identify a connection.  ie. if two packets have the same tuple, they
-  are in the same connection; if not, they are not.
-
-  We divide the structure along "manipulatable" and
-  "non-manipulatable" lines, for the benefit of the NAT code.
-*/
-
-#define NF_CT_TUPLE_L3SIZE	ARRAY_SIZE(((union nf_inet_addr *)NULL)->all)
-
-/* The protocol-specific manipulable parts of the tuple: always in
-   network order! */
-union nf_conntrack_man_proto
-{
-	/* Add other protocols here. */
-	__be16 all;
-
-	struct {
-		__be16 port;
-	} tcp;
-	struct {
-		__be16 port;
-	} udp;
-	struct {
-		__be16 id;
-	} icmp;
-	struct {
-		__be16 port;
-	} dccp;
-	struct {
-		__be16 port;
-	} sctp;
-	struct {
-		__be16 key;	/* GRE key is 32bit, PPtP only uses 16bit */
-	} gre;
-};
-
-/* The manipulable part of the tuple. */
-struct nf_conntrack_man
-{
-	union nf_inet_addr u3;
-	union nf_conntrack_man_proto u;
-	/* Layer 3 protocol */
-	u_int16_t l3num;
-};
-
-/* This contains the information to distinguish a connection. */
-struct nf_conntrack_tuple
-{
-	struct nf_conntrack_man src;
-
-	/* These are the parts of the tuple which are fixed. */
-	struct {
-		union nf_inet_addr u3;
-		union {
-			/* Add other protocols here. */
-			__be16 all;
-
-			struct {
-				__be16 port;
-			} tcp;
-			struct {
-				__be16 port;
-			} udp;
-			struct {
-				u_int8_t type, code;
-			} icmp;
-			struct {
-				__be16 port;
-			} dccp;
-			struct {
-				__be16 port;
-			} sctp;
-			struct {
-				__be16 key;
-			} gre;
-		} u;
-
-		/* The protocol. */
-		u_int8_t protonum;
-
-		/* The direction (for tuplehash) */
-		u_int8_t dir;
-	} dst;
-};
-
-struct nf_conntrack_tuple_mask
-{
-	struct {
-		union nf_inet_addr u3;
-		union nf_conntrack_man_proto u;
-	} src;
-};
-
-#endif /* _NF_CONNTRACK_TUPLE_H */
diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h
deleted file mode 100644
index c3e2060..0000000
--- a/include/net/netfilter/nf_nat.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef _NF_NAT_H
-#define _NF_NAT_H
-#include <linux/netfilter_ipv4.h>
-#include <net/netfilter/nf_conntrack_tuple.h>
-
-#define NF_NAT_MAPPING_TYPE_MAX_NAMELEN 16
-
-enum nf_nat_manip_type
-{
-	IP_NAT_MANIP_SRC,
-	IP_NAT_MANIP_DST
-};
-
-/* SRC manip occurs POST_ROUTING or LOCAL_IN */
-#define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \
-			     (hooknum) != NF_INET_LOCAL_IN)
-
-#define IP_NAT_RANGE_MAP_IPS 1
-#define IP_NAT_RANGE_PROTO_SPECIFIED 2
-#define IP_NAT_RANGE_PROTO_RANDOM 4
-#define IP_NAT_RANGE_PERSISTENT 8
-
-/* NAT sequence number modifications */
-struct nf_nat_seq {
-	/* position of the last TCP sequence number modification (if any) */
-	u_int32_t correction_pos;
-
-	/* sequence number offset before and after last modification */
-	int16_t offset_before, offset_after;
-};
-
-/* Single range specification. */
-struct nf_nat_range
-{
-	/* Set to OR of flags above. */
-	unsigned int flags;
-
-	/* Inclusive: network order. */
-	__be32 min_ip, max_ip;
-
-	/* Inclusive: network order */
-	union nf_conntrack_man_proto min, max;
-};
-
-/* For backwards compat: don't use in modern code. */
-struct nf_nat_multi_range_compat
-{
-	unsigned int rangesize; /* Must be 1. */
-
-	/* hangs off end. */
-	struct nf_nat_range range[1];
-};
-
-#define nf_nat_multi_range nf_nat_multi_range_compat
-#endif
diff --git a/include/xtables-version.h.in b/include/xtables-version.h.in
new file mode 100644
index 0000000..cb13827
--- /dev/null
+++ b/include/xtables-version.h.in
@@ -0,0 +1,2 @@
+#define XTABLES_VERSION "libxtables.so.@libxtables_vmajor@"
+#define XTABLES_VERSION_CODE @libxtables_vmajor@
diff --git a/include/xtables.h b/include/xtables.h
index b5cd9e9..c35a6e6 100644
--- a/include/xtables.h
+++ b/include/xtables.h
@@ -31,8 +31,7 @@
 #define IPPROTO_UDPLITE	136
 #endif
 
-#define XTABLES_VERSION "libxtables.so.6"
-#define XTABLES_VERSION_CODE 6
+#include <xtables-version.h>
 
 struct in_addr;
 
@@ -137,11 +136,13 @@
  * @arg:	input from command line
  * @ext_name:	name of extension currently being processed
  * @entry:	current option being processed
- * @data:	per-extension data block
+ * @data:	per-extension kernel data block
  * @xflags:	options of the extension that have been used
  * @invert:	whether option was used with !
  * @nvals:	number of results in uXX_multi
  * @val:	parsed result
+ * @udata:	per-extension private scratch area
+ * 		(cf. xtables_{match,target}->udata_size)
  */
 struct xt_option_call {
 	const char *arg, *ext_name;
@@ -174,16 +175,19 @@
 		struct xt_entry_target **target;
 	};
 	void *xt_entry;
+	void *udata;
 };
 
 /**
  * @ext_name:	name of extension currently being processed
- * @data:	per-extension data block
+ * @data:	per-extension (kernel) data block
+ * @udata:	per-extension private scratch area
+ * 		(cf. xtables_{match,target}->udata_size)
  * @xflags:	options of the extension that have been used
  */
 struct xt_fcheck_call {
 	const char *ext_name;
-	void *data;
+	void *data, *udata;
 	unsigned int xflags;
 };
 
@@ -197,6 +201,10 @@
 	struct xtables_lmap *next;
 };
 
+enum xtables_ext_flags {
+	XTABLES_EXT_ALIAS = 1 << 0,
+};
+
 /* Include file for additions: new matches and targets. */
 struct xtables_match
 {
@@ -209,10 +217,14 @@
 	struct xtables_match *next;
 
 	const char *name;
+	const char *real_name;
 
 	/* Revision of match (0 by default). */
 	u_int8_t revision;
 
+	/* Extension flags */
+	u_int8_t ext_flags;
+
 	u_int16_t family;
 
 	/* Size of match data. */
@@ -246,6 +258,9 @@
 	/* ip is struct ipt_ip * for example */
 	void (*save)(const void *ip, const struct xt_entry_match *match);
 
+	/* Print match name or alias */
+	const char *(*alias)(const struct xt_entry_match *match);
+
 	/* Pointer to list of extra command-line options */
 	const struct option *extra_opts;
 
@@ -254,7 +269,11 @@
 	void (*x6_fcheck)(struct xt_fcheck_call *);
 	const struct xt_option_entry *x6_options;
 
+	/* Size of per-extension instance extra "global" scratch space */
+	size_t udata_size;
+
 	/* Ignore these men behind the curtain: */
+	void *udata;
 	unsigned int option_offset;
 	struct xt_entry_match *m;
 	unsigned int mflags;
@@ -274,9 +293,15 @@
 
 	const char *name;
 
+	/* Real target behind this, if any. */
+	const char *real_name;
+
 	/* Revision of target (0 by default). */
 	u_int8_t revision;
 
+	/* Extension flags */
+	u_int8_t ext_flags;
+
 	u_int16_t family;
 
 
@@ -310,6 +335,9 @@
 	void (*save)(const void *ip,
 		     const struct xt_entry_target *target);
 
+	/* Print target name or alias */
+	const char *(*alias)(const struct xt_entry_target *target);
+
 	/* Pointer to list of extra command-line options */
 	const struct option *extra_opts;
 
@@ -318,7 +346,10 @@
 	void (*x6_fcheck)(struct xt_fcheck_call *);
 	const struct xt_option_entry *x6_options;
 
+	size_t udata_size;
+
 	/* Ignore these men behind the curtain: */
+	void *udata;
 	unsigned int option_offset;
 	struct xt_entry_target *t;
 	unsigned int tflags;
@@ -402,6 +433,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);
@@ -420,8 +453,6 @@
 /* this is a special 64bit data type that is 8-byte aligned */
 #define aligned_u64 u_int64_t __attribute__((aligned(8)))
 
-int xtables_check_inverse(const char option[], int *invert,
-	int *my_optind, int argc, char **argv);
 extern struct xtables_globals *xt_params;
 #define xtables_error (xt_params->exit_err)
 
@@ -432,6 +463,7 @@
 extern const char *xtables_ipmask_to_numeric(const struct in_addr *);
 extern struct in_addr *xtables_numeric_to_ipaddr(const char *);
 extern struct in_addr *xtables_numeric_to_ipmask(const char *);
+extern int xtables_ipmask_to_cidr(const struct in_addr *);
 extern void xtables_ipparse_any(const char *, struct in_addr **,
 	struct in_addr *, unsigned int *);
 extern void xtables_ipparse_multiple(const char *, struct in_addr **,
@@ -441,6 +473,7 @@
 extern const char *xtables_ip6addr_to_numeric(const struct in6_addr *);
 extern const char *xtables_ip6addr_to_anyname(const struct in6_addr *);
 extern const char *xtables_ip6mask_to_numeric(const struct in6_addr *);
+extern int xtables_ip6mask_to_cidr(const struct in6_addr *);
 extern void xtables_ip6parse_any(const char *, struct in6_addr **,
 	struct in6_addr *, unsigned int *);
 extern void xtables_ip6parse_multiple(const char *, struct in6_addr **,
@@ -452,6 +485,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
@@ -467,6 +516,14 @@
 extern const struct xtables_pprot xtables_chain_protos[];
 extern u_int16_t xtables_parse_protocol(const char *s);
 
+/* kernel revision handling */
+extern int kernel_version;
+extern void get_kernel_version(void);
+#define LINUX_VERSION(x,y,z)	(0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x)	(((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x)	(((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x)	( (x)      & 0xFF)
+
 /* xtoptions.c */
 extern void xtables_option_metavalidate(const char *,
 					const struct xt_option_entry *);
diff --git a/include/xtables.h.in b/include/xtables.h.in
deleted file mode 100644
index 2565dd2..0000000
--- a/include/xtables.h.in
+++ /dev/null
@@ -1,507 +0,0 @@
-#ifndef _XTABLES_H
-#define _XTABLES_H
-
-/*
- * Changing any structs/functions may incur a needed change
- * in libxtables_vcurrent/vage too.
- */
-
-#include <sys/socket.h> /* PF_* */
-#include <sys/types.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <netinet/in.h>
-#include <net/if.h>
-#include <linux/types.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter/x_tables.h>
-
-#ifndef IPPROTO_SCTP
-#define IPPROTO_SCTP 132
-#endif
-#ifndef IPPROTO_DCCP
-#define IPPROTO_DCCP 33
-#endif
-#ifndef IPPROTO_MH
-#	define IPPROTO_MH 135
-#endif
-#ifndef IPPROTO_UDPLITE
-#define IPPROTO_UDPLITE	136
-#endif
-
-#define XTABLES_VERSION "libxtables.so.@libxtables_vmajor@"
-#define XTABLES_VERSION_CODE @libxtables_vmajor@
-
-struct in_addr;
-
-/*
- * .size is here so that there is a somewhat reasonable check
- * against the chosen .type.
- */
-#define XTOPT_POINTER(stype, member) \
-	.ptroff = offsetof(stype, member), \
-	.size = sizeof(((stype *)NULL)->member)
-#define XTOPT_TABLEEND {.name = NULL}
-
-/**
- * Select the format the input has to conform to, as well as the target type
- * (area pointed to with XTOPT_POINTER). Note that the storing is not always
- * uniform. @cb->val will be populated with as much as there is space, i.e.
- * exactly 2 items for ranges, but the target area can receive more values
- * (e.g. in case of ranges), or less values (e.g. %XTTYPE_HOSTMASK).
- *
- * %XTTYPE_NONE:	option takes no argument
- * %XTTYPE_UINT*:	standard integer
- * %XTTYPE_UINT*RC:	colon-separated range of standard integers
- * %XTTYPE_DOUBLE:	double-precision floating point number
- * %XTTYPE_STRING:	arbitrary string
- * %XTTYPE_TOSMASK:	8-bit TOS value with optional mask
- * %XTTYPE_MARKMASK32:	32-bit mark with optional mask
- * %XTTYPE_SYSLOGLEVEL:	syslog level by name or number
- * %XTTYPE_HOST:	one host or address (ptr: union nf_inet_addr)
- * %XTTYPE_HOSTMASK:	one host or address, with an optional prefix length
- * 			(ptr: union nf_inet_addr; only host portion is stored)
- * %XTTYPE_PROTOCOL:	protocol number/name from /etc/protocols (ptr: uint8_t)
- * %XTTYPE_PORT:	16-bit port name or number (supports %XTOPT_NBO)
- * %XTTYPE_PORTRC:	colon-separated port range (names acceptable),
- * 			(supports %XTOPT_NBO)
- * %XTTYPE_PLEN:	prefix length
- * %XTTYPE_PLENMASK:	prefix length (ptr: union nf_inet_addr)
- * %XTTYPE_ETHERMAC:	Ethernet MAC address in hex form
- */
-enum xt_option_type {
-	XTTYPE_NONE,
-	XTTYPE_UINT8,
-	XTTYPE_UINT16,
-	XTTYPE_UINT32,
-	XTTYPE_UINT64,
-	XTTYPE_UINT8RC,
-	XTTYPE_UINT16RC,
-	XTTYPE_UINT32RC,
-	XTTYPE_UINT64RC,
-	XTTYPE_DOUBLE,
-	XTTYPE_STRING,
-	XTTYPE_TOSMASK,
-	XTTYPE_MARKMASK32,
-	XTTYPE_SYSLOGLEVEL,
-	XTTYPE_HOST,
-	XTTYPE_HOSTMASK,
-	XTTYPE_PROTOCOL,
-	XTTYPE_PORT,
-	XTTYPE_PORTRC,
-	XTTYPE_PLEN,
-	XTTYPE_PLENMASK,
-	XTTYPE_ETHERMAC,
-};
-
-/**
- * %XTOPT_INVERT:	option is invertible (usable with !)
- * %XTOPT_MAND:		option is mandatory
- * %XTOPT_MULTI:	option may be specified multiple times
- * %XTOPT_PUT:		store value into memory at @ptroff
- * %XTOPT_NBO:		store value in network-byte order
- * 			(only certain XTTYPEs recognize this)
- */
-enum xt_option_flags {
-	XTOPT_INVERT = 1 << 0,
-	XTOPT_MAND   = 1 << 1,
-	XTOPT_MULTI  = 1 << 2,
-	XTOPT_PUT    = 1 << 3,
-	XTOPT_NBO    = 1 << 4,
-};
-
-/**
- * @name:	name of option
- * @type:	type of input and validation method, see %XTTYPE_*
- * @id:		unique number (within extension) for option, 0-31
- * @excl:	bitmask of flags that cannot be used with this option
- * @also:	bitmask of flags that must be used with this option
- * @flags:	bitmask of option flags, see %XTOPT_*
- * @ptroff:	offset into private structure for member
- * @size:	size of the item pointed to by @ptroff; this is a safeguard
- * @min:	lowest allowed value (for singular integral types)
- * @max:	highest allowed value (for singular integral types)
- */
-struct xt_option_entry {
-	const char *name;
-	enum xt_option_type type;
-	unsigned int id, excl, also, flags;
-	unsigned int ptroff;
-	size_t size;
-	unsigned int min, max;
-};
-
-/**
- * @arg:	input from command line
- * @ext_name:	name of extension currently being processed
- * @entry:	current option being processed
- * @data:	per-extension data block
- * @xflags:	options of the extension that have been used
- * @invert:	whether option was used with !
- * @nvals:	number of results in uXX_multi
- * @val:	parsed result
- */
-struct xt_option_call {
-	const char *arg, *ext_name;
-	const struct xt_option_entry *entry;
-	void *data;
-	unsigned int xflags;
-	bool invert;
-	uint8_t nvals;
-	union {
-		uint8_t u8, u8_range[2], syslog_level, protocol;
-		uint16_t u16, u16_range[2], port, port_range[2];
-		uint32_t u32, u32_range[2];
-		uint64_t u64, u64_range[2];
-		double dbl;
-		struct {
-			union nf_inet_addr haddr, hmask;
-			uint8_t hlen;
-		};
-		struct {
-			uint8_t tos_value, tos_mask;
-		};
-		struct {
-			uint32_t mark, mask;
-		};
-		uint8_t ethermac[6];
-	} val;
-	/* Wished for a world where the ones below were gone: */
-	union {
-		struct xt_entry_match **match;
-		struct xt_entry_target **target;
-	};
-	void *xt_entry;
-};
-
-/**
- * @ext_name:	name of extension currently being processed
- * @data:	per-extension data block
- * @xflags:	options of the extension that have been used
- */
-struct xt_fcheck_call {
-	const char *ext_name;
-	void *data;
-	unsigned int xflags;
-};
-
-/**
- * A "linear"/linked-list based name<->id map, for files similar to
- * /etc/iproute2/.
- */
-struct xtables_lmap {
-	char *name;
-	int id;
-	struct xtables_lmap *next;
-};
-
-/* Include file for additions: new matches and targets. */
-struct xtables_match
-{
-	/*
-	 * ABI/API version this module requires. Must be first member,
-	 * as the rest of this struct may be subject to ABI changes.
-	 */
-	const char *version;
-
-	struct xtables_match *next;
-
-	const char *name;
-
-	/* Revision of match (0 by default). */
-	u_int8_t revision;
-
-	u_int16_t family;
-
-	/* Size of match data. */
-	size_t size;
-
-	/* Size of match data relevent for userspace comparison purposes */
-	size_t userspacesize;
-
-	/* Function which prints out usage message. */
-	void (*help)(void);
-
-	/* Initialize the match. */
-	void (*init)(struct xt_entry_match *m);
-
-	/* Function which parses command options; returns true if it
-           ate an option */
-	/* entry is struct ipt_entry for example */
-	int (*parse)(int c, char **argv, int invert, unsigned int *flags,
-		     const void *entry,
-		     struct xt_entry_match **match);
-
-	/* Final check; exit if not ok. */
-	void (*final_check)(unsigned int flags);
-
-	/* Prints out the match iff non-NULL: put space at end */
-	/* ip is struct ipt_ip * for example */
-	void (*print)(const void *ip,
-		      const struct xt_entry_match *match, int numeric);
-
-	/* Saves the match info in parsable form to stdout. */
-	/* ip is struct ipt_ip * for example */
-	void (*save)(const void *ip, const struct xt_entry_match *match);
-
-	/* Pointer to list of extra command-line options */
-	const struct option *extra_opts;
-
-	/* New parser */
-	void (*x6_parse)(struct xt_option_call *);
-	void (*x6_fcheck)(struct xt_fcheck_call *);
-	const struct xt_option_entry *x6_options;
-
-	/* Ignore these men behind the curtain: */
-	unsigned int option_offset;
-	struct xt_entry_match *m;
-	unsigned int mflags;
-	unsigned int loaded; /* simulate loading so options are merged properly */
-};
-
-struct xtables_target
-{
-	/*
-	 * ABI/API version this module requires. Must be first member,
-	 * as the rest of this struct may be subject to ABI changes.
-	 */
-	const char *version;
-
-	struct xtables_target *next;
-
-
-	const char *name;
-
-	/* Revision of target (0 by default). */
-	u_int8_t revision;
-
-	u_int16_t family;
-
-
-	/* Size of target data. */
-	size_t size;
-
-	/* Size of target data relevent for userspace comparison purposes */
-	size_t userspacesize;
-
-	/* Function which prints out usage message. */
-	void (*help)(void);
-
-	/* Initialize the target. */
-	void (*init)(struct xt_entry_target *t);
-
-	/* Function which parses command options; returns true if it
-           ate an option */
-	/* entry is struct ipt_entry for example */
-	int (*parse)(int c, char **argv, int invert, unsigned int *flags,
-		     const void *entry,
-		     struct xt_entry_target **targetinfo);
-
-	/* Final check; exit if not ok. */
-	void (*final_check)(unsigned int flags);
-
-	/* Prints out the target iff non-NULL: put space at end */
-	void (*print)(const void *ip,
-		      const struct xt_entry_target *target, int numeric);
-
-	/* Saves the targinfo in parsable form to stdout. */
-	void (*save)(const void *ip,
-		     const struct xt_entry_target *target);
-
-	/* Pointer to list of extra command-line options */
-	const struct option *extra_opts;
-
-	/* New parser */
-	void (*x6_parse)(struct xt_option_call *);
-	void (*x6_fcheck)(struct xt_fcheck_call *);
-	const struct xt_option_entry *x6_options;
-
-	/* Ignore these men behind the curtain: */
-	unsigned int option_offset;
-	struct xt_entry_target *t;
-	unsigned int tflags;
-	unsigned int used;
-	unsigned int loaded; /* simulate loading so options are merged properly */
-};
-
-struct xtables_rule_match {
-	struct xtables_rule_match *next;
-	struct xtables_match *match;
-	/* Multiple matches of the same type: the ones before
-	   the current one are completed from parsing point of view */
-	bool completed;
-};
-
-/**
- * struct xtables_pprot -
- *
- * A few hardcoded protocols for 'all' and in case the user has no
- * /etc/protocols.
- */
-struct xtables_pprot {
-	const char *name;
-	u_int8_t num;
-};
-
-enum xtables_tryload {
-	XTF_DONT_LOAD,
-	XTF_DURING_LOAD,
-	XTF_TRY_LOAD,
-	XTF_LOAD_MUST_SUCCEED,
-};
-
-enum xtables_exittype {
-	OTHER_PROBLEM = 1,
-	PARAMETER_PROBLEM,
-	VERSION_PROBLEM,
-	RESOURCE_PROBLEM,
-	XTF_ONLY_ONCE,
-	XTF_NO_INVERT,
-	XTF_BAD_VALUE,
-	XTF_ONE_ACTION,
-};
-
-struct xtables_globals
-{
-	unsigned int option_offset;
-	const char *program_name, *program_version;
-	struct option *orig_opts;
-	struct option *opts;
-	void (*exit_err)(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
-};
-
-#define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern const char *xtables_modprobe_program;
-extern struct xtables_match *xtables_matches;
-extern struct xtables_target *xtables_targets;
-
-extern void xtables_init(void);
-extern void xtables_set_nfproto(uint8_t);
-extern void *xtables_calloc(size_t, size_t);
-extern void *xtables_malloc(size_t);
-extern void *xtables_realloc(void *, size_t);
-
-extern int xtables_insmod(const char *, const char *, bool);
-extern int xtables_load_ko(const char *, bool);
-extern int xtables_set_params(struct xtables_globals *xtp);
-extern void xtables_free_opts(int reset_offset);
-extern struct option *xtables_merge_options(struct option *origopts,
-	struct option *oldopts, const struct option *newopts,
-	unsigned int *option_offset);
-
-extern int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto);
-extern struct xtables_match *xtables_find_match(const char *name,
-	enum xtables_tryload, struct xtables_rule_match **match);
-extern struct xtables_target *xtables_find_target(const char *name,
-	enum xtables_tryload);
-
-/* 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);
-extern void xtables_register_target(struct xtables_target *me);
-extern void xtables_register_targets(struct xtables_target *, unsigned int);
-
-extern bool xtables_strtoul(const char *, char **, uintmax_t *,
-	uintmax_t, uintmax_t);
-extern bool xtables_strtoui(const char *, char **, unsigned int *,
-	unsigned int, unsigned int);
-extern int xtables_service_to_port(const char *name, const char *proto);
-extern u_int16_t xtables_parse_port(const char *port, const char *proto);
-extern void
-xtables_parse_interface(const char *arg, char *vianame, unsigned char *mask);
-
-/* this is a special 64bit data type that is 8-byte aligned */
-#define aligned_u64 u_int64_t __attribute__((aligned(8)))
-
-int xtables_check_inverse(const char option[], int *invert,
-	int *my_optind, int argc, char **argv);
-extern struct xtables_globals *xt_params;
-#define xtables_error (xt_params->exit_err)
-
-extern void xtables_param_act(unsigned int, const char *, ...);
-
-extern const char *xtables_ipaddr_to_numeric(const struct in_addr *);
-extern const char *xtables_ipaddr_to_anyname(const struct in_addr *);
-extern const char *xtables_ipmask_to_numeric(const struct in_addr *);
-extern struct in_addr *xtables_numeric_to_ipaddr(const char *);
-extern struct in_addr *xtables_numeric_to_ipmask(const char *);
-extern void xtables_ipparse_any(const char *, struct in_addr **,
-	struct in_addr *, unsigned int *);
-extern void xtables_ipparse_multiple(const char *, struct in_addr **,
-	struct in_addr **, unsigned int *);
-
-extern struct in6_addr *xtables_numeric_to_ip6addr(const char *);
-extern const char *xtables_ip6addr_to_numeric(const struct in6_addr *);
-extern const char *xtables_ip6addr_to_anyname(const struct in6_addr *);
-extern const char *xtables_ip6mask_to_numeric(const struct in6_addr *);
-extern void xtables_ip6parse_any(const char *, struct in6_addr **,
-	struct in6_addr *, unsigned int *);
-extern void xtables_ip6parse_multiple(const char *, struct in6_addr **,
-	struct in6_addr **, unsigned int *);
-
-/**
- * Print the specified value to standard output, quoting dangerous
- * characters if required.
- */
-extern void xtables_save_string(const char *value);
-
-#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
-#	ifdef _INIT
-#		undef _init
-#		define _init _INIT
-#	endif
-	extern void init_extensions(void);
-	extern void init_extensions4(void);
-	extern void init_extensions6(void);
-#else
-#	define _init __attribute__((constructor)) _INIT
-#endif
-
-extern const struct xtables_pprot xtables_chain_protos[];
-extern u_int16_t xtables_parse_protocol(const char *s);
-
-/* xtoptions.c */
-extern void xtables_option_metavalidate(const char *,
-					const struct xt_option_entry *);
-extern struct option *xtables_options_xfrm(struct option *, struct option *,
-					   const struct xt_option_entry *,
-					   unsigned int *);
-extern void xtables_option_parse(struct xt_option_call *);
-extern void xtables_option_tpcall(unsigned int, char **, bool,
-				  struct xtables_target *, void *);
-extern void xtables_option_mpcall(unsigned int, char **, bool,
-				  struct xtables_match *, void *);
-extern void xtables_option_tfcall(struct xtables_target *);
-extern void xtables_option_mfcall(struct xtables_match *);
-extern void xtables_options_fcheck(const char *, unsigned int,
-				   const struct xt_option_entry *);
-
-extern struct xtables_lmap *xtables_lmap_init(const char *);
-extern void xtables_lmap_free(struct xtables_lmap *);
-extern int xtables_lmap_name2id(const struct xtables_lmap *, const char *);
-extern const char *xtables_lmap_id2name(const struct xtables_lmap *, int);
-
-#ifdef XTABLES_INTERNAL
-
-/* Shipped modules rely on this... */
-
-#	ifndef ARRAY_SIZE
-#		define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
-#	endif
-
-extern void _init(void);
-
-#endif
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* _XTABLES_H */
diff --git a/iptables/.gitignore b/iptables/.gitignore
index 5a08937..c9c3178 100644
--- a/iptables/.gitignore
+++ b/iptables/.gitignore
@@ -5,6 +5,8 @@
 /ip6tables-static
 /iptables
 /iptables.8
+/iptables-extensions.8
+/iptables-extensions.8.tmpl
 /iptables-save
 /iptables-restore
 /iptables-static
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 13cca9c..501e825 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -3,20 +3,8 @@
 AM_CFLAGS        = ${regular_CFLAGS}
 AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
 
-lib_LTLIBRARIES       = libxtables.la
-libxtables_la_SOURCES = xtables.c xtoptions.c
-libxtables_la_LDFLAGS = -version-info ${libxtables_vcurrent}:0:${libxtables_vage}
-if ENABLE_SHARED
-libxtables_la_CFLAGS  = ${AM_CFLAGS}
-libxtables_la_LIBADD  = -ldl
-else
-libxtables_la_CFLAGS  = ${AM_CFLAGS} -DNO_SHARED_LIBS=1
-libxtables_la_LIBADD  =
-endif
-
 xtables_multi_SOURCES  = xtables-multi.c iptables-xml.c
-xtables_multi_CFLAGS   = ${AM_CFLAGS} -DIPTABLES_MULTI
-xtables_multi_LDFLAGS  = -rdynamic
+xtables_multi_CFLAGS   = ${AM_CFLAGS}
 xtables_multi_LDADD    = ../extensions/libext.a
 if ENABLE_STATIC
 xtables_multi_CFLAGS  += -DALL_INCLUSIVE
@@ -34,12 +22,12 @@
 xtables_multi_LDADD   += ../libiptc/libip6tc.la ../extensions/libext6.a
 endif
 xtables_multi_SOURCES += xshared.c
-xtables_multi_LDADD   += libxtables.la -lm
+xtables_multi_LDADD   += ../libxtables/libxtables.la -lm
 
 sbin_PROGRAMS    = xtables-multi
 man_MANS         = iptables.8 iptables-restore.8 iptables-save.8 \
                    iptables-xml.1 ip6tables.8 ip6tables-restore.8 \
-                   ip6tables-save.8
+                   ip6tables-save.8 iptables-extensions.8
 CLEANFILES       = iptables.8 ip6tables.8
 
 vx_bin_links   = iptables-xml
@@ -50,11 +38,10 @@
 v6_sbin_links  = ip6tables ip6tables-restore ip6tables-save
 endif
 
-iptables.8: ${srcdir}/iptables.8.in ../extensions/matches4.man ../extensions/targets4.man
-	${AM_VERBOSE_GEN} sed -e 's/@PACKAGE_AND_VERSION@/${PACKAGE} ${PACKAGE_VERSION}/g' -e '/@MATCH@/ r extensions/matches4.man' -e '/@TARGET@/ r extensions/targets4.man' $< >$@;
-
-ip6tables.8: ${srcdir}/ip6tables.8.in ../extensions/matches6.man ../extensions/targets6.man
-	${AM_VERBOSE_GEN} sed -e 's/@PACKAGE_AND_VERSION@/${PACKAGE} ${PACKAGE_VERSION}/g' -e '/@MATCH@/ r extensions/matches6.man' -e '/@TARGET@/ r extensions/targets6.man' $< >$@;
+iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../extensions/targets.man
+	${AM_VERBOSE_GEN} sed \
+		-e '/@MATCH@/ r ../extensions/matches.man' \
+		-e '/@TARGET@/ r ../extensions/targets.man' $< >$@;
 
 pkgconfig_DATA = xtables.pc
 
diff --git a/iptables/ip6tables-restore.8 b/iptables/ip6tables-restore.8
index 0264807..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,9 +32,26 @@
 \fB\-c\fR, \fB\-\-counters\fR
 restore the values of all packet and byte counters
 .TP
-\fB\-n\fR, \fB\-\-noflush\fR 
+\fB\-h\fP, \fB\-\-help\fP
+Print a short option summary.
 .TP
-don't flush the previous contents of the table. If not specified, 
+\fB\-n\fR, \fB\-\-noflush\fR 
+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.
 .B ip6tables-restore
 flushes (deletes) all previous contents of the respective IPv6 Table.
 .SH BUGS
diff --git a/iptables/ip6tables-restore.c b/iptables/ip6tables-restore.c
index 420bc52..b8b9e0d 100644
--- a/iptables/ip6tables-restore.c
+++ b/iptables/ip6tables-restore.c
@@ -36,6 +36,7 @@
 	{.name = "help",     .has_arg = false, .val = 'h'},
 	{.name = "noflush",  .has_arg = false, .val = 'n'},
 	{.name = "modprobe", .has_arg = true,  .val = 'M'},
+	{.name = "table",    .has_arg = true,  .val = 'T'},
 	{NULL},
 };
 
@@ -55,9 +56,9 @@
 	exit(1);
 }
 
-static struct ip6tc_handle *create_handle(const char *tablename)
+static struct xtc_handle *create_handle(const char *tablename)
 {
-	struct ip6tc_handle *handle;
+	struct xtc_handle *handle;
 
 	handle = ip6tc_init(tablename);
 
@@ -76,7 +77,7 @@
 	return handle;
 }
 
-static int parse_counters(char *string, struct ip6t_counters *ctr)
+static int parse_counters(char *string, struct xt_counters *ctr)
 {
 	unsigned long long pcnt, bcnt;
 	int ret;
@@ -97,7 +98,7 @@
 	DEBUGP("add_argv: %s\n", what);
 	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
 		newargv[newargc] = strdup(what);
-		newargc++;
+		newargv[++newargc] = NULL;
 		return 1;
 	} else {
 		xtables_error(PARAMETER_PROBLEM,
@@ -113,18 +114,80 @@
 		free(newargv[i]);
 }
 
-#ifdef IPTABLES_MULTI
-int ip6tables_restore_main(int argc, char *argv[])
-#else
-int main(int argc, char *argv[])
-#endif
+static void add_param_to_argv(char *parsestart)
 {
-	struct ip6tc_handle *handle = NULL;
+	int quote_open = 0, escaped = 0, param_len = 0;
+	char param_buffer[1024], *curchar;
+
+	/* After fighting with strtok enough, here's now
+	 * a 'real' parser. According to Rusty I'm now no
+	 * longer a real hacker, but I can live with that */
+
+	for (curchar = parsestart; *curchar; curchar++) {
+		if (quote_open) {
+			if (escaped) {
+				param_buffer[param_len++] = *curchar;
+				escaped = 0;
+				continue;
+			} else if (*curchar == '\\') {
+				escaped = 1;
+				continue;
+			} else if (*curchar == '"') {
+				quote_open = 0;
+				*curchar = ' ';
+			} else {
+				param_buffer[param_len++] = *curchar;
+				continue;
+			}
+		} else {
+			if (*curchar == '"') {
+				quote_open = 1;
+				continue;
+			}
+		}
+
+		if (*curchar == ' '
+		    || *curchar == '\t'
+		    || * curchar == '\n') {
+			if (!param_len) {
+				/* two spaces? */
+				continue;
+			}
+
+			param_buffer[param_len] = '\0';
+
+			/* check if table name specified */
+			if (!strncmp(param_buffer, "-t", 2)
+                            || !strncmp(param_buffer, "--table", 8)) {
+				xtables_error(PARAMETER_PROBLEM,
+				"The -t option (seen in line %u) cannot be "
+				"used in ip6tables-restore.\n", line);
+				exit(1);
+			}
+
+			add_argv(param_buffer);
+			param_len = 0;
+		} else {
+			/* regular character, copy to buffer */
+			param_buffer[param_len++] = *curchar;
+
+			if (param_len >= sizeof(param_buffer))
+				xtables_error(PARAMETER_PROBLEM,
+				   "Parameter too long!");
+		}
+	}
+}
+
+int ip6tables_restore_main(int argc, char *argv[])
+{
+	struct xtc_handle *handle = NULL;
 	char buffer[10240];
 	int c;
-	char curtable[IP6T_TABLE_MAXNAMELEN + 1];
+	char curtable[XT_TABLE_MAXNAMELEN + 1];
 	FILE *in;
 	int in_table = 0, testing = 0;
+	const char *tablename = NULL;
+	const struct xtc_ops *ops = &ip6tc_ops;
 
 	line = 0;
 
@@ -141,7 +204,7 @@
 	init_extensions6();
 #endif
 
-	while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
 		switch (c) {
 			case 'b':
 				binary = 1;
@@ -165,6 +228,9 @@
 			case 'M':
 				xtables_modprobe_program = optarg;
 				break;
+			case 'T':
+				tablename = optarg;
+				break;
 		}
 	}
 
@@ -196,8 +262,8 @@
 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
 			if (!testing) {
 				DEBUGP("Calling commit\n");
-				ret = ip6tc_commit(handle);
-				ip6tc_free(handle);
+				ret = ops->commit(handle);
+				ops->free(handle);
 				handle = NULL;
 			} else {
 				DEBUGP("Not calling commit, testing\n");
@@ -213,15 +279,16 @@
 			if (!table) {
 				xtables_error(PARAMETER_PROBLEM,
 					"%s: line %u table name invalid\n",
-					ip6tables_globals.program_name,
-					line);
+					xt_params->program_name, line);
 				exit(1);
 			}
-			strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
-			curtable[IP6T_TABLE_MAXNAMELEN] = '\0';
+			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
+			curtable[XT_TABLE_MAXNAMELEN] = '\0';
 
+			if (tablename != NULL && strcmp(tablename, table) != 0)
+				continue;
 			if (handle)
-				ip6tc_free(handle);
+				ops->free(handle);
 
 			handle = create_handle(table);
 			if (noflush == 0) {
@@ -248,8 +315,7 @@
 			if (!chain) {
 				xtables_error(PARAMETER_PROBLEM,
 					   "%s: line %u chain name invalid\n",
-					   ip6tables_globals.program_name,
-					   line);
+					   xt_params->program_name, line);
 				exit(1);
 			}
 
@@ -259,17 +325,17 @@
 					   "(%u chars max)",
 					   chain, XT_EXTENSION_MAXNAMELEN - 1);
 
-			if (ip6tc_builtin(chain, handle) <= 0) {
-				if (noflush && ip6tc_is_chain(chain, handle)) {
+			if (ops->builtin(chain, handle) <= 0) {
+				if (noflush && ops->is_chain(chain, handle)) {
 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
-					if (!ip6tc_flush_entries(chain, handle))
+					if (!ops->flush_entries(chain, handle))
 						xtables_error(PARAMETER_PROBLEM,
 							   "error flushing chain "
 							   "'%s':%s\n", chain,
 							   strerror(errno));
 				} else {
 					DEBUGP("Creating new chain '%s'\n", chain);
-					if (!ip6tc_create_chain(chain, handle))
+					if (!ops->create_chain(chain, handle))
 						xtables_error(PARAMETER_PROBLEM,
 							   "error creating chain "
 							   "'%s':%s\n", chain,
@@ -282,13 +348,12 @@
 			if (!policy) {
 				xtables_error(PARAMETER_PROBLEM,
 					   "%s: line %u policy invalid\n",
-					   ip6tables_globals.program_name,
-					   line);
+					   xt_params->program_name, line);
 				exit(1);
 			}
 
 			if (strcmp(policy, "-") != 0) {
-				struct ip6t_counters count;
+				struct xt_counters count;
 
 				if (counters) {
 					char *ctrs;
@@ -300,20 +365,19 @@
 							  "for chain '%s'\n", chain);
 
 				} else {
-					memset(&count, 0,
-					       sizeof(struct ip6t_counters));
+					memset(&count, 0, sizeof(count));
 				}
 
 				DEBUGP("Setting policy of chain %s to %s\n",
 					chain, policy);
 
-				if (!ip6tc_set_policy(chain, policy, &count,
+				if (!ops->set_policy(chain, policy, &count,
 						     handle))
 					xtables_error(OTHER_PROBLEM,
 						"Can't set policy `%s'"
 						" on `%s' line %u: %s\n",
 						policy, chain, line,
-						ip6tc_strerror(errno));
+						ops->strerror(errno));
 			}
 
 			ret = 1;
@@ -325,11 +389,6 @@
 			char *bcnt = NULL;
 			char *parsestart;
 
-			/* the parser */
-			char *curchar;
-			int quote_open, escaped;
-			size_t param_len;
-
 			/* reset the newargv */
 			newargc = 0;
 
@@ -370,69 +429,7 @@
 				add_argv((char *) bcnt);
 			}
 
-			/* After fighting with strtok enough, here's now
-			 * a 'real' parser. According to Rusty I'm now no
-			 * longer a real hacker, but I can live with that */
-
-			quote_open = 0;
-			escaped = 0;
-			param_len = 0;
-
-			for (curchar = parsestart; *curchar; curchar++) {
-				char param_buffer[1024];
-
-				if (quote_open) {
-					if (escaped) {
-						param_buffer[param_len++] = *curchar;
-						escaped = 0;
-						continue;
-					} else if (*curchar == '\\') {
-						escaped = 1;
-						continue;
-					} else if (*curchar == '"') {
-						quote_open = 0;
-						*curchar = ' ';
-					} else {
-						param_buffer[param_len++] = *curchar;
-						continue;
-					}
-				} else {
-					if (*curchar == '"') {
-						quote_open = 1;
-						continue;
-					}
-				}
-
-				if (*curchar == ' '
-				    || *curchar == '\t'
-				    || * curchar == '\n') {
-					if (!param_len) {
-						/* two spaces? */
-						continue;
-					}
-
-					param_buffer[param_len] = '\0';
-
-					/* check if table name specified */
-					if (!strncmp(param_buffer, "-t", 2)
-                                            || !strncmp(param_buffer, "--table", 8)) {
-						xtables_error(PARAMETER_PROBLEM,
-						   "Line %u seems to have a "
-						   "-t table option.\n", line);
-						exit(1);
-					}
-
-					add_argv(param_buffer);
-					param_len = 0;
-				} else {
-					/* regular character, copy to buffer */
-					param_buffer[param_len++] = *curchar;
-
-					if (param_len >= sizeof(param_buffer))
-						xtables_error(PARAMETER_PROBLEM,
-						   "Parameter too long!");
-				}
-			}
+			add_param_to_argv(parsestart);
 
 			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
 				newargc, curtable);
@@ -441,26 +438,25 @@
 				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
 
 			ret = do_command6(newargc, newargv,
-					 &newargv[2], &handle);
+					 &newargv[2], &handle, true);
 
 			free_argv();
 			fflush(stdout);
 		}
+		if (tablename != NULL && strcmp(tablename, curtable) != 0)
+			continue;
 		if (!ret) {
 			fprintf(stderr, "%s: line %u failed\n",
-					ip6tables_globals.program_name,
-					line);
+					xt_params->program_name, line);
 			exit(1);
 		}
 	}
 	if (in_table) {
 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
-				ip6tables_globals.program_name,
-				line + 1);
+				xt_params->program_name, line + 1);
 		exit(1);
 	}
 
-	if (in != NULL)
-		fclose(in);
+	fclose(in);
 	return 0;
 }
diff --git a/iptables/ip6tables-save.c b/iptables/ip6tables-save.c
index 39a3325..d819b30 100644
--- a/iptables/ip6tables-save.c
+++ b/iptables/ip6tables-save.c
@@ -22,10 +22,9 @@
 #include <dlfcn.h>
 #endif
 
-static int show_binary = 0, show_counters = 0;
+static int show_counters = 0;
 
 static const struct option options[] = {
-	{.name = "binary",   .has_arg = false, .val = 'b'},
 	{.name = "counters", .has_arg = false, .val = 'c'},
 	{.name = "dump",     .has_arg = false, .val = 'd'},
 	{.name = "table",    .has_arg = true,  .val = 't'},
@@ -39,7 +38,7 @@
 {
 	int ret = 1;
 	FILE *procfile = NULL;
-	char tablename[IP6T_TABLE_MAXNAMELEN+1];
+	char tablename[XT_TABLE_MAXNAMELEN+1];
 
 	procfile = fopen("/proc/net/ip6_tables_names", "re");
 	if (!procfile)
@@ -61,7 +60,7 @@
 
 static int do_output(const char *tablename)
 {
-	struct ip6tc_handle *h;
+	struct xtc_handle *h;
 	const char *chain = NULL;
 
 	if (!tablename)
@@ -76,52 +75,45 @@
 		xtables_error(OTHER_PROBLEM, "Cannot initialize: %s\n",
 			   ip6tc_strerror(errno));
 
-	if (!show_binary) {
-		time_t now = time(NULL);
+	time_t now = time(NULL);
 
-		printf("# Generated by ip6tables-save v%s on %s",
-		       IPTABLES_VERSION, ctime(&now));
-		printf("*%s\n", tablename);
+	printf("# Generated by ip6tables-save v%s on %s",
+	       IPTABLES_VERSION, ctime(&now));
+	printf("*%s\n", tablename);
 
-		/* Dump out chain names first,
-		 * thereby preventing dependency conflicts */
-		for (chain = ip6tc_first_chain(h);
-		     chain;
-		     chain = ip6tc_next_chain(h)) {
+	/* Dump out chain names first,
+	 * thereby preventing dependency conflicts */
+	for (chain = ip6tc_first_chain(h);
+	     chain;
+	     chain = ip6tc_next_chain(h)) {
 
-			printf(":%s ", chain);
-			if (ip6tc_builtin(chain, h)) {
-				struct ip6t_counters count;
-				printf("%s ",
-				       ip6tc_get_policy(chain, &count, h));
-				printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
-			} else {
-				printf("- [0:0]\n");
-			}
+		printf(":%s ", chain);
+		if (ip6tc_builtin(chain, h)) {
+			struct xt_counters count;
+			printf("%s ",
+			       ip6tc_get_policy(chain, &count, h));
+			printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+		} else {
+			printf("- [0:0]\n");
 		}
-
-
-		for (chain = ip6tc_first_chain(h);
-		     chain;
-		     chain = ip6tc_next_chain(h)) {
-			const struct ip6t_entry *e;
-
-			/* Dump out rules */
-			e = ip6tc_first_rule(chain, h);
-			while(e) {
-				print_rule6(e, h, chain, show_counters);
-				e = ip6tc_next_rule(e, h);
-			}
-		}
-
-		now = time(NULL);
-		printf("COMMIT\n");
-		printf("# Completed on %s", ctime(&now));
-	} else {
-		/* Binary, huh?  OK. */
-		xtables_error(OTHER_PROBLEM, "Binary NYI\n");
 	}
 
+	for (chain = ip6tc_first_chain(h);
+	     chain;
+	     chain = ip6tc_next_chain(h)) {
+		const struct ip6t_entry *e;
+
+		/* Dump out rules */
+		e = ip6tc_first_rule(chain, h);
+		while(e) {
+			print_rule6(e, h, chain, show_counters);
+			e = ip6tc_next_rule(e, h);
+		}
+	}
+
+	now = time(NULL);
+	printf("COMMIT\n");
+	printf("# Completed on %s", ctime(&now));
 	ip6tc_free(h);
 
 	return 1;
@@ -131,11 +123,7 @@
  * :Chain name POLICY packets bytes
  * rule
  */
-#ifdef IPTABLES_MULTI
 int ip6tables_save_main(int argc, char *argv[])
-#else
-int main(int argc, char *argv[])
-#endif
 {
 	const char *tablename = NULL;
 	int c;
@@ -155,10 +143,6 @@
 
 	while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
 		switch (c) {
-		case 'b':
-			show_binary = 1;
-			break;
-
 		case 'c':
 			show_counters = 1;
 			break;
diff --git a/iptables/ip6tables-standalone.c b/iptables/ip6tables-standalone.c
index 9d8d5a0..656e08d 100644
--- a/iptables/ip6tables-standalone.c
+++ b/iptables/ip6tables-standalone.c
@@ -37,17 +37,12 @@
 #include <ip6tables.h>
 #include "ip6tables-multi.h"
 
-#ifdef IPTABLES_MULTI
 int
 ip6tables_main(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
 {
 	int ret;
 	char *table = "filter";
-	struct ip6tc_handle *handle = NULL;
+	struct xtc_handle *handle = NULL;
 
 	ip6tables_globals.program_name = "ip6tables";
 	ret = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
@@ -63,7 +58,7 @@
 	init_extensions6();
 #endif
 
-	ret = do_command6(argc, argv, &table, &handle);
+	ret = do_command6(argc, argv, &table, &handle, false);
 	if (ret) {
 		ret = ip6tc_commit(handle);
 		ip6tc_free(handle);
diff --git a/iptables/ip6tables.8.in b/iptables/ip6tables.8.in
index 48ba18e..5b2a7d7 100644
--- a/iptables/ip6tables.8.in
+++ b/iptables/ip6tables.8.in
@@ -1,4 +1,4 @@
-.TH IP6TABLES 8 "" "iptables 1.4.4" "iptables 1.4.4"
+.TH IP6TABLES 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
 .\"
 .\" Man page written by Andras Kis-Szabo <kisza@sch.bme.hu>
 .\" It is based on iptables man page.
@@ -87,7 +87,7 @@
 is matched, the target specified by the chain policy determines the
 fate of the packet.
 .SH TABLES
-There are currently three independent tables (which tables are present
+There are currently five independent tables (which tables are present
 at any time depends on the kernel configuration options and which
 modules are present).
 .TP
@@ -106,6 +106,13 @@
 \fBFORWARD\fP (for packets being routed through the box), and
 \fBOUTPUT\fP (for locally-generated packets).
 .TP
+\fBnat\fP:
+This table is consulted when a packet that creates a new
+connection is encountered.  It consists of three built-ins: \fBPREROUTING\fP
+(for altering packets as soon as they come in), \fBOUTPUT\fP
+(for altering locally-generated packets before routing), and \fBPOSTROUTING\fP
+(for altering packets as they are about to go out). Available since kernel 3.7.
+.TP
 \fBmangle\fP:
 This table is used for specialized packet alteration.  Until kernel
 2.4.17 it had two built-in chains: \fBPREROUTING\fP
@@ -240,6 +247,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,
@@ -250,7 +266,11 @@
 \fBesp\fP and \fBipv6\-nonext\fP
 can be used with Kernel version 2.6.11 or later.
 A "!" argument before the protocol inverts the
-test.  The number zero is equivalent to \fBall\fP. "\fBall\fP"
+test.  The number zero is equivalent to \fBall\fP, which means that you cannot
+test the protocol field for the value 0 directly. To match on a HBH header,
+even if it were the last, you cannot use \fB\-p 0\fP, but always need
+\fB\-m hbh\fP.
+"\fBall\fP"
 will match with all protocols and is taken as default when this
 option is omitted.
 .TP
@@ -277,6 +297,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
@@ -333,7 +360,15 @@
 1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
 the \fB\-x\fP flag to change this).
 For appending, insertion, deletion and replacement, this causes
-detailed information on the rule or rules to be printed.
+detailed information on the rule or rules to be printed. \fB\-v\fP may be
+specified multiple times to possibly emit more detailed debug statements.
+.TP
+\fB\-w\fP, \fB\-\-wait\fP
+Wait for the xtables lock.
+To prevent multiple instances of the program from running concurrently,
+an attempt will be made to obtain an exclusive lock at launch.  By default,
+the program will exit if the lock cannot be obtained.  This option will
+make the program wait until the exclusive lock can be obtained.
 .TP
 \fB\-n\fP, \fB\-\-numeric\fP
 Numeric output.
@@ -356,23 +391,9 @@
 When adding or inserting rules into a chain, use \fIcommand\fP
 to load any necessary modules (targets, match extensions, etc).
 .SH MATCH EXTENSIONS
-ip6tables can use extended packet matching modules.  These are loaded
-in two ways: implicitly, when \fB\-p\fP or \fB\-\-protocol\fP
-is specified, or with the \fB\-m\fP or \fB\-\-match\fP
-options, followed by the matching module name; after these, various
-extra command line options become available, depending on the specific
-module.  You can specify multiple extended match modules in one line,
-and you can use the \fB\-h\fP or \fB\-\-help\fP
-options after the module has been specified to receive help specific
-to that module.
 .PP
-The following are included in the base package, and most of these can
-be preceded by a "\fB!\fP" to invert the sense of the match.
-.\" @MATCH@
-.SH TARGET EXTENSIONS
-ip6tables can use extended target modules: the following are included
-in the standard distribution.
-.\" @TARGET@
+iptables can use extended packet matching and target modules.
+A list of these is available in the \fBiptables\-extensions\fP(8) manpage.
 .SH DIAGNOSTICS
 Various error messages are printed to standard error.  The exit code
 is 0 for correct functioning.  Errors which appear to be caused by
@@ -399,6 +420,8 @@
 \fBip6tables\-save\fP(8),
 \fBip6tables\-restore\fP(8),
 \fBiptables\fP(8),
+\fBiptables\-apply\fP(8),
+\fBiptables\-extensions\fP(8),
 \fBiptables\-save\fP(8),
 \fBiptables\-restore\fP(8),
 \fBlibipq\fP(3).
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 4037acf..1fb33f6 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
@@ -85,7 +70,7 @@
 #define CMD_CHECK		0x4000U
 #define NUMBER_OF_CMD	16
 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
-				 'Z', 'N', 'X', 'P', 'E', 'S', 'C' };
+				 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
 
 #define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
 static const char optflags[]
@@ -117,6 +102,7 @@
 	{.name = "numeric",       .has_arg = 0, .val = 'n'},
 	{.name = "out-interface", .has_arg = 1, .val = 'o'},
 	{.name = "verbose",       .has_arg = 0, .val = 'v'},
+	{.name = "wait",          .has_arg = 0, .val = 'w'},
 	{.name = "exact",         .has_arg = 0, .val = 'x'},
 	{.name = "version",       .has_arg = 0, .val = 'V'},
 	{.name = "help",          .has_arg = 2, .val = 'h'},
@@ -172,7 +158,7 @@
 /* -n */ 0,
 /* -s */ IP6T_INV_SRCIP,
 /* -d */ IP6T_INV_DSTIP,
-/* -p */ IP6T_INV_PROTO,
+/* -p */ XT_INV_PROTO,
 /* -j */ 0,
 /* -v */ 0,
 /* -x */ 0,
@@ -252,7 +238,7 @@
 "Options:\n"
 "    --ipv4	-4		Error (line is ignored by ip6tables-restore)\n"
 "    --ipv6	-6		Nothing (line is ignored by iptables-restore)\n"
-"[!] --proto	-p proto	protocol: by number or name, eg. `tcp'\n"
+"[!] --protocol	-p proto	protocol: by number or name, eg. `tcp'\n"
 "[!] --source	-s address[/mask][,...]\n"
 "				source specification\n"
 "[!] --destination -d address[/mask][,...]\n"
@@ -272,6 +258,7 @@
 "				network interface name ([+] for wildcard)\n"
 "  --table	-t table	table to manipulate (default: `filter')\n"
 "  --verbose	-v		verbose mode\n"
+"  --wait	-w		wait for the xtables lock\n"
 "  --line-numbers		print line numbers when listing\n"
 "  --exact	-x		expand numbers (display exact values)\n"
 /*"[!] --fragment	-f		match second or further fragments only\n"*/
@@ -442,45 +429,20 @@
 	}
 }
 
-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 ip6tc_handle *handle)
+print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
 {
-	struct ip6t_counters counters;
+	struct xt_counters counters;
 	const char *pol = ip6tc_get_policy(chain, &counters, handle);
 	printf("Chain %s", chain);
 	if (pol) {
 		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");
@@ -519,7 +481,7 @@
 
 
 static int
-print_match(const struct ip6t_entry_match *m,
+print_match(const struct xt_entry_match *m,
 	    const struct ip6t_ip6 *ip,
 	    int numeric)
 {
@@ -545,16 +507,16 @@
 	       const char *targname,
 	       unsigned int num,
 	       unsigned int format,
-	       struct ip6tc_handle *const handle)
+	       struct xtc_handle *const handle)
 {
 	const struct xtables_target *target = NULL;
-	const struct ip6t_entry_target *t;
+	const struct xt_entry_target *t;
 	char buf[BUFSIZ];
 
 	if (!ip6tc_is_chain(targname, handle))
 		target = xtables_find_target(targname, XTF_TRY_LOAD);
 	else
-		target = xtables_find_target(IP6T_STANDARD_TARGET,
+		target = xtables_find_target(XT_STANDARD_TARGET,
 		         XTF_LOAD_MUST_SUCCEED);
 
 	t = ip6t_get_target((struct ip6t_entry *)fw);
@@ -563,14 +525,14 @@
 		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))
 		printf(FMT("%-9s ", "%s "), targname);
 
-	fputc(fw->ipv6.invflags & IP6T_INV_PROTO ? '!' : ' ', stdout);
+	fputc(fw->ipv6.invflags & XT_INV_PROTO ? '!' : ' ', stdout);
 	{
 		const char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC);
 		if (pname)
@@ -667,16 +629,16 @@
 
 static void
 print_firewall_line(const struct ip6t_entry *fw,
-		    struct ip6tc_handle *const h)
+		    struct xtc_handle *const h)
 {
-	struct ip6t_entry_target *t;
+	struct xt_entry_target *t;
 
 	t = ip6t_get_target((struct ip6t_entry *)fw);
 	print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
 }
 
 static int
-append_entry(const ip6t_chainlabel chain,
+append_entry(const xt_chainlabel chain,
 	     struct ip6t_entry *fw,
 	     unsigned int nsaddrs,
 	     const struct in6_addr saddrs[],
@@ -685,7 +647,7 @@
 	     const struct in6_addr daddrs[],
 	     const struct in6_addr dmasks[],
 	     int verbose,
-	     struct ip6tc_handle *handle)
+	     struct xtc_handle *handle)
 {
 	unsigned int i, j;
 	int ret = 1;
@@ -706,13 +668,13 @@
 }
 
 static int
-replace_entry(const ip6t_chainlabel chain,
+replace_entry(const xt_chainlabel chain,
 	      struct ip6t_entry *fw,
 	      unsigned int rulenum,
 	      const struct in6_addr *saddr, const struct in6_addr *smask,
 	      const struct in6_addr *daddr, const struct in6_addr *dmask,
 	      int verbose,
-	      struct ip6tc_handle *handle)
+	      struct xtc_handle *handle)
 {
 	fw->ipv6.src = *saddr;
 	fw->ipv6.dst = *daddr;
@@ -725,7 +687,7 @@
 }
 
 static int
-insert_entry(const ip6t_chainlabel chain,
+insert_entry(const xt_chainlabel chain,
 	     struct ip6t_entry *fw,
 	     unsigned int rulenum,
 	     unsigned int nsaddrs,
@@ -735,7 +697,7 @@
 	     const struct in6_addr daddrs[],
 	     const struct in6_addr dmasks[],
 	     int verbose,
-	     struct ip6tc_handle *handle)
+	     struct xtc_handle *handle)
 {
 	unsigned int i, j;
 	int ret = 1;
@@ -766,10 +728,10 @@
 
 	size = sizeof(struct ip6t_entry);
 	for (matchp = matches; matchp; matchp = matchp->next)
-		size += XT_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
 
 	mask = xtables_calloc(1, size
-			 + XT_ALIGN(sizeof(struct ip6t_entry_target))
+			 + XT_ALIGN(sizeof(struct xt_entry_target))
 			 + target->size);
 
 	memset(mask, 0xFF, sizeof(struct ip6t_entry));
@@ -777,20 +739,20 @@
 
 	for (matchp = matches; matchp; matchp = matchp->next) {
 		memset(mptr, 0xFF,
-		       XT_ALIGN(sizeof(struct ip6t_entry_match))
+		       XT_ALIGN(sizeof(struct xt_entry_match))
 		       + matchp->match->userspacesize);
-		mptr += XT_ALIGN(sizeof(struct ip6t_entry_match)) + matchp->match->size;
+		mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
 	}
 
 	memset(mptr, 0xFF,
-	       XT_ALIGN(sizeof(struct ip6t_entry_target))
+	       XT_ALIGN(sizeof(struct xt_entry_target))
 	       + target->userspacesize);
 
 	return mask;
 }
 
 static int
-delete_entry(const ip6t_chainlabel chain,
+delete_entry(const xt_chainlabel chain,
 	     struct ip6t_entry *fw,
 	     unsigned int nsaddrs,
 	     const struct in6_addr saddrs[],
@@ -799,7 +761,7 @@
 	     const struct in6_addr daddrs[],
 	     const struct in6_addr dmasks[],
 	     int verbose,
-	     struct ip6tc_handle *handle,
+	     struct xtc_handle *handle,
 	     struct xtables_rule_match *matches,
 	     const struct xtables_target *target)
 {
@@ -825,11 +787,11 @@
 }
 
 static int
-check_entry(const ip6t_chainlabel chain, struct ip6t_entry *fw,
+check_entry(const xt_chainlabel chain, struct ip6t_entry *fw,
 	    unsigned int nsaddrs, const struct in6_addr *saddrs,
 	    const struct in6_addr *smasks, unsigned int ndaddrs,
 	    const struct in6_addr *daddrs, const struct in6_addr *dmasks,
-	    bool verbose, struct ip6tc_handle *handle,
+	    bool verbose, struct xtc_handle *handle,
 	    struct xtables_rule_match *matches,
 	    const struct xtables_target *target)
 {
@@ -855,8 +817,8 @@
 }
 
 int
-for_each_chain6(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *),
-	       int verbose, int builtinstoo, struct ip6tc_handle *handle)
+for_each_chain6(int (*fn)(const xt_chainlabel, int, struct xtc_handle *),
+	       int verbose, int builtinstoo, struct xtc_handle *handle)
 {
 	int ret = 1;
 	const char *chain;
@@ -869,21 +831,21 @@
 		chain = ip6tc_next_chain(handle);
 	}
 
-	chains = xtables_malloc(sizeof(ip6t_chainlabel) * chaincount);
+	chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount);
 	i = 0;
 	chain = ip6tc_first_chain(handle);
 	while (chain) {
-		strcpy(chains + i*sizeof(ip6t_chainlabel), chain);
+		strcpy(chains + i*sizeof(xt_chainlabel), chain);
 		i++;
 		chain = ip6tc_next_chain(handle);
 	}
 
 	for (i = 0; i < chaincount; i++) {
 		if (!builtinstoo
-		    && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel),
+		    && ip6tc_builtin(chains + i*sizeof(xt_chainlabel),
 				    handle) == 1)
 			continue;
-		ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle);
+		ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle);
 	}
 
 	free(chains);
@@ -891,8 +853,8 @@
 }
 
 int
-flush_entries6(const ip6t_chainlabel chain, int verbose,
-	      struct ip6tc_handle *handle)
+flush_entries6(const xt_chainlabel chain, int verbose,
+	      struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain6(flush_entries6, verbose, 1, handle);
@@ -903,8 +865,8 @@
 }
 
 static int
-zero_entries(const ip6t_chainlabel chain, int verbose,
-	     struct ip6tc_handle *handle)
+zero_entries(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain6(zero_entries, verbose, 1, handle);
@@ -915,8 +877,8 @@
 }
 
 int
-delete_chain6(const ip6t_chainlabel chain, int verbose,
-	     struct ip6tc_handle *handle)
+delete_chain6(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain6(delete_chain6, verbose, 0, handle);
@@ -927,8 +889,8 @@
 }
 
 static int
-list_entries(const ip6t_chainlabel chain, int rulenum, int verbose, int numeric,
-	     int expanded, int linenumbers, struct ip6tc_handle *handle)
+list_entries(const xt_chainlabel chain, int rulenum, int verbose, int numeric,
+	     int expanded, int linenumbers, struct xtc_handle *handle)
 {
 	int found = 0;
 	unsigned int format;
@@ -1033,14 +995,15 @@
 	}
 }
 
-static int print_match_save(const struct ip6t_entry_match *e,
+static int print_match_save(const struct xt_entry_match *e,
 			const struct ip6t_ip6 *ip)
 {
 	const struct xtables_match *match =
 		xtables_find_match(e->u.user.name, XTF_TRY_LOAD, NULL);
 
 	if (match) {
-		printf(" -m %s", e->u.user.name);
+		printf(" -m %s",
+			match->alias ? match->alias(e) : e->u.user.name);
 
 		/* some matches don't provide a save function */
 		if (match->save)
@@ -1061,7 +1024,7 @@
 		     const struct in6_addr *mask, int invert)
 {
 	char buf[51];
-	int l = ipv6_prefix_length(mask);
+	int l = xtables_ip6mask_to_cidr(mask);
 
 	if (l == 0 && !invert)
 		return;
@@ -1080,9 +1043,9 @@
 /* We want this to be readable, so only print out neccessary fields.
  * Because that's the kind of world I want to live in.  */
 void print_rule6(const struct ip6t_entry *e,
-		       struct ip6tc_handle *h, const char *chain, int counters)
+		       struct xtc_handle *h, const char *chain, int counters)
 {
-	const struct ip6t_entry_target *t;
+	const struct xt_entry_target *t;
 	const char *target_name;
 
 	/* print counters for iptables-save */
@@ -1105,7 +1068,7 @@
 	print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
 		    e->ipv6.invflags & IP6T_INV_VIA_OUT);
 
-	print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
+	print_proto(e->ipv6.proto, e->ipv6.invflags & XT_INV_PROTO);
 
 #if 0
 	/* not definied in ipv6
@@ -1129,16 +1092,8 @@
 	if (counters < 0)
 		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
 
-	/* Print target name */
+	/* Print target name and targinfo part */
 	target_name = ip6tc_get_target(e, h);
-	if (target_name && (*target_name != '\0'))
-#ifdef IP6T_F_GOTO
-		printf(" -%c %s", e->ipv6.flags & IP6T_F_GOTO ? 'g' : 'j', target_name);
-#else
-		printf(" -j %s", target_name);
-#endif
-
-	/* Print targinfo part */
 	t = ip6t_get_target((struct ip6t_entry *)e);
 	if (t->u.user.name[0]) {
 		struct xtables_target *target =
@@ -1150,27 +1105,34 @@
 			exit(1);
 		}
 
+		printf(" -j %s", target->alias ? target->alias(t) : target_name);
 		if (target->save)
 			target->save(&e->ipv6, t);
 		else {
-			/* If the target size is greater than ip6t_entry_target
+			/* If the target size is greater than xt_entry_target
 			 * there is something to be saved, we just don't know
 			 * how to print it */
 			if (t->u.target_size !=
-			    sizeof(struct ip6t_entry_target)) {
+			    sizeof(struct xt_entry_target)) {
 				fprintf(stderr, "Target `%s' is missing "
 						"save function\n",
 					t->u.user.name);
 				exit(1);
 			}
 		}
-	}
+	} else if (target_name && (*target_name != '\0'))
+#ifdef IP6T_F_GOTO
+		printf(" -%c %s", e->ipv6.flags & IP6T_F_GOTO ? 'g' : 'j', target_name);
+#else
+		printf(" -j %s", target_name);
+#endif
+
 	printf("\n");
 }
 
 static int
-list_rules(const ip6t_chainlabel chain, int rulenum, int counters,
-	     struct ip6tc_handle *handle)
+list_rules(const xt_chainlabel chain, int rulenum, int counters,
+	     struct xtc_handle *handle)
 {
 	const char *this = NULL;
 	int found = 0;
@@ -1187,7 +1149,7 @@
 			continue;
 
 		if (ip6tc_builtin(this, handle)) {
-			struct ip6t_counters count;
+			struct xt_counters count;
 			printf("-P %s %s", this, ip6tc_get_policy(this, &count, handle));
 			if (counters)
 			    printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
@@ -1224,7 +1186,7 @@
 static struct ip6t_entry *
 generate_entry(const struct ip6t_entry *fw,
 	       struct xtables_rule_match *matches,
-	       struct ip6t_entry_target *target)
+	       struct xt_entry_target *target)
 {
 	unsigned int size;
 	struct xtables_rule_match *matchp;
@@ -1249,27 +1211,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;
@@ -1282,14 +1223,22 @@
 	if (cs->target == NULL)
 		return;
 
-	size = XT_ALIGN(sizeof(struct ip6t_entry_target)) + cs->target->size;
+	size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
 
 	cs->target->t = xtables_calloc(1, size);
 	cs->target->t->u.target_size = size;
-	strcpy(cs->target->t->u.user.name, cs->jumpto);
+	if (cs->target->real_name == NULL) {
+		strcpy(cs->target->t->u.user.name, cs->jumpto);
+	} else {
+		strcpy(cs->target->t->u.user.name, cs->target->real_name);
+		if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
+			fprintf(stderr, "Notice: The %s target is converted into %s target "
+			        "in rule listing and saving.\n",
+			        cs->jumpto, cs->target->real_name);
+	}
 	cs->target->t->u.user.revision = cs->target->revision;
-	if (cs->target->init != NULL)
-		cs->target->init(cs->target->t);
+
+	xs_init_target(cs->target);
 	if (cs->target->x6_options != NULL)
 		opts = xtables_options_xfrm(ip6tables_globals.orig_opts, opts,
 					    cs->target->x6_options,
@@ -1312,13 +1261,20 @@
 			   "unexpected ! flag before --match");
 
 	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
-	size = XT_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
 	m->m = xtables_calloc(1, size);
 	m->m->u.match_size = size;
-	strcpy(m->m->u.user.name, m->name);
+	if (m->real_name == NULL) {
+		strcpy(m->m->u.user.name, m->name);
+	} else {
+		strcpy(m->m->u.user.name, m->real_name);
+		if (!(m->ext_flags & XTABLES_EXT_ALIAS))
+			fprintf(stderr, "Notice: The %s match is converted into %s match "
+			        "in rule listing and saving.\n", m->name, m->real_name);
+	}
 	m->m->u.user.revision = m->revision;
-	if (m->init != NULL)
-		m->init(m->m);
+
+	xs_init_match(m);
 	if (m == m->next)
 		return;
 	/* Merge options for non-cloned matches */
@@ -1330,7 +1286,8 @@
 					     m->extra_opts, &m->option_offset);
 }
 
-int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **handle)
+int do_command6(int argc, char *argv[], char **table,
+		struct xtc_handle **handle, bool restore)
 {
 	struct iptables_command_state cs;
 	struct ip6t_entry *e = NULL;
@@ -1339,6 +1296,7 @@
 	struct in6_addr *smasks = NULL, *dmasks = NULL;
 
 	int verbose = 0;
+	bool wait = false;
 	const char *chain = NULL;
 	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
 	const char *policy = NULL, *newname = NULL;
@@ -1374,7 +1332,7 @@
 
 	opts = xt_params->orig_opts;
 	while ((cs.c = getopt_long(argc, argv,
-	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:g:46",
+	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvwnt:m:xc:g:46",
 					   opts, NULL)) != -1) {
 		switch (cs.c) {
 			/*
@@ -1538,7 +1496,6 @@
 			 * Option selection
 			 */
 		case 'p':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_PROTOCOL, &cs.fw6.ipv6.invflags,
 				   cs.invert);
 
@@ -1551,12 +1508,12 @@
 			cs.fw6.ipv6.flags |= IP6T_F_PROTO;
 
 			if (cs.fw6.ipv6.proto == 0
-			    && (cs.fw6.ipv6.invflags & IP6T_INV_PROTO))
+			    && (cs.fw6.ipv6.invflags & XT_INV_PROTO))
 				xtables_error(PARAMETER_PROBLEM,
 					   "rule would never match protocol");
 
 			if (is_exthdr(cs.fw6.ipv6.proto)
-			    && (cs.fw6.ipv6.invflags & IP6T_INV_PROTO) == 0)
+			    && (cs.fw6.ipv6.invflags & XT_INV_PROTO) == 0)
 				fprintf(stderr,
 					"Warning: never matched protocol: %s. "
 					"use extension match instead.\n",
@@ -1564,14 +1521,12 @@
 			break;
 
 		case 's':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_SOURCE, &cs.fw6.ipv6.invflags,
 				   cs.invert);
 			shostnetworkmask = optarg;
 			break;
 
 		case 'd':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_DESTINATION, &cs.fw6.ipv6.invflags,
 				   cs.invert);
 			dhostnetworkmask = optarg;
@@ -1596,7 +1551,6 @@
 				xtables_error(PARAMETER_PROBLEM,
 					"Empty interface is likely to be "
 					"undesired");
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_VIANAMEIN, &cs.fw6.ipv6.invflags,
 				   cs.invert);
 			xtables_parse_interface(optarg,
@@ -1609,7 +1563,6 @@
 				xtables_error(PARAMETER_PROBLEM,
 					"Empty interface is likely to be "
 					"undesired");
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw6.ipv6.invflags,
 				   cs.invert);
 			xtables_parse_interface(optarg,
@@ -1624,6 +1577,15 @@
 			verbose++;
 			break;
 
+		case 'w':
+			if (restore) {
+				xtables_error(PARAMETER_PROBLEM,
+					      "You cannot use `-w' from "
+					      "ip6tables-restore");
+			}
+			wait = true;
+			break;
+
 		case 'm':
 			command_match(&cs);
 			break;
@@ -1775,6 +1737,14 @@
 			   "chain name `%s' too long (must be under %u chars)",
 			   chain, XT_EXTENSION_MAXNAMELEN);
 
+	/* Attempt to acquire the xtables lock */
+	if (!restore && !xtables_lock(wait)) {
+		fprintf(stderr, "Another app is currently holding the xtables lock. "
+			"Perhaps you want to use the -w option?\n");
+		xtables_free_opts(1);
+		exit(RESOURCE_PROBLEM);
+	}
+
 	/* only allocate handle if we weren't called with a handle */
 	if (!*handle)
 		*handle = ip6tc_init(*table);
@@ -1831,16 +1801,15 @@
 			|| ip6tc_is_chain(cs.jumpto, *handle))) {
 			size_t size;
 
-			cs.target = xtables_find_target(IP6T_STANDARD_TARGET,
+			cs.target = xtables_find_target(XT_STANDARD_TARGET,
 					XTF_LOAD_MUST_SUCCEED);
 
-			size = sizeof(struct ip6t_entry_target)
+			size = sizeof(struct xt_entry_target)
 				+ cs.target->size;
 			cs.target->t = xtables_calloc(1, size);
 			cs.target->t->u.target_size = size;
 			strcpy(cs.target->t->u.user.name, cs.jumpto);
-			if (cs.target->init != NULL)
-				cs.target->init(cs.target->t);
+			xs_init_target(cs.target);
 		}
 
 		if (!cs.target) {
@@ -1956,7 +1925,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-apply b/iptables/iptables-apply
index 5fec76b..86b8d5a 100755
--- a/iptables/iptables-apply
+++ b/iptables/iptables-apply
@@ -11,7 +11,6 @@
 VERSION=1.0
 
 TIMEOUT=10
-DEFAULT_FILE=/etc/network/iptables
 
 function blurb()
 {
@@ -87,6 +86,19 @@
 	shift
 done
 
+case "$PROGNAME" in
+	(*6*)
+		SAVE=ip6tables-save
+		RESTORE=ip6tables-restore
+		DEFAULT_FILE=/etc/network/ip6tables
+		;;
+	(*)
+		SAVE=iptables-save
+		RESTORE=iptables-restore
+		DEFAULT_FILE=/etc/network/iptables
+		;;
+esac
+
 FILE="${1:-$DEFAULT_FILE}";
 
 if [[ -z "$FILE" ]]; then
@@ -99,17 +111,6 @@
 	exit 2
 fi
 
-case "${0##*/}" in
-	(*6*)
-		SAVE=ip6tables-save
-		RESTORE=ip6tables-restore
-		;;
-	(*)
-		SAVE=iptables-save
-		RESTORE=iptables-restore
-		;;
-esac
-
 COMMANDS=(tempfile "$SAVE" "$RESTORE")
 
 for cmd in "${COMMANDS[@]}"; do
diff --git a/iptables/iptables-apply.8 b/iptables/iptables-apply.8
index 8208fd0..66eaf57 100644
--- a/iptables/iptables-apply.8
+++ b/iptables/iptables-apply.8
@@ -18,7 +18,7 @@
 case, the script rolls back to the previous ruleset after the timeout
 expired. The timeout can be set with \fB\-t\fP.
 .PP
-When called as ip6tables\-apply, the script will use
+When called as \fBip6tables\-apply\fP, the script will use
 ip6tables\-save/\-restore instead.
 .SH OPTIONS
 .TP
diff --git a/iptables/iptables-extensions.8.tmpl.in b/iptables/iptables-extensions.8.tmpl.in
new file mode 100644
index 0000000..99d89a1
--- /dev/null
+++ b/iptables/iptables-extensions.8.tmpl.in
@@ -0,0 +1,28 @@
+.TH iptables-extensions 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.SH NAME
+iptables-extensions \(em list of extensions in the standard iptables distribution
+.SH SYNOPSIS
+\fBip6tables\fP [\fB\-m\fP \fIname\fP [\fImodule-options\fP...]]
+[\fB\-j\fP \fItarget-name\fP [\fItarget-options\fP...]
+.PP
+\fBiptables\fP [\fB\-m\fP \fIname\fP [\fImodule-options\fP...]]
+[\fB\-j\fP \fItarget-name\fP [\fItarget-options\fP...]
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules
+with the \fB\-m\fP or \fB\-\-match\fP
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module.  You can specify multiple extended match modules in one line,
+and you can use the \fB\-h\fP or \fB\-\-help\fP
+options after the module has been specified to receive help specific
+to that module.  The extended match modules are evaluated in the order
+they are specified in the rule.
+.PP
+If the \fB\-p\fP or \fB\-\-protocol\fP was specified and if and only if an
+unknown option is encountered, iptables will try load a match module of the
+same name as the protocol, to try making the option available.
+.\" @MATCH@
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.\" @TARGET@
diff --git a/iptables/iptables-restore.8 b/iptables/iptables-restore.8
index a52bceb..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]
+\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,26 @@
 \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.
 .SH BUGS
 None known as of iptables-1.2.1 release
 .SH AUTHOR
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
index 2624599..8c942ff 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -56,9 +56,9 @@
 	exit(1);
 }
 
-static struct iptc_handle *create_handle(const char *tablename)
+static struct xtc_handle *create_handle(const char *tablename)
 {
-	struct iptc_handle *handle;
+	struct xtc_handle *handle;
 
 	handle = iptc_init(tablename);
 
@@ -76,7 +76,7 @@
 	return handle;
 }
 
-static int parse_counters(char *string, struct ipt_counters *ctr)
+static int parse_counters(char *string, struct xt_counters *ctr)
 {
 	unsigned long long pcnt, bcnt;
 	int ret;
@@ -97,7 +97,7 @@
 	DEBUGP("add_argv: %s\n", what);
 	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
 		newargv[newargc] = strdup(what);
-		newargc++;
+		newargv[++newargc] = NULL;
 		return 1;
 	} else {
 		xtables_error(PARAMETER_PROBLEM,
@@ -113,21 +113,81 @@
 		free(newargv[i]);
 }
 
-#ifdef IPTABLES_MULTI
+static void add_param_to_argv(char *parsestart)
+{
+	int quote_open = 0, escaped = 0, param_len = 0;
+	char param_buffer[1024], *curchar;
+
+	/* After fighting with strtok enough, here's now
+	 * a 'real' parser. According to Rusty I'm now no
+	 * longer a real hacker, but I can live with that */
+
+	for (curchar = parsestart; *curchar; curchar++) {
+		if (quote_open) {
+			if (escaped) {
+				param_buffer[param_len++] = *curchar;
+				escaped = 0;
+				continue;
+			} else if (*curchar == '\\') {
+				escaped = 1;
+				continue;
+			} else if (*curchar == '"') {
+				quote_open = 0;
+				*curchar = ' ';
+			} else {
+				param_buffer[param_len++] = *curchar;
+				continue;
+			}
+		} else {
+			if (*curchar == '"') {
+				quote_open = 1;
+				continue;
+			}
+		}
+
+		if (*curchar == ' '
+		    || *curchar == '\t'
+		    || * curchar == '\n') {
+			if (!param_len) {
+				/* two spaces? */
+				continue;
+			}
+
+			param_buffer[param_len] = '\0';
+
+			/* check if table name specified */
+			if (!strncmp(param_buffer, "-t", 2)
+			    || !strncmp(param_buffer, "--table", 8)) {
+				xtables_error(PARAMETER_PROBLEM,
+				"The -t option (seen in line %u) cannot be "
+				"used in iptables-restore.\n", line);
+				exit(1);
+			}
+
+			add_argv(param_buffer);
+			param_len = 0;
+		} else {
+			/* regular character, copy to buffer */
+			param_buffer[param_len++] = *curchar;
+
+			if (param_len >= sizeof(param_buffer))
+				xtables_error(PARAMETER_PROBLEM,
+				   "Parameter too long!");
+		}
+	}
+}
+
 int
 iptables_restore_main(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
 {
-	struct iptc_handle *handle = NULL;
+	struct xtc_handle *handle = NULL;
 	char buffer[10240];
 	int c;
-	char curtable[IPT_TABLE_MAXNAMELEN + 1];
+	char curtable[XT_TABLE_MAXNAMELEN + 1];
 	FILE *in;
 	int in_table = 0, testing = 0;
 	const char *tablename = NULL;
+	const struct xtc_ops *ops = &iptc_ops;
 
 	line = 0;
 
@@ -202,8 +262,8 @@
 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
 			if (!testing) {
 				DEBUGP("Calling commit\n");
-				ret = iptc_commit(handle);
-				iptc_free(handle);
+				ret = ops->commit(handle);
+				ops->free(handle);
 				handle = NULL;
 			} else {
 				DEBUGP("Not calling commit, testing\n");
@@ -219,16 +279,16 @@
 			if (!table) {
 				xtables_error(PARAMETER_PROBLEM,
 					"%s: line %u table name invalid\n",
-					prog_name, line);
+					xt_params->program_name, line);
 				exit(1);
 			}
-			strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
-			curtable[IPT_TABLE_MAXNAMELEN] = '\0';
+			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
+			curtable[XT_TABLE_MAXNAMELEN] = '\0';
 
 			if (tablename && (strcmp(tablename, table) != 0))
 				continue;
 			if (handle)
-				iptc_free(handle);
+				ops->free(handle);
 
 			handle = create_handle(table);
 			if (noflush == 0) {
@@ -255,7 +315,7 @@
 			if (!chain) {
 				xtables_error(PARAMETER_PROBLEM,
 					   "%s: line %u chain name invalid\n",
-					   prog_name, line);
+					   xt_params->program_name, line);
 				exit(1);
 			}
 
@@ -265,17 +325,17 @@
 					   "(%u chars max)",
 					   chain, XT_EXTENSION_MAXNAMELEN - 1);
 
-			if (iptc_builtin(chain, handle) <= 0) {
-				if (noflush && iptc_is_chain(chain, handle)) {
+			if (ops->builtin(chain, handle) <= 0) {
+				if (noflush && ops->is_chain(chain, handle)) {
 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
-					if (!iptc_flush_entries(chain, handle))
+					if (!ops->flush_entries(chain, handle))
 						xtables_error(PARAMETER_PROBLEM,
 							   "error flushing chain "
 							   "'%s':%s\n", chain,
 							   strerror(errno));
 				} else {
 					DEBUGP("Creating new chain '%s'\n", chain);
-					if (!iptc_create_chain(chain, handle))
+					if (!ops->create_chain(chain, handle))
 						xtables_error(PARAMETER_PROBLEM,
 							   "error creating chain "
 							   "'%s':%s\n", chain,
@@ -288,12 +348,12 @@
 			if (!policy) {
 				xtables_error(PARAMETER_PROBLEM,
 					   "%s: line %u policy invalid\n",
-					   prog_name, line);
+					   xt_params->program_name, line);
 				exit(1);
 			}
 
 			if (strcmp(policy, "-") != 0) {
-				struct ipt_counters count;
+				struct xt_counters count;
 
 				if (counters) {
 					char *ctrs;
@@ -305,20 +365,19 @@
 							   "for chain '%s'\n", chain);
 
 				} else {
-					memset(&count, 0,
-					       sizeof(struct ipt_counters));
+					memset(&count, 0, sizeof(count));
 				}
 
 				DEBUGP("Setting policy of chain %s to %s\n",
 					chain, policy);
 
-				if (!iptc_set_policy(chain, policy, &count,
+				if (!ops->set_policy(chain, policy, &count,
 						     handle))
 					xtables_error(OTHER_PROBLEM,
 						"Can't set policy `%s'"
 						" on `%s' line %u: %s\n",
 						policy, chain, line,
-						iptc_strerror(errno));
+						ops->strerror(errno));
 			}
 
 			ret = 1;
@@ -330,11 +389,6 @@
 			char *bcnt = NULL;
 			char *parsestart;
 
-			/* the parser */
-			char *curchar;
-			int quote_open, escaped;
-			size_t param_len;
-
 			/* reset the newargv */
 			newargc = 0;
 
@@ -375,69 +429,7 @@
 				add_argv((char *) bcnt);
 			}
 
-			/* After fighting with strtok enough, here's now
-			 * a 'real' parser. According to Rusty I'm now no
-			 * longer a real hacker, but I can live with that */
-
-			quote_open = 0;
-			escaped = 0;
-			param_len = 0;
-
-			for (curchar = parsestart; *curchar; curchar++) {
-				char param_buffer[1024];
-
-				if (quote_open) {
-					if (escaped) {
-						param_buffer[param_len++] = *curchar;
-						escaped = 0;
-						continue;
-					} else if (*curchar == '\\') {
-						escaped = 1;
-						continue;
-					} else if (*curchar == '"') {
-						quote_open = 0;
-						*curchar = ' ';
-					} else {
-						param_buffer[param_len++] = *curchar;
-						continue;
-					}
-				} else {
-					if (*curchar == '"') {
-						quote_open = 1;
-						continue;
-					}
-				}
-
-				if (*curchar == ' '
-				    || *curchar == '\t'
-				    || * curchar == '\n') {
-					if (!param_len) {
-						/* two spaces? */
-						continue;
-					}
-
-					param_buffer[param_len] = '\0';
-
-					/* check if table name specified */
-					if (!strncmp(param_buffer, "-t", 2)
-					    || !strncmp(param_buffer, "--table", 8)) {
-						xtables_error(PARAMETER_PROBLEM,
-						   "Line %u seems to have a "
-						   "-t table option.\n", line);
-						exit(1);
-					}
-
-					add_argv(param_buffer);
-					param_len = 0;
-				} else {
-					/* regular character, copy to buffer */
-					param_buffer[param_len++] = *curchar;
-
-					if (param_len >= sizeof(param_buffer))
-						xtables_error(PARAMETER_PROBLEM,
-						   "Parameter too long!");
-				}
-			}
+			add_param_to_argv(parsestart);
 
 			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
 				newargc, curtable);
@@ -446,7 +438,7 @@
 				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
 
 			ret = do_command4(newargc, newargv,
-					 &newargv[2], &handle);
+					 &newargv[2], &handle, true);
 
 			free_argv();
 			fflush(stdout);
@@ -455,17 +447,16 @@
 			continue;
 		if (!ret) {
 			fprintf(stderr, "%s: line %u failed\n",
-					prog_name, line);
+					xt_params->program_name, line);
 			exit(1);
 		}
 	}
 	if (in_table) {
 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
-				prog_name, line + 1);
+				xt_params->program_name, line + 1);
 		exit(1);
 	}
 
-	if (in != NULL)
-		fclose(in);
+	fclose(in);
 	return 0;
 }
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
index 7542bdc..e599fce 100644
--- a/iptables/iptables-save.c
+++ b/iptables/iptables-save.c
@@ -21,10 +21,9 @@
 #include <dlfcn.h>
 #endif
 
-static int show_binary = 0, show_counters = 0;
+static int show_counters = 0;
 
 static const struct option options[] = {
-	{.name = "binary",   .has_arg = false, .val = 'b'},
 	{.name = "counters", .has_arg = false, .val = 'c'},
 	{.name = "dump",     .has_arg = false, .val = 'd'},
 	{.name = "table",    .has_arg = true,  .val = 't'},
@@ -37,7 +36,7 @@
 {
 	int ret = 1;
 	FILE *procfile = NULL;
-	char tablename[IPT_TABLE_MAXNAMELEN+1];
+	char tablename[XT_TABLE_MAXNAMELEN+1];
 
 	procfile = fopen("/proc/net/ip_tables_names", "re");
 	if (!procfile)
@@ -59,7 +58,7 @@
 
 static int do_output(const char *tablename)
 {
-	struct iptc_handle *h;
+	struct xtc_handle *h;
 	const char *chain = NULL;
 
 	if (!tablename)
@@ -74,52 +73,45 @@
 		xtables_error(OTHER_PROBLEM, "Cannot initialize: %s\n",
 			   iptc_strerror(errno));
 
-	if (!show_binary) {
-		time_t now = time(NULL);
+	time_t now = time(NULL);
 
-		printf("# Generated by iptables-save v%s on %s",
-		       IPTABLES_VERSION, ctime(&now));
-		printf("*%s\n", tablename);
+	printf("# Generated by iptables-save v%s on %s",
+	       IPTABLES_VERSION, ctime(&now));
+	printf("*%s\n", tablename);
 
-		/* Dump out chain names first,
-		 * thereby preventing dependency conflicts */
-		for (chain = iptc_first_chain(h);
-		     chain;
-		     chain = iptc_next_chain(h)) {
+	/* Dump out chain names first,
+	 * thereby preventing dependency conflicts */
+	for (chain = iptc_first_chain(h);
+	     chain;
+	     chain = iptc_next_chain(h)) {
 
-			printf(":%s ", chain);
-			if (iptc_builtin(chain, h)) {
-				struct ipt_counters count;
-				printf("%s ",
-				       iptc_get_policy(chain, &count, h));
-				printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
-			} else {
-				printf("- [0:0]\n");
-			}
+		printf(":%s ", chain);
+		if (iptc_builtin(chain, h)) {
+			struct xt_counters count;
+			printf("%s ",
+			       iptc_get_policy(chain, &count, h));
+			printf("[%llu:%llu]\n", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+		} else {
+			printf("- [0:0]\n");
 		}
-
-
-		for (chain = iptc_first_chain(h);
-		     chain;
-		     chain = iptc_next_chain(h)) {
-			const struct ipt_entry *e;
-
-			/* Dump out rules */
-			e = iptc_first_rule(chain, h);
-			while(e) {
-				print_rule4(e, h, chain, show_counters);
-				e = iptc_next_rule(e, h);
-			}
-		}
-
-		now = time(NULL);
-		printf("COMMIT\n");
-		printf("# Completed on %s", ctime(&now));
-	} else {
-		/* Binary, huh?  OK. */
-		xtables_error(OTHER_PROBLEM, "Binary NYI\n");
 	}
 
+	for (chain = iptc_first_chain(h);
+	     chain;
+	     chain = iptc_next_chain(h)) {
+		const struct ipt_entry *e;
+
+		/* Dump out rules */
+		e = iptc_first_rule(chain, h);
+		while(e) {
+			print_rule4(e, h, chain, show_counters);
+			e = iptc_next_rule(e, h);
+		}
+	}
+
+	now = time(NULL);
+	printf("COMMIT\n");
+	printf("# Completed on %s", ctime(&now));
 	iptc_free(h);
 
 	return 1;
@@ -129,13 +121,8 @@
  * :Chain name POLICY packets bytes
  * rule
  */
-#ifdef IPTABLES_MULTI
 int
 iptables_save_main(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
 {
 	const char *tablename = NULL;
 	int c;
@@ -155,10 +142,6 @@
 
 	while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
 		switch (c) {
-		case 'b':
-			show_binary = 1;
-			break;
-
 		case 'c':
 			show_counters = 1;
 			break;
diff --git a/iptables/iptables-standalone.c b/iptables/iptables-standalone.c
index 6cf8483..c60b4b7 100644
--- a/iptables/iptables-standalone.c
+++ b/iptables/iptables-standalone.c
@@ -39,17 +39,12 @@
 #include <iptables.h>
 #include "iptables-multi.h"
 
-#ifdef IPTABLES_MULTI
 int
 iptables_main(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
 {
 	int ret;
 	char *table = "filter";
-	struct iptc_handle *handle = NULL;
+	struct xtc_handle *handle = NULL;
 
 	signal(SIGPIPE, SIG_IGN);
 
@@ -66,7 +61,7 @@
 	init_extensions4();
 #endif
 
-	ret = do_command4(argc, argv, &table, &handle);
+	ret = do_command4(argc, argv, &table, &handle, false);
 	if (ret) {
 		ret = iptc_commit(handle);
 		iptc_free(handle);
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
index 5aa638c..9628447 100644
--- a/iptables/iptables-xml.c
+++ b/iptables/iptables-xml.c
@@ -23,10 +23,6 @@
 #define DEBUGP(x, args...)
 #endif
 
-#ifndef IPTABLES_MULTI
-int line = 0;
-#endif
-
 struct xtables_globals iptables_xml_globals = {
 	.option_offset = 0,
 	.program_version = IPTABLES_VERSION,
@@ -60,7 +56,7 @@
 }
 
 static int
-parse_counters(char *string, struct ipt_counters *ctr)
+parse_counters(char *string, struct xt_counters *ctr)
 {
 	__u64 *pcnt, *bcnt;
 
@@ -85,16 +81,16 @@
 /* arg meta data, were they quoted, frinstance */
 static int newargvattr[255];
 
-#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
-static char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
-static char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
-static char curTable[IPT_TABLE_MAXNAMELEN + 1];
-static char curChain[IPT_CHAIN_MAXNAMELEN + 1];
+#define XT_CHAIN_MAXNAMELEN XT_TABLE_MAXNAMELEN
+static char closeActionTag[XT_TABLE_MAXNAMELEN + 1];
+static char closeRuleTag[XT_TABLE_MAXNAMELEN + 1];
+static char curTable[XT_TABLE_MAXNAMELEN + 1];
+static char curChain[XT_CHAIN_MAXNAMELEN + 1];
 
 struct chain {
 	char *chain;
 	char *policy;
-	struct ipt_counters count;
+	struct xt_counters count;
 	int created;
 };
 
@@ -237,12 +233,12 @@
 }
 
 static void
-openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
+openChain(char *chain, char *policy, struct xt_counters *ctr, char close)
 {
 	closeChain();
 
-	strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
-	curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
+	strncpy(curChain, chain, XT_CHAIN_MAXNAMELEN);
+	curChain[XT_CHAIN_MAXNAMELEN] = '\0';
 
 	printf("    <chain ");
 	xmlAttrS("name", curChain);
@@ -291,7 +287,7 @@
 }
 
 static void
-saveChain(char *chain, char *policy, struct ipt_counters *ctr)
+saveChain(char *chain, char *policy, struct xt_counters *ctr)
 {
 	if (nextChain >= maxChains) {
 		xtables_error(PARAMETER_PROBLEM,
@@ -336,8 +332,8 @@
 {
 	closeTable();
 
-	strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
-	curTable[IPT_TABLE_MAXNAMELEN] = '\0';
+	strncpy(curTable, table, XT_TABLE_MAXNAMELEN);
+	curTable[XT_TABLE_MAXNAMELEN] = '\0';
 
 	printf("  <table ");
 	xmlAttrS("name", curTable);
@@ -371,7 +367,8 @@
 do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
 	     char *argv[], int argvattr[])
 {
-	int arg = 1;		// ignore leading -A
+	int i;
+	int arg = 2;		// ignore leading -A <chain>
 	char invert_next = 0;
 	char *spacer = "";	// space when needed to assemble arguments
 	char *level1 = NULL;
@@ -403,11 +400,17 @@
 			arg++;
 	}
 
-	/* Before we start, if the first arg is -[^-] and not -m or -j or -g 
-	   then start a dummy <match> tag for old style built-in matches.  
-	   We would do this in any case, but no need if it would be empty */
-	if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
-	    && strcmp(argv[arg], "-m") != 0) {
+	/* Before we start, if the first arg is -[^-] and not -m or -j or -g
+	 * then start a dummy <match> tag for old style built-in matches.
+	 * We would do this in any case, but no need if it would be empty.
+	 * In the case of negation, we need to look at arg+1
+	 */
+	if (arg < argc && strcmp(argv[arg], "!") == 0)
+		i = arg + 1;
+	else
+		i = arg;
+	if (i < argc && argv[i][0] == '-' && !isTarget(argv[i])
+	    && strcmp(argv[i], "-m") != 0) {
 		OPEN_LEVEL(1, "match");
 		printf(">\n");
 	}
@@ -596,8 +599,8 @@
 			xmlAttrS("byte-count", bcnt);
 		printf(">\n");
 
-		strncpy(closeRuleTag, "      </rule>\n", IPT_TABLE_MAXNAMELEN);
-		closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
+		strncpy(closeRuleTag, "      </rule>\n", XT_TABLE_MAXNAMELEN);
+		closeRuleTag[XT_TABLE_MAXNAMELEN] = '\0';
 
 		/* no point in writing out condition if there isn't one */
 		if (argc >= 3 && !isTarget(argv[2])) {
@@ -611,19 +614,14 @@
 	if (!closeActionTag[0]) {
 		printf("       <actions>\n");
 		strncpy(closeActionTag, "       </actions>\n",
-			IPT_TABLE_MAXNAMELEN);
-		closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
+			XT_TABLE_MAXNAMELEN);
+		closeActionTag[XT_TABLE_MAXNAMELEN] = '\0';
 	}
 	do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
 }
 
-#ifdef IPTABLES_MULTI
 int
 iptables_xml_main(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
 {
 	char buffer[10240];
 	int c;
@@ -703,7 +701,7 @@
 		} else if ((buffer[0] == ':') && (curTable[0])) {
 			/* New chain. */
 			char *policy, *chain;
-			struct ipt_counters count;
+			struct xt_counters count;
 			char *ctrs;
 
 			chain = strtok(buffer + 1, " \t\n");
@@ -742,6 +740,7 @@
 			/* the parser */
 			char *param_start, *curchar;
 			int quote_open, quoted;
+			char param_buffer[1024];
 
 			/* reset the newargv */
 			newargc = 0;
@@ -801,7 +800,6 @@
 				}
 				if (*curchar == ' '
 				    || *curchar == '\t' || *curchar == '\n') {
-					char param_buffer[1024];
 					int param_len = curchar - param_start;
 
 					if (quote_open)
@@ -865,8 +863,7 @@
 		exit(1);
 	}
 
-	if (in != NULL)
-		fclose(in);
+	fclose(in);
 	printf("</iptables-rules>\n");
 	free_argv();
 
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
index d09bf7a..6f31003 100644
--- a/iptables/iptables.8.in
+++ b/iptables/iptables.8.in
@@ -1,4 +1,4 @@
-.TH IPTABLES 8 "" "@PACKAGE_AND_VERSION@" "@PACKAGE_AND_VERSION@"
+.TH IPTABLES 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
 .\"
 .\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
 .\" It is based on ipchains page.
@@ -86,7 +86,7 @@
 is matched, the target specified by the chain policy determines the
 fate of the packet.
 .SH TABLES
-There are currently three independent tables (which tables are present
+There are currently five independent tables (which tables are present
 at any time depends on the kernel configuration options and which
 modules are present).
 .TP
@@ -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
@@ -332,7 +348,15 @@
 1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
 the \fB\-x\fP flag to change this).
 For appending, insertion, deletion and replacement, this causes
-detailed information on the rule or rules to be printed.
+detailed information on the rule or rules to be printed. \fB\-v\fP may be
+specified multiple times to possibly emit more detailed debug statements.
+.TP
+\fB\-w\fP, \fB\-\-wait\fP
+Wait for the xtables lock.
+To prevent multiple instances of the program from running concurrently,
+an attempt will be made to obtain an exclusive lock at launch.  By default,
+the program will exit if the lock cannot be obtained.  This option will
+make the program wait until the exclusive lock can be obtained.
 .TP
 \fB\-n\fP, \fB\-\-numeric\fP
 Numeric output.
@@ -354,24 +378,10 @@
 \fB\-\-modprobe=\fP\fIcommand\fP
 When adding or inserting rules into a chain, use \fIcommand\fP
 to load any necessary modules (targets, match extensions, etc).
-.SH MATCH EXTENSIONS
-iptables can use extended packet matching modules.  These are loaded
-in two ways: implicitly, when \fB\-p\fP or \fB\-\-protocol\fP
-is specified, or with the \fB\-m\fP or \fB\-\-match\fP
-options, followed by the matching module name; after these, various
-extra command line options become available, depending on the specific
-module.  You can specify multiple extended match modules in one line,
-and you can use the \fB\-h\fP or \fB\-\-help\fP
-options after the module has been specified to receive help specific
-to that module.
+.SH MATCH AND TARGET EXTENSIONS
 .PP
-The following are included in the base package, and most of these can
-be preceded by a "\fB!\fP" to invert the sense of the match.
-.\" @MATCH@
-.SH TARGET EXTENSIONS
-iptables can use extended target modules: the following are included
-in the standard distribution.
-.\" @TARGET@
+iptables can use extended packet matching and target modules.
+A list of these is available in the \fBiptables\-extensions\fP(8) manpage.
 .SH DIAGNOSTICS
 Various error messages are printed to standard error.  The exit code
 is 0 for correct functioning.  Errors which appear to be caused by
@@ -406,8 +416,10 @@
 .fi
 There are several other changes in iptables.
 .SH SEE ALSO
+\fBiptables\-apply\fP(8),
 \fBiptables\-save\fP(8),
 \fBiptables\-restore\fP(8),
+\fBiptables\-extensions\fP(8),
 \fBip6tables\fP(8),
 \fBip6tables\-save\fP(8),
 \fBip6tables\-restore\fP(8),
diff --git a/iptables/iptables.c b/iptables/iptables.c
index 4ae7541..fe18e1c 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -39,7 +39,6 @@
 #include <iptables.h>
 #include <xtables.h>
 #include <fcntl.h>
-#include <sys/utsname.h>
 #include "xshared.h"
 
 #ifndef TRUE
@@ -49,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
@@ -82,7 +66,7 @@
 #define CMD_CHECK		0x4000U
 #define NUMBER_OF_CMD	16
 static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
-				 'Z', 'N', 'X', 'P', 'E', 'S', 'C' };
+				 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
 
 #define OPT_FRAGMENT    0x00800U
 #define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
@@ -115,6 +99,7 @@
 	{.name = "numeric",       .has_arg = 0, .val = 'n'},
 	{.name = "out-interface", .has_arg = 1, .val = 'o'},
 	{.name = "verbose",       .has_arg = 0, .val = 'v'},
+	{.name = "wait",          .has_arg = 0, .val = 'w'},
 	{.name = "exact",         .has_arg = 0, .val = 'x'},
 	{.name = "fragments",     .has_arg = 0, .val = 'f'},
 	{.name = "version",       .has_arg = 0, .val = 'V'},
@@ -172,39 +157,21 @@
 /* -n */ 0,
 /* -s */ IPT_INV_SRCIP,
 /* -d */ IPT_INV_DSTIP,
-/* -p */ IPT_INV_PROTO,
+/* -p */ XT_INV_PROTO,
 /* -j */ 0,
 /* -v */ 0,
 /* -x */ 0,
 /* -i */ IPT_INV_VIA_IN,
 /* -o */ IPT_INV_VIA_OUT,
-/* -f */ IPT_INV_FRAG,
 /*--line*/ 0,
 /* -c */ 0,
+/* -f */ IPT_INV_FRAG,
 };
 
 #define opts iptables_globals.opts
 #define prog_name iptables_globals.program_name
 #define prog_vers iptables_globals.program_version
 
-int kernel_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)
 {
@@ -265,7 +232,7 @@
 "Options:\n"
 "    --ipv4	-4		Nothing (line is ignored by ip6tables-restore)\n"
 "    --ipv6	-6		Error (line is ignored by iptables-restore)\n"
-"[!] --proto	-p proto	protocol: by number or name, eg. `tcp'\n"
+"[!] --protocol	-p proto	protocol: by number or name, eg. `tcp'\n"
 "[!] --source	-s address[/mask][...]\n"
 "				source specification\n"
 "[!] --destination -d address[/mask][...]\n"
@@ -285,6 +252,7 @@
 "				network interface name ([+] for wildcard)\n"
 "  --table	-t table	table to manipulate (default: `filter')\n"
 "  --verbose	-v		verbose mode\n"
+"  --wait	-w		wait for the xtables lock\n"
 "  --line-numbers		print line numbers when listing\n"
 "  --exact	-x		expand numbers (display exact values)\n"
 "[!] --fragment	-f		match second or further fragments only\n"
@@ -448,44 +416,18 @@
 }
 
 static void
-print_num(uint64_t number, unsigned int format)
+print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
 {
-	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 iptc_handle *handle)
-{
-	struct ipt_counters counters;
+	struct xt_counters counters;
 	const char *pol = iptc_get_policy(chain, &counters, handle);
 	printf("Chain %s", chain);
 	if (pol) {
 		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");
@@ -524,7 +466,7 @@
 
 
 static int
-print_match(const struct ipt_entry_match *m,
+print_match(const struct xt_entry_match *m,
 	    const struct ipt_ip *ip,
 	    int numeric)
 {
@@ -550,17 +492,17 @@
 	       const char *targname,
 	       unsigned int num,
 	       unsigned int format,
-	       struct iptc_handle *const handle)
+	       struct xtc_handle *const handle)
 {
 	const struct xtables_target *target = NULL;
-	const struct ipt_entry_target *t;
+	const struct xt_entry_target *t;
 	uint8_t flags;
 	char buf[BUFSIZ];
 
 	if (!iptc_is_chain(targname, handle))
 		target = xtables_find_target(targname, XTF_TRY_LOAD);
 	else
-		target = xtables_find_target(IPT_STANDARD_TARGET,
+		target = xtables_find_target(XT_STANDARD_TARGET,
 		         XTF_LOAD_MUST_SUCCEED);
 
 	t = ipt_get_target((struct ipt_entry *)fw);
@@ -570,14 +512,14 @@
 		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))
 		printf(FMT("%-9s ", "%s "), targname);
 
-	fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+	fputc(fw->ip.invflags & XT_INV_PROTO ? '!' : ' ', stdout);
 	{
 		const char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
 		if (pname)
@@ -672,16 +614,16 @@
 
 static void
 print_firewall_line(const struct ipt_entry *fw,
-		    struct iptc_handle *const h)
+		    struct xtc_handle *const h)
 {
-	struct ipt_entry_target *t;
+	struct xt_entry_target *t;
 
 	t = ipt_get_target((struct ipt_entry *)fw);
 	print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
 }
 
 static int
-append_entry(const ipt_chainlabel chain,
+append_entry(const xt_chainlabel chain,
 	     struct ipt_entry *fw,
 	     unsigned int nsaddrs,
 	     const struct in_addr saddrs[],
@@ -690,7 +632,7 @@
 	     const struct in_addr daddrs[],
 	     const struct in_addr dmasks[],
 	     int verbose,
-	     struct iptc_handle *handle)
+	     struct xtc_handle *handle)
 {
 	unsigned int i, j;
 	int ret = 1;
@@ -711,13 +653,13 @@
 }
 
 static int
-replace_entry(const ipt_chainlabel chain,
+replace_entry(const xt_chainlabel chain,
 	      struct ipt_entry *fw,
 	      unsigned int rulenum,
 	      const struct in_addr *saddr, const struct in_addr *smask,
 	      const struct in_addr *daddr, const struct in_addr *dmask,
 	      int verbose,
-	      struct iptc_handle *handle)
+	      struct xtc_handle *handle)
 {
 	fw->ip.src.s_addr = saddr->s_addr;
 	fw->ip.dst.s_addr = daddr->s_addr;
@@ -730,7 +672,7 @@
 }
 
 static int
-insert_entry(const ipt_chainlabel chain,
+insert_entry(const xt_chainlabel chain,
 	     struct ipt_entry *fw,
 	     unsigned int rulenum,
 	     unsigned int nsaddrs,
@@ -740,7 +682,7 @@
 	     const struct in_addr daddrs[],
 	     const struct in_addr dmasks[],
 	     int verbose,
-	     struct iptc_handle *handle)
+	     struct xtc_handle *handle)
 {
 	unsigned int i, j;
 	int ret = 1;
@@ -771,10 +713,10 @@
 
 	size = sizeof(struct ipt_entry);
 	for (matchp = matches; matchp; matchp = matchp->next)
-		size += XT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
 
 	mask = xtables_calloc(1, size
-			 + XT_ALIGN(sizeof(struct ipt_entry_target))
+			 + XT_ALIGN(sizeof(struct xt_entry_target))
 			 + target->size);
 
 	memset(mask, 0xFF, sizeof(struct ipt_entry));
@@ -782,20 +724,20 @@
 
 	for (matchp = matches; matchp; matchp = matchp->next) {
 		memset(mptr, 0xFF,
-		       XT_ALIGN(sizeof(struct ipt_entry_match))
+		       XT_ALIGN(sizeof(struct xt_entry_match))
 		       + matchp->match->userspacesize);
-		mptr += XT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size;
+		mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
 	}
 
 	memset(mptr, 0xFF,
-	       XT_ALIGN(sizeof(struct ipt_entry_target))
+	       XT_ALIGN(sizeof(struct xt_entry_target))
 	       + target->userspacesize);
 
 	return mask;
 }
 
 static int
-delete_entry(const ipt_chainlabel chain,
+delete_entry(const xt_chainlabel chain,
 	     struct ipt_entry *fw,
 	     unsigned int nsaddrs,
 	     const struct in_addr saddrs[],
@@ -804,7 +746,7 @@
 	     const struct in_addr daddrs[],
 	     const struct in_addr dmasks[],
 	     int verbose,
-	     struct iptc_handle *handle,
+	     struct xtc_handle *handle,
 	     struct xtables_rule_match *matches,
 	     const struct xtables_target *target)
 {
@@ -830,11 +772,11 @@
 }
 
 static int
-check_entry(const ipt_chainlabel chain, struct ipt_entry *fw,
+check_entry(const xt_chainlabel chain, struct ipt_entry *fw,
 	    unsigned int nsaddrs, const struct in_addr *saddrs,
 	    const struct in_addr *smasks, unsigned int ndaddrs,
 	    const struct in_addr *daddrs, const struct in_addr *dmasks,
-	    bool verbose, struct iptc_handle *handle,
+	    bool verbose, struct xtc_handle *handle,
 	    struct xtables_rule_match *matches,
 	    const struct xtables_target *target)
 {
@@ -860,8 +802,8 @@
 }
 
 int
-for_each_chain4(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *),
-	       int verbose, int builtinstoo, struct iptc_handle *handle)
+for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *),
+	       int verbose, int builtinstoo, struct xtc_handle *handle)
 {
         int ret = 1;
 	const char *chain;
@@ -874,21 +816,21 @@
 		chain = iptc_next_chain(handle);
         }
 
-	chains = xtables_malloc(sizeof(ipt_chainlabel) * chaincount);
+	chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount);
 	i = 0;
 	chain = iptc_first_chain(handle);
 	while (chain) {
-		strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+		strcpy(chains + i*sizeof(xt_chainlabel), chain);
 		i++;
 		chain = iptc_next_chain(handle);
         }
 
 	for (i = 0; i < chaincount; i++) {
 		if (!builtinstoo
-		    && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+		    && iptc_builtin(chains + i*sizeof(xt_chainlabel),
 				    handle) == 1)
 			continue;
-	        ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+	        ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle);
 	}
 
 	free(chains);
@@ -896,8 +838,8 @@
 }
 
 int
-flush_entries4(const ipt_chainlabel chain, int verbose,
-	      struct iptc_handle *handle)
+flush_entries4(const xt_chainlabel chain, int verbose,
+	      struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain4(flush_entries4, verbose, 1, handle);
@@ -908,8 +850,8 @@
 }
 
 static int
-zero_entries(const ipt_chainlabel chain, int verbose,
-	     struct iptc_handle *handle)
+zero_entries(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain4(zero_entries, verbose, 1, handle);
@@ -920,8 +862,8 @@
 }
 
 int
-delete_chain4(const ipt_chainlabel chain, int verbose,
-	     struct iptc_handle *handle)
+delete_chain4(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
 {
 	if (!chain)
 		return for_each_chain4(delete_chain4, verbose, 0, handle);
@@ -932,8 +874,8 @@
 }
 
 static int
-list_entries(const ipt_chainlabel chain, int rulenum, int verbose, int numeric,
-	     int expanded, int linenumbers, struct iptc_handle *handle)
+list_entries(const xt_chainlabel chain, int rulenum, int verbose, int numeric,
+	     int expanded, int linenumbers, struct xtc_handle *handle)
 {
 	int found = 0;
 	unsigned int format;
@@ -1044,14 +986,15 @@
 	}
 }
 
-static int print_match_save(const struct ipt_entry_match *e,
+static int print_match_save(const struct xt_entry_match *e,
 			const struct ipt_ip *ip)
 {
 	const struct xtables_match *match =
 		xtables_find_match(e->u.user.name, XTF_TRY_LOAD, NULL);
 
 	if (match) {
-		printf(" -m %s", e->u.user.name);
+		printf(" -m %s",
+			match->alias ? match->alias(e) : e->u.user.name);
 
 		/* some matches don't provide a save function */
 		if (match->save)
@@ -1100,9 +1043,9 @@
 /* We want this to be readable, so only print out neccessary fields.
  * Because that's the kind of world I want to live in.  */
 void print_rule4(const struct ipt_entry *e,
-		struct iptc_handle *h, const char *chain, int counters)
+		struct xtc_handle *h, const char *chain, int counters)
 {
-	const struct ipt_entry_target *t;
+	const struct xt_entry_target *t;
 	const char *target_name;
 
 	/* print counters for iptables-save */
@@ -1125,7 +1068,7 @@
 	print_iface('o', e->ip.outiface, e->ip.outiface_mask,
 		    e->ip.invflags & IPT_INV_VIA_OUT);
 
-	print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
+	print_proto(e->ip.proto, e->ip.invflags & XT_INV_PROTO);
 
 	if (e->ip.flags & IPT_F_FRAG)
 		printf("%s -f",
@@ -1140,16 +1083,8 @@
 	if (counters < 0)
 		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
 
-	/* Print target name */
+	/* Print target name and targinfo part */
 	target_name = iptc_get_target(e, h);
-	if (target_name && (*target_name != '\0'))
-#ifdef IPT_F_GOTO
-		printf(" -%c %s", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
-#else
-		printf(" -j %s", target_name);
-#endif
-
-	/* Print targinfo part */
 	t = ipt_get_target((struct ipt_entry *)e);
 	if (t->u.user.name[0]) {
 		const struct xtables_target *target =
@@ -1161,27 +1096,34 @@
 			exit(1);
 		}
 
+		printf(" -j %s", target->alias ? target->alias(t) : target_name);
 		if (target->save)
 			target->save(&e->ip, t);
 		else {
-			/* If the target size is greater than ipt_entry_target
+			/* If the target size is greater than xt_entry_target
 			 * there is something to be saved, we just don't know
 			 * how to print it */
 			if (t->u.target_size !=
-			    sizeof(struct ipt_entry_target)) {
+			    sizeof(struct xt_entry_target)) {
 				fprintf(stderr, "Target `%s' is missing "
 						"save function\n",
 					t->u.user.name);
 				exit(1);
 			}
 		}
-	}
+	} else if (target_name && (*target_name != '\0'))
+#ifdef IPT_F_GOTO
+		printf(" -%c %s", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
+#else
+		printf(" -j %s", target_name);
+#endif
+
 	printf("\n");
 }
 
 static int
-list_rules(const ipt_chainlabel chain, int rulenum, int counters,
-	     struct iptc_handle *handle)
+list_rules(const xt_chainlabel chain, int rulenum, int counters,
+	     struct xtc_handle *handle)
 {
 	const char *this = NULL;
 	int found = 0;
@@ -1198,7 +1140,7 @@
 			continue;
 
 		if (iptc_builtin(this, handle)) {
-			struct ipt_counters count;
+			struct xt_counters count;
 			printf("-P %s %s", this, iptc_get_policy(this, &count, handle));
 			if (counters)
 			    printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
@@ -1235,7 +1177,7 @@
 static struct ipt_entry *
 generate_entry(const struct ipt_entry *fw,
 	       struct xtables_rule_match *matches,
-	       struct ipt_entry_target *target)
+	       struct xt_entry_target *target)
 {
 	unsigned int size;
 	struct xtables_rule_match *matchp;
@@ -1260,42 +1202,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;
-}
-
-void
-get_kernel_version(void) {
-	static struct utsname uts;
-	int x = 0, y = 0, z = 0;
-
-	if (uname(&uts) == -1) {
-		fprintf(stderr, "Unable to retrieve kernel version.\n");
-		xtables_free_opts(1);
-		exit(1);
-	}
-
-	sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
-	kernel_version = LINUX_VERSION(x, y, z);
-}
-
 static void command_jump(struct iptables_command_state *cs)
 {
 	size_t size;
@@ -1308,15 +1214,25 @@
 	if (cs->target == NULL)
 		return;
 
-	size = XT_ALIGN(sizeof(struct ipt_entry_target))
+	size = XT_ALIGN(sizeof(struct xt_entry_target))
 		+ cs->target->size;
 
 	cs->target->t = xtables_calloc(1, size);
 	cs->target->t->u.target_size = size;
-	strcpy(cs->target->t->u.user.name, cs->jumpto);
+	if (cs->target->real_name == NULL) {
+		strcpy(cs->target->t->u.user.name, cs->jumpto);
+	} else {
+		/* Alias support for userspace side */
+		strcpy(cs->target->t->u.user.name, cs->target->real_name);
+		if (!(cs->target->ext_flags & XTABLES_EXT_ALIAS))
+			fprintf(stderr, "Notice: The %s target is converted into %s target "
+			        "in rule listing and saving.\n",
+			        cs->jumpto, cs->target->real_name);
+	}
 	cs->target->t->u.user.revision = cs->target->revision;
-	if (cs->target->init != NULL)
-		cs->target->init(cs->target->t);
+
+	xs_init_target(cs->target);
+
 	if (cs->target->x6_options != NULL)
 		opts = xtables_options_xfrm(iptables_globals.orig_opts, opts,
 					    cs->target->x6_options,
@@ -1339,13 +1255,20 @@
 			   "unexpected ! flag before --match");
 
 	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
-	size = XT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
 	m->m = xtables_calloc(1, size);
 	m->m->u.match_size = size;
-	strcpy(m->m->u.user.name, m->name);
+	if (m->real_name == NULL) {
+		strcpy(m->m->u.user.name, m->name);
+	} else {
+		strcpy(m->m->u.user.name, m->real_name);
+		if (!(m->ext_flags & XTABLES_EXT_ALIAS))
+			fprintf(stderr, "Notice: the %s match is converted into %s match "
+			        "in rule listing and saving.\n", m->name, m->real_name);
+	}
 	m->m->u.user.revision = m->revision;
-	if (m->init != NULL)
-		m->init(m->m);
+
+	xs_init_match(m);
 	if (m == m->next)
 		return;
 	/* Merge options for non-cloned matches */
@@ -1359,7 +1282,8 @@
 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
 }
 
-int do_command4(int argc, char *argv[], char **table, struct iptc_handle **handle)
+int do_command4(int argc, char *argv[], char **table,
+		struct xtc_handle **handle, bool restore)
 {
 	struct iptables_command_state cs;
 	struct ipt_entry *e = NULL;
@@ -1368,6 +1292,7 @@
 	struct in_addr *daddrs = NULL, *dmasks = NULL;
 
 	int verbose = 0;
+	bool wait = false;
 	const char *chain = NULL;
 	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
 	const char *policy = NULL, *newname = NULL;
@@ -1403,7 +1328,7 @@
 
 	opts = xt_params->orig_opts;
 	while ((cs.c = getopt_long(argc, argv,
-	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46",
+	   "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvwnt:m:xc:g:46",
 					   opts, NULL)) != -1) {
 		switch (cs.c) {
 			/*
@@ -1567,7 +1492,6 @@
 			 * Option selection
 			 */
 		case 'p':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags,
 				   cs.invert);
 
@@ -1579,20 +1503,18 @@
 			cs.fw.ip.proto = xtables_parse_protocol(cs.protocol);
 
 			if (cs.fw.ip.proto == 0
-			    && (cs.fw.ip.invflags & IPT_INV_PROTO))
+			    && (cs.fw.ip.invflags & XT_INV_PROTO))
 				xtables_error(PARAMETER_PROBLEM,
 					   "rule would never match protocol");
 			break;
 
 		case 's':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags,
 				   cs.invert);
 			shostnetworkmask = optarg;
 			break;
 
 		case 'd':
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags,
 				   cs.invert);
 			dhostnetworkmask = optarg;
@@ -1617,7 +1539,6 @@
 				xtables_error(PARAMETER_PROBLEM,
 					"Empty interface is likely to be "
 					"undesired");
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags,
 				   cs.invert);
 			xtables_parse_interface(optarg,
@@ -1630,7 +1551,6 @@
 				xtables_error(PARAMETER_PROBLEM,
 					"Empty interface is likely to be "
 					"undesired");
-			xtables_check_inverse(optarg, &cs.invert, &optind, argc, argv);
 			set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags,
 				   cs.invert);
 			xtables_parse_interface(optarg,
@@ -1651,6 +1571,15 @@
 			verbose++;
 			break;
 
+		case 'w':
+			if (restore) {
+				xtables_error(PARAMETER_PROBLEM,
+					      "You cannot use `-w' from "
+					      "iptables-restore");
+			}
+			wait = true;
+			break;
+
 		case 'm':
 			command_match(&cs);
 			break;
@@ -1805,6 +1734,14 @@
 			   "chain name `%s' too long (must be under %u chars)",
 			   chain, XT_EXTENSION_MAXNAMELEN);
 
+	/* Attempt to acquire the xtables lock */
+	if (!restore && !xtables_lock(wait)) {
+		fprintf(stderr, "Another app is currently holding the xtables lock. "
+			"Perhaps you want to use the -w option?\n");
+		xtables_free_opts(1);
+		exit(RESOURCE_PROBLEM);
+	}
+
 	/* only allocate handle if we weren't called with a handle */
 	if (!*handle)
 		*handle = iptc_init(*table);
@@ -1861,18 +1798,17 @@
 			|| iptc_is_chain(cs.jumpto, *handle))) {
 			size_t size;
 
-			cs.target = xtables_find_target(IPT_STANDARD_TARGET,
+			cs.target = xtables_find_target(XT_STANDARD_TARGET,
 					 XTF_LOAD_MUST_SUCCEED);
 
-			size = sizeof(struct ipt_entry_target)
+			size = sizeof(struct xt_entry_target)
 				+ cs.target->size;
 			cs.target->t = xtables_calloc(1, size);
 			cs.target->t->u.target_size = size;
 			strcpy(cs.target->t->u.user.name, cs.jumpto);
 			if (!iptc_is_chain(cs.jumpto, *handle))
 				cs.target->t->u.user.revision = cs.target->revision;
-			if (cs.target->init != NULL)
-				cs.target->init(cs.target->t);
+			xs_init_target(cs.target);
 		}
 
 		if (!cs.target) {
@@ -1988,7 +1924,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/iptables/xshared.c b/iptables/xshared.c
index 0e3857b..6c9992e 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -6,9 +6,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
 #include <xtables.h>
 #include "xshared.h"
 
+#define XT_SOCKET_NAME "xtables"
+#define XT_SOCKET_LEN 8
+
 /*
  * 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
@@ -139,14 +145,13 @@
 
 		cs->proto_used = 1;
 
-		size = XT_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+		size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
 
 		m->m = xtables_calloc(1, size);
 		m->m->u.match_size = size;
 		strcpy(m->m->u.user.name, m->name);
 		m->m->u.user.revision = m->revision;
-		if (m->init != NULL)
-			m->init(m->m);
+		xs_init_match(m);
 
 		if (m->x6_options != NULL)
 			gl->opts = xtables_options_xfrm(gl->orig_opts,
@@ -207,3 +212,60 @@
 		fprintf(stderr, " * %s\n", cb->name);
 	exit(EXIT_FAILURE);
 }
+
+void xs_init_target(struct xtables_target *target)
+{
+	if (target->udata_size != 0) {
+		free(target->udata);
+		target->udata = calloc(1, target->udata_size);
+		if (target->udata == NULL)
+			xtables_error(RESOURCE_PROBLEM, "malloc");
+	}
+	if (target->init != NULL)
+		target->init(target->t);
+}
+
+void xs_init_match(struct xtables_match *match)
+{
+	if (match->udata_size != 0) {
+		/*
+		 * As soon as a subsequent instance of the same match
+		 * is used, e.g. "-m time -m time", the first instance
+		 * is no longer reachable anyway, so we can free udata.
+		 * Same goes for target.
+		 */
+		free(match->udata);
+		match->udata = calloc(1, match->udata_size);
+		if (match->udata == NULL)
+			xtables_error(RESOURCE_PROBLEM, "malloc");
+	}
+	if (match->init != NULL)
+		match->init(match->m);
+}
+
+bool xtables_lock(bool wait)
+{
+	int i = 0, ret, xt_socket;
+	struct sockaddr_un xt_addr;
+
+	memset(&xt_addr, 0, sizeof(xt_addr));
+	xt_addr.sun_family = AF_UNIX;
+	strcpy(xt_addr.sun_path+1, XT_SOCKET_NAME);
+	xt_socket = socket(AF_UNIX, SOCK_STREAM, 0);
+	/* If we can't even create a socket, fall back to prior (lockless) behavior */
+	if (xt_socket < 0)
+		return true;
+
+	while (1) {
+		ret = bind(xt_socket, (struct sockaddr*)&xt_addr,
+			   offsetof(struct sockaddr_un, sun_path)+XT_SOCKET_LEN);
+		if (ret == 0)
+			return true;
+		else if (wait == false)
+			return false;
+		if (++i % 2 == 0)
+			fprintf(stderr, "Another app is currently holding the xtables lock; "
+				"waiting for it to exit...\n");
+		sleep(1);
+	}
+}
diff --git a/iptables/xshared.h b/iptables/xshared.h
index b44a3a3..1e2b9b8 100644
--- a/iptables/xshared.h
+++ b/iptables/xshared.h
@@ -2,6 +2,7 @@
 #define IPTABLES_XSHARED_H 1
 
 #include <limits.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <netinet/in.h>
 #include <net/if.h>
@@ -81,6 +82,9 @@
 	struct xtables_globals *);
 extern struct xtables_match *load_proto(struct iptables_command_state *);
 extern int subcmd_main(int, char **, const struct subcommand *);
+extern void xs_init_target(struct xtables_target *);
+extern void xs_init_match(struct xtables_match *);
+extern bool xtables_lock(bool wait);
 
 extern const struct xtables_afinfo *afinfo;
 
diff --git a/libipq/.gitignore b/libipq/.gitignore
new file mode 100644
index 0000000..6cb21a3
--- /dev/null
+++ b/libipq/.gitignore
@@ -0,0 +1 @@
+/libipq.pc
diff --git a/libipq/Makefile.am b/libipq/Makefile.am
index 93e5b1c..9e3a2ca 100644
--- a/libipq/Makefile.am
+++ b/libipq/Makefile.am
@@ -9,3 +9,5 @@
                    ipq_get_msgerr.3 ipq_get_packet.3 ipq_message_type.3 \
                    ipq_perror.3 ipq_read.3 ipq_set_mode.3 ipq_set_verdict.3 \
                    libipq.3
+
+pkgconfig_DATA = libipq.pc
diff --git a/libipq/libipq.c b/libipq/libipq.c
index e330487..fb65971 100644
--- a/libipq/libipq.c
+++ b/libipq/libipq.c
@@ -231,7 +231,6 @@
         
 	if (h->fd == -1) {
 		ipq_errno = IPQ_ERR_SOCKET;
-		close(h->fd);
 		free(h);
 		return NULL;
 	}
diff --git a/libipq/libipq.pc.in b/libipq/libipq.pc.in
new file mode 100644
index 0000000..ea31ec7
--- /dev/null
+++ b/libipq/libipq.pc.in
@@ -0,0 +1,11 @@
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name:		libipq
+Description:	Interface to the (old) ip_queue mechanism
+Version:	@PACKAGE_VERSION@
+Libs:		-L${libdir} -lipq
+Cflags:		-I${includedir}
diff --git a/libiptc/.gitignore b/libiptc/.gitignore
index 8767550..49ca83d 100644
--- a/libiptc/.gitignore
+++ b/libiptc/.gitignore
@@ -1 +1 @@
-/libiptc.pc
+/*.pc
diff --git a/libiptc/Makefile.am b/libiptc/Makefile.am
index 22c920f..f789d34 100644
--- a/libiptc/Makefile.am
+++ b/libiptc/Makefile.am
@@ -3,13 +3,13 @@
 AM_CFLAGS        = ${regular_CFLAGS}
 AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
 
-pkgconfig_DATA      = libiptc.pc
+pkgconfig_DATA      = libiptc.pc libip4tc.pc libip6tc.pc
 
 lib_LTLIBRARIES     = libip4tc.la libip6tc.la libiptc.la
 libiptc_la_SOURCES  =
 libiptc_la_LIBADD   = libip4tc.la libip6tc.la
 libiptc_la_LDFLAGS  = -version-info 0:0:0 ${libiptc_LDFLAGS2}
 libip4tc_la_SOURCES = libip4tc.c
-libip4tc_la_LDFLAGS = -version-info 0:0:0
+libip4tc_la_LDFLAGS = -version-info 1:0:1
 libip6tc_la_SOURCES = libip6tc.c
-libip6tc_la_LDFLAGS = -version-info 0:0:0 ${libiptc_LDFLAGS2}
+libip6tc_la_LDFLAGS = -version-info 1:0:1 ${libiptc_LDFLAGS2}
diff --git a/libiptc/libip4tc.c b/libiptc/libip4tc.c
index e9e85c5..2b029d4 100644
--- a/libiptc/libip4tc.c
+++ b/libiptc/libip4tc.c
@@ -36,33 +36,27 @@
 #define HOOK_FORWARD		NF_IP_FORWARD
 #define HOOK_LOCAL_OUT		NF_IP_LOCAL_OUT
 #define HOOK_POST_ROUTING	NF_IP_POST_ROUTING
-#ifdef NF_IP_DROPPING
-#define HOOK_DROPPING		NF_IP_DROPPING
-#endif
 
-#define STRUCT_ENTRY_TARGET	struct ipt_entry_target
+#define STRUCT_ENTRY_TARGET	struct xt_entry_target
 #define STRUCT_ENTRY		struct ipt_entry
-#define STRUCT_ENTRY_MATCH	struct ipt_entry_match
+#define STRUCT_ENTRY_MATCH	struct xt_entry_match
 #define STRUCT_GETINFO		struct ipt_getinfo
 #define STRUCT_GET_ENTRIES	struct ipt_get_entries
-#define STRUCT_COUNTERS		struct ipt_counters
-#define STRUCT_COUNTERS_INFO	struct ipt_counters_info
-#define STRUCT_STANDARD_TARGET	struct ipt_standard_target
+#define STRUCT_COUNTERS		struct xt_counters
+#define STRUCT_COUNTERS_INFO	struct xt_counters_info
+#define STRUCT_STANDARD_TARGET	struct xt_standard_target
 #define STRUCT_REPLACE		struct ipt_replace
 
-#define STRUCT_TC_HANDLE	struct iptc_handle
-#define xtc_handle		iptc_handle
-
 #define ENTRY_ITERATE		IPT_ENTRY_ITERATE
-#define TABLE_MAXNAMELEN	IPT_TABLE_MAXNAMELEN
-#define FUNCTION_MAXNAMELEN	IPT_FUNCTION_MAXNAMELEN
+#define TABLE_MAXNAMELEN	XT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN	XT_FUNCTION_MAXNAMELEN
 
 #define GET_TARGET		ipt_get_target
 
-#define ERROR_TARGET		IPT_ERROR_TARGET
+#define ERROR_TARGET		XT_ERROR_TARGET
 #define NUMHOOKS		NF_IP_NUMHOOKS
 
-#define IPT_CHAINLABEL		ipt_chainlabel
+#define IPT_CHAINLABEL		xt_chainlabel
 
 #define TC_DUMP_ENTRIES		dump_entries
 #define TC_IS_CHAIN		iptc_is_chain
@@ -96,6 +90,7 @@
 #define TC_STRERROR		iptc_strerror
 #define TC_NUM_RULES		iptc_num_rules
 #define TC_GET_RULE		iptc_get_rule
+#define TC_OPS			iptc_ops
 
 #define TC_AF			AF_INET
 #define TC_IPPROTO		IPPROTO_IP
@@ -106,14 +101,14 @@
 #define SO_GET_ENTRIES		IPT_SO_GET_ENTRIES
 #define SO_GET_VERSION		IPT_SO_GET_VERSION
 
-#define STANDARD_TARGET		IPT_STANDARD_TARGET
+#define STANDARD_TARGET		XT_STANDARD_TARGET
 #define LABEL_RETURN		IPTC_LABEL_RETURN
 #define LABEL_ACCEPT		IPTC_LABEL_ACCEPT
 #define LABEL_DROP		IPTC_LABEL_DROP
 #define LABEL_QUEUE		IPTC_LABEL_QUEUE
 
 #define ALIGN			XT_ALIGN
-#define RETURN			IPT_RETURN
+#define RETURN			XT_RETURN
 
 #include "libiptc.c"
 
@@ -126,7 +121,7 @@
 #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
 
 static int
-dump_entry(struct ipt_entry *e, struct iptc_handle *const handle)
+dump_entry(struct ipt_entry *e, struct xtc_handle *const handle)
 {
 	size_t i;
 	STRUCT_ENTRY_TARGET *t;
@@ -166,7 +161,7 @@
 			       : "UNKNOWN");
 		else
 			printf("verdict=%u\n", pos);
-	} else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+	} else if (strcmp(t->u.user.name, XT_ERROR_TARGET) == 0)
 		printf("error=`%s'\n", t->data);
 
 	printf("\n");
@@ -209,7 +204,7 @@
 	mptr = matchmask + sizeof(STRUCT_ENTRY);
 	if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
 		return NULL;
-	mptr += XT_ALIGN(sizeof(struct ipt_entry_target));
+	mptr += XT_ALIGN(sizeof(struct xt_entry_target));
 
 	return mptr;
 }
@@ -241,7 +236,7 @@
 static inline int
 check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
 	    unsigned int user_offset, int *was_return,
-	    struct iptc_handle *h)
+	    struct xtc_handle *h)
 {
 	unsigned int toff;
 	STRUCT_STANDARD_TARGET *t;
@@ -277,14 +272,14 @@
 
 			idx = iptcb_entry2index(h, te);
 			assert(strcmp(GET_TARGET(te)->u.user.name,
-				      IPT_ERROR_TARGET)
+				      XT_ERROR_TARGET)
 			       != 0);
 			assert(te != e);
 
 			/* Prior node must be error node, or this node. */
 			assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset
 			       || strcmp(GET_TARGET(index2entry(h, idx-1))
-					 ->u.user.name, IPT_ERROR_TARGET)
+					 ->u.user.name, XT_ERROR_TARGET)
 			       == 0);
 		}
 
@@ -294,7 +289,7 @@
 			*was_return = 1;
 		else
 			*was_return = 0;
-	} else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+	} else if (strcmp(t->target.u.user.name, XT_ERROR_TARGET) == 0) {
 		assert(t->target.u.target_size
 		       == ALIGN(sizeof(struct ipt_error_target)));
 
@@ -307,7 +302,7 @@
 	else *was_return = 0;
 
 	if (*off == user_offset)
-		assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+		assert(strcmp(t->target.u.user.name, XT_ERROR_TARGET) == 0);
 
 	(*off) += e->next_offset;
 	(*i)++;
@@ -317,7 +312,7 @@
 #ifdef IPTC_DEBUG
 /* Do every conceivable sanity check on the handle */
 static void
-do_check(struct iptc_handle *h, unsigned int line)
+do_check(struct xtc_handle *h, unsigned int line)
 {
 	unsigned int i, n;
 	unsigned int user_offset; /* Offset of first user chain */
@@ -426,15 +421,6 @@
 		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
 
 		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
-
-#ifdef NF_IP_DROPPING
-	} else if (strcmp(h->info.name, "drop") == 0) {
-		assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
-
-		/* Hook should be first */
-		assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
-		user_offset = 0;
-#endif
 	} else {
 		fprintf(stderr, "Unknown table `%s'\n", h->info.name);
 		abort();
diff --git a/libiptc/libip4tc.pc.in b/libiptc/libip4tc.pc.in
new file mode 100644
index 0000000..5efa1ca
--- /dev/null
+++ b/libiptc/libip4tc.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name:		libip4tc
+Description:	iptables IPv4 ruleset ADT and kernel interface
+Version:	@PACKAGE_VERSION@
+Libs:		-L${libdir} -lip4tc
+Cflags:		-I${includedir}
diff --git a/libiptc/libip6tc.c b/libiptc/libip6tc.c
index a97f397..4e47e69 100644
--- a/libiptc/libip6tc.c
+++ b/libiptc/libip6tc.c
@@ -35,29 +35,26 @@
 #define HOOK_LOCAL_OUT		NF_IP6_LOCAL_OUT
 #define HOOK_POST_ROUTING	NF_IP6_POST_ROUTING
 
-#define STRUCT_ENTRY_TARGET	struct ip6t_entry_target
+#define STRUCT_ENTRY_TARGET	struct xt_entry_target
 #define STRUCT_ENTRY		struct ip6t_entry
-#define STRUCT_ENTRY_MATCH	struct ip6t_entry_match
+#define STRUCT_ENTRY_MATCH	struct xt_entry_match
 #define STRUCT_GETINFO		struct ip6t_getinfo
 #define STRUCT_GET_ENTRIES	struct ip6t_get_entries
-#define STRUCT_COUNTERS		struct ip6t_counters
-#define STRUCT_COUNTERS_INFO	struct ip6t_counters_info
-#define STRUCT_STANDARD_TARGET	struct ip6t_standard_target
+#define STRUCT_COUNTERS		struct xt_counters
+#define STRUCT_COUNTERS_INFO	struct xt_counters_info
+#define STRUCT_STANDARD_TARGET	struct xt_standard_target
 #define STRUCT_REPLACE		struct ip6t_replace
 
-#define STRUCT_TC_HANDLE	struct ip6tc_handle
-#define xtc_handle		ip6tc_handle
-
 #define ENTRY_ITERATE		IP6T_ENTRY_ITERATE
-#define TABLE_MAXNAMELEN	IP6T_TABLE_MAXNAMELEN
-#define FUNCTION_MAXNAMELEN	IP6T_FUNCTION_MAXNAMELEN
+#define TABLE_MAXNAMELEN	XT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN	XT_FUNCTION_MAXNAMELEN
 
 #define GET_TARGET		ip6t_get_target
 
-#define ERROR_TARGET		IP6T_ERROR_TARGET
+#define ERROR_TARGET		XT_ERROR_TARGET
 #define NUMHOOKS		NF_IP6_NUMHOOKS
 
-#define IPT_CHAINLABEL		ip6t_chainlabel
+#define IPT_CHAINLABEL		xt_chainlabel
 
 #define TC_DUMP_ENTRIES		dump_entries6
 #define TC_IS_CHAIN		ip6tc_is_chain
@@ -91,6 +88,7 @@
 #define TC_STRERROR		ip6tc_strerror
 #define TC_NUM_RULES		ip6tc_num_rules
 #define TC_GET_RULE		ip6tc_get_rule
+#define TC_OPS			ip6tc_ops
 
 #define TC_AF			AF_INET6
 #define TC_IPPROTO		IPPROTO_IPV6
@@ -101,21 +99,21 @@
 #define SO_GET_ENTRIES		IP6T_SO_GET_ENTRIES
 #define SO_GET_VERSION		IP6T_SO_GET_VERSION
 
-#define STANDARD_TARGET		IP6T_STANDARD_TARGET
+#define STANDARD_TARGET		XT_STANDARD_TARGET
 #define LABEL_RETURN		IP6TC_LABEL_RETURN
 #define LABEL_ACCEPT		IP6TC_LABEL_ACCEPT
 #define LABEL_DROP		IP6TC_LABEL_DROP
 #define LABEL_QUEUE		IP6TC_LABEL_QUEUE
 
 #define ALIGN			XT_ALIGN
-#define RETURN			IP6T_RETURN
+#define RETURN			XT_RETURN
 
 #include "libiptc.c"
 
 #define BIT6(a, l) \
  ((ntohl(a->s6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
 
-int
+static int
 ipv6_prefix_length(const struct in6_addr *a)
 {
 	int l, i;
@@ -131,12 +129,12 @@
 }
 
 static int
-dump_entry(struct ip6t_entry *e, struct ip6tc_handle *const handle)
+dump_entry(struct ip6t_entry *e, struct xtc_handle *const handle)
 {
 	size_t i;
 	char buf[40];
 	int len;
-	struct ip6t_entry_target *t;
+	struct xt_entry_target *t;
 	
 	printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
 	       iptcb_entry2offset(handle, e));
@@ -185,18 +183,18 @@
 
 	t = ip6t_get_target(e);
 	printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
-	if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+	if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0) {
 		const unsigned char *data = t->data;
 		int pos = *(const int *)data;
 		if (pos < 0)
 			printf("verdict=%s\n",
 			       pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
 			       : pos == -NF_DROP-1 ? "NF_DROP"
-			       : pos == IP6T_RETURN ? "RETURN"
+			       : pos == XT_RETURN ? "RETURN"
 			       : "UNKNOWN");
 		else
 			printf("verdict=%u\n", pos);
-	} else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+	} else if (strcmp(t->u.user.name, XT_ERROR_TARGET) == 0)
 		printf("error=`%s'\n", t->data);
 
 	printf("\n");
@@ -241,7 +239,7 @@
 	mptr = matchmask + sizeof(STRUCT_ENTRY);
 	if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
 		return NULL;
-	mptr += XT_ALIGN(sizeof(struct ip6t_entry_target));
+	mptr += XT_ALIGN(sizeof(struct xt_entry_target));
 
 	return mptr;
 }
diff --git a/libiptc/libip6tc.pc.in b/libiptc/libip6tc.pc.in
new file mode 100644
index 0000000..30a61b2
--- /dev/null
+++ b/libiptc/libip6tc.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name:		libip6tc
+Description:	iptables IPv6 ruleset ADT and kernel interface
+Version:	@PACKAGE_VERSION@
+Libs:		-L${libdir} -lip6tc
+Cflags:		-I${includedir}
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
index 4fff5a6..3a8e143 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -29,10 +29,13 @@
  * 	- performance work: speedup initial ruleset parsing.
  * 	- sponsored by ComX Networks A/S (http://www.comx.dk/)
  */
+#include <unistd.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdbool.h>
 #include <xtables.h>
+#include <libiptc/xtcshared.h>
 
 #include "linux_list.h"
 
@@ -61,9 +64,6 @@
 	[HOOK_FORWARD]		= "FORWARD",
 	[HOOK_LOCAL_OUT]	= "OUTPUT",
 	[HOOK_POST_ROUTING]	= "POSTROUTING",
-#ifdef HOOK_DROPPING
-	[HOOK_DROPPING]		= "DROPPING"
-#endif
 };
 
 /* Convenience structures */
@@ -131,8 +131,7 @@
 	unsigned int foot_offset;	/* offset in rule blob */
 };
 
-STRUCT_TC_HANDLE
-{
+struct xtc_handle {
 	int sockfd;
 	int changed;			 /* Have changes been made? */
 
@@ -404,7 +403,7 @@
 		}
 		debug("jump back to pos:%d (end:%d)\n", pos, end);
 		goto loop;
-	} else if (res > 0 ){ /* Not far enough, jump forward */
+	} else { /* res > 0; Not far enough, jump forward */
 
 		/* Exit case: Last element of array */
 		if (pos == handle->chain_index_sz-1) {
@@ -431,8 +430,6 @@
 		debug("jump forward to pos:%d (end:%d)\n", pos, end);
 		goto loop;
 	}
-
-	return list_pos;
 }
 
 /* Wrapper for string chain name based bsearch */
@@ -1015,6 +1012,7 @@
 			if (t->target.u.target_size
 			    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
 				errno = EINVAL;
+				free(r);
 				return -1;
 			}
 
@@ -1095,10 +1093,10 @@
 /* Convenience structures */
 struct iptcb_chain_start{
 	STRUCT_ENTRY e;
-	struct ipt_error_target name;
+	struct xt_error_target name;
 };
 #define IPTCB_CHAIN_START_SIZE	(sizeof(STRUCT_ENTRY) +			\
-				 ALIGN(sizeof(struct ipt_error_target)))
+				 ALIGN(sizeof(struct xt_error_target)))
 
 struct iptcb_chain_foot {
 	STRUCT_ENTRY e;
@@ -1109,10 +1107,10 @@
 
 struct iptcb_chain_error {
 	STRUCT_ENTRY entry;
-	struct ipt_error_target target;
+	struct xt_error_target target;
 };
 #define IPTCB_CHAIN_ERROR_SIZE	(sizeof(STRUCT_ENTRY) +			\
-				 ALIGN(sizeof(struct ipt_error_target)))
+				 ALIGN(sizeof(struct xt_error_target)))
 
 
 
@@ -1155,10 +1153,10 @@
 		head = (void *)repl->entries + c->head_offset;
 		head->e.target_offset = sizeof(STRUCT_ENTRY);
 		head->e.next_offset = IPTCB_CHAIN_START_SIZE;
-		strcpy(head->name.t.u.user.name, ERROR_TARGET);
-		head->name.t.u.target_size =
-				ALIGN(sizeof(struct ipt_error_target));
-		strcpy(head->name.error, c->name);
+		strcpy(head->name.target.u.user.name, ERROR_TARGET);
+		head->name.target.u.target_size =
+				ALIGN(sizeof(struct xt_error_target));
+		strcpy(head->name.errorname, c->name);
 	} else {
 		repl->hook_entry[c->hooknum-1] = c->head_offset;
 		repl->underflow[c->hooknum-1] = c->foot_offset;
@@ -1201,7 +1199,7 @@
 	if (!iptcc_is_builtin(c))  {
 		/* Chain has header */
 		*offset += sizeof(STRUCT_ENTRY)
-			     + ALIGN(sizeof(struct ipt_error_target));
+			     + ALIGN(sizeof(struct xt_error_target));
 		(*num)++;
 	}
 
@@ -1241,7 +1239,7 @@
 	/* Append one error rule at end of chain */
 	num++;
 	offset += sizeof(STRUCT_ENTRY)
-		  + ALIGN(sizeof(struct ipt_error_target));
+		  + ALIGN(sizeof(struct xt_error_target));
 
 	/* ruleset size is now in offset */
 	*size = offset;
@@ -1264,10 +1262,10 @@
 	error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
 	error->entry.target_offset = sizeof(STRUCT_ENTRY);
 	error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
-	error->target.t.u.user.target_size =
-		ALIGN(sizeof(struct ipt_error_target));
-	strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
-	strcpy((char *)&error->target.error, "ERROR");
+	error->target.target.u.user.target_size =
+		ALIGN(sizeof(struct xt_error_target));
+	strcpy((char *)&error->target.target.u.user.name, ERROR_TARGET);
+	strcpy((char *)&error->target.errorname, "ERROR");
 
 	return 1;
 }
@@ -1282,7 +1280,7 @@
 {
 	struct xtc_handle *h;
 
-	h = malloc(sizeof(STRUCT_TC_HANDLE));
+	h = malloc(sizeof(*h));
 	if (!h) {
 		errno = ENOMEM;
 		return NULL;
@@ -1316,6 +1314,7 @@
 	socklen_t s;
 	int sockfd;
 
+retry:
 	iptc_fn = TC_INIT;
 
 	if (strlen(tablename) >= TABLE_MAXNAMELEN) {
@@ -1327,7 +1326,12 @@
 	if (sockfd < 0)
 		return NULL;
 
-retry:
+	if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
+		fprintf(stderr, "Could not set close on exec: %s\n",
+			strerror(errno));
+		abort();
+	}
+
 	s = sizeof(info);
 
 	strcpy(info.name, tablename);
@@ -2004,8 +2008,10 @@
 			continue;
 
 		/* if we are just doing a dry run, we simply skip the rest */
-		if (dry_run)
+		if (dry_run){
+			free(r);
 			return 1;
+		}
 
 		/* If we are about to delete the rule that is the
 		 * current iterator, move rule iterator back.  next
@@ -2744,3 +2750,14 @@
 
 	return strerror(err);
 }
+
+const struct xtc_ops TC_OPS = {
+	.commit        = TC_COMMIT,
+	.free          = TC_FREE,
+	.builtin       = TC_BUILTIN,
+	.is_chain      = TC_IS_CHAIN,
+	.flush_entries = TC_FLUSH_ENTRIES,
+	.create_chain  = TC_CREATE_CHAIN,
+	.set_policy    = TC_SET_POLICY,
+	.strerror      = TC_STRERROR,
+};
diff --git a/libiptc/libiptc.pc.in b/libiptc/libiptc.pc.in
index 99a3544..0264bf0 100644
--- a/libiptc/libiptc.pc.in
+++ b/libiptc/libiptc.pc.in
@@ -5,8 +5,6 @@
 includedir=@includedir@
 
 Name:		libiptc
-Description:	iptables ruleset ADT and kernel interface
+Description:	iptables v4/v6 ruleset ADT and kernel interface
 Version:	@PACKAGE_VERSION@
-Libs:		-L${libdir} -liptc
-Libs.private:	-lip4tc -lip6tc
-Cflags:		-I${includedir}
+Requires:	libip4tc libip6tc
diff --git a/libxtables/Makefile.am b/libxtables/Makefile.am
new file mode 100644
index 0000000..4267cb5
--- /dev/null
+++ b/libxtables/Makefile.am
@@ -0,0 +1,20 @@
+# -*- Makefile -*-
+
+AM_CFLAGS   = ${regular_CFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir}/iptables ${kinclude_CPPFLAGS}
+
+lib_LTLIBRARIES       = libxtables.la
+libxtables_la_SOURCES = xtables.c xtoptions.c
+libxtables_la_LDFLAGS = -version-info ${libxtables_vcurrent}:0:${libxtables_vage}
+libxtables_la_LIBADD  =
+if ENABLE_STATIC
+# With --enable-static, shipped extensions are linked into the main executable,
+# so we need all the LIBADDs here too
+libxtables_la_LIBADD += -lm ${libnetfilter_conntrack_LIBS}
+endif
+if ENABLE_SHARED
+libxtables_la_CFLAGS  = ${AM_CFLAGS}
+libxtables_la_LIBADD += -ldl
+else
+libxtables_la_CFLAGS  = ${AM_CFLAGS} -DNO_SHARED_LIBS=1
+endif
diff --git a/iptables/xtables.c b/libxtables/xtables.c
similarity index 88%
rename from iptables/xtables.c
rename to libxtables/xtables.c
index acfcf8b..ef5bc07 100644
--- a/iptables/xtables.c
+++ b/libxtables/xtables.c
@@ -15,6 +15,7 @@
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+#include "config.h"
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -30,9 +31,16 @@
 #include <sys/stat.h>
 #include <sys/statfs.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <arpa/inet.h>
-#include <linux/magic.h> /* for PROC_SUPER_MAGIC */
+#if defined(HAVE_LINUX_MAGIC_H)
+#	include <linux/magic.h> /* for PROC_SUPER_MAGIC */
+#elif defined(HAVE_LINUX_PROC_FS_H)
+#	include <linux/proc_fs.h>	/* Linux 2.4 */
+#else
+#	define PROC_SUPER_MAGIC	0x9fa0
+#endif
 
 #include <xtables.h>
 #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
@@ -297,8 +305,8 @@
 {
 	int procfile;
 	char *ret;
+	int count;
 
-#define PROCFILE_BUFSIZ	1024
 	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
 	if (procfile < 0)
 		return NULL;
@@ -308,19 +316,19 @@
 		exit(1);
 	}
 
-	ret = malloc(PROCFILE_BUFSIZ);
+	ret = malloc(PATH_MAX);
 	if (ret) {
-		memset(ret, 0, PROCFILE_BUFSIZ);
-		switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
-		case -1: goto fail;
-		case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
+		count = read(procfile, ret, PATH_MAX);
+		if (count > 0 && count < PATH_MAX)
+		{
+			if (ret[count - 1] == '\n')
+				ret[count - 1] = '\0';
+			else
+				ret[count] = '\0';
+			close(procfile);
+			return ret;
 		}
-		if (ret[strlen(ret)-1]=='\n') 
-			ret[strlen(ret)-1]=0;
-		close(procfile);
-		return ret;
 	}
- fail:
 	free(ret);
 	close(procfile);
 	return NULL;
@@ -362,6 +370,7 @@
 		/* not usually reached */
 		exit(1);
 	case -1:
+		free(buf);
 		return -1;
 
 	default: /* parent */
@@ -507,15 +516,13 @@
 
 	strcpy(vianame, arg);
 	if (vialen == 0)
-		memset(mask, 0, IFNAMSIZ);
+		return;
 	else if (vianame[vialen - 1] == '+') {
 		memset(mask, 0xFF, vialen - 1);
-		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
 		/* Don't remove `+' here! -HW */
 	} else {
 		/* Include nul-terminator in match */
 		memset(mask, 0xFF, vialen + 1);
-		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
 		for (i = 0; vianame[i]; i++) {
 			if (vianame[i] == '/' ||
 			    vianame[i] == ' ') {
@@ -626,6 +633,7 @@
 			/* Second and subsequent clones */
 			clone = xtables_malloc(sizeof(struct xtables_match));
 			memcpy(clone, ptr, sizeof(struct xtables_match));
+			clone->udata = NULL;
 			clone->mflags = 0;
 			/* This is a clone: */
 			clone->next = clone;
@@ -854,14 +862,77 @@
 	xtables_pending_matches = me;
 }
 
+/**
+ * Compare two actions for their preference
+ * @a:	one action
+ * @b: 	another
+ *
+ * Like strcmp, returns a negative number if @a is less preferred than @b,
+ * positive number if @a is more preferred than @b, or zero if equally
+ * preferred.
+ */
+static int
+xtables_mt_prefer(bool a_alias, unsigned int a_rev, unsigned int a_fam,
+		  bool b_alias, unsigned int b_rev, unsigned int b_fam)
+{
+	/*
+	 * Alias ranks higher than no alias.
+	 * (We want the new action to be used whenever possible.)
+	 */
+	if (!a_alias && b_alias)
+		return -1;
+	if (a_alias && !b_alias)
+		return 1;
+
+	/* Higher revision ranks higher. */
+	if (a_rev < b_rev)
+		return -1;
+	if (a_rev > b_rev)
+		return 1;
+
+	/* NFPROTO_<specific> ranks higher than NFPROTO_UNSPEC. */
+	if (a_fam == NFPROTO_UNSPEC && b_fam != NFPROTO_UNSPEC)
+		return -1;
+	if (a_fam != NFPROTO_UNSPEC && b_fam == NFPROTO_UNSPEC)
+		return 1;
+
+	/* Must be the same thing. */
+	return 0;
+}
+
+static int xtables_match_prefer(const struct xtables_match *a,
+				const struct xtables_match *b)
+{
+	return xtables_mt_prefer(a->real_name != NULL,
+				 a->revision, a->family,
+				 b->real_name != NULL,
+				 b->revision, b->family);
+}
+
+static int xtables_target_prefer(const struct xtables_target *a,
+				 const struct xtables_target *b)
+{
+	/*
+	 * Note that if x->real_name==NULL, it will be set to x->name in
+	 * xtables_register_*; the direct pointer comparison here is therefore
+	 * legitimate to detect an alias.
+	 */
+	return xtables_mt_prefer(a->real_name != NULL,
+				 a->revision, a->family,
+				 b->real_name != NULL,
+				 b->revision, b->family);
+}
+
 static void xtables_fully_register_pending_match(struct xtables_match *me)
 {
 	struct xtables_match **i, *old;
+	const char *rn;
+	int compare;
 
 	old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
 	if (old) {
-		if (old->revision == me->revision &&
-		    old->family == me->family) {
+		compare = xtables_match_prefer(old, me);
+		if (compare == 0) {
 			fprintf(stderr,
 				"%s: match `%s' already registered.\n",
 				xt_params->program_name, me->name);
@@ -869,16 +940,14 @@
 		}
 
 		/* Now we have two (or more) options, check compatibility. */
-		if (compatible_match_revision(old->name, old->revision)
-		    && old->revision > me->revision)
+		rn = (old->real_name != NULL) ? old->real_name : old->name;
+		if (compare > 0 &&
+		    compatible_match_revision(rn, old->revision))
 			return;
 
 		/* See if new match can be used. */
-		if (!compatible_match_revision(me->name, me->revision))
-			return;
-
-		/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
-		if (old->revision == me->revision && me->family == AF_UNSPEC)
+		rn = (me->real_name != NULL) ? me->real_name : me->name;
+		if (!compatible_match_revision(rn, me->revision))
 			return;
 
 		/* Delete old one. */
@@ -954,13 +1023,15 @@
 static void xtables_fully_register_pending_target(struct xtables_target *me)
 {
 	struct xtables_target *old;
+	const char *rn;
+	int compare;
 
 	old = xtables_find_target(me->name, XTF_DURING_LOAD);
 	if (old) {
 		struct xtables_target **i;
 
-		if (old->revision == me->revision &&
-		    old->family == me->family) {
+		compare = xtables_target_prefer(old, me);
+		if (compare == 0) {
 			fprintf(stderr,
 				"%s: target `%s' already registered.\n",
 				xt_params->program_name, me->name);
@@ -968,16 +1039,14 @@
 		}
 
 		/* Now we have two (or more) options, check compatibility. */
-		if (compatible_target_revision(old->name, old->revision)
-		    && old->revision > me->revision)
+		rn = (old->real_name != NULL) ? old->real_name : old->name;
+		if (compare > 0 &&
+		    compatible_target_revision(rn, old->revision))
 			return;
 
 		/* See if new target can be used. */
-		if (!compatible_target_revision(me->name, me->revision))
-			return;
-
-		/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
-		if (old->revision == me->revision && me->family == AF_UNSPEC)
+		rn = (me->real_name != NULL) ? me->real_name : me->name;
+		if (!compatible_target_revision(rn, me->revision))
 			return;
 
 		/* Delete old one. */
@@ -1006,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
@@ -1042,8 +1133,10 @@
 	case XTF_ONLY_ONCE:
 		p2 = va_arg(args, const char *);
 		b  = va_arg(args, unsigned int);
-		if (!b)
+		if (!b) {
+			va_end(args);
 			return;
+		}
 		xt_params->exit_err(PARAMETER_PROBLEM,
 		           "%s: \"%s\" option may only be specified once",
 		           p1, p2);
@@ -1051,8 +1144,10 @@
 	case XTF_NO_INVERT:
 		p2 = va_arg(args, const char *);
 		b  = va_arg(args, unsigned int);
-		if (!b)
+		if (!b) {
+			va_end(args);
 			return;
+		}
 		xt_params->exit_err(PARAMETER_PROBLEM,
 		           "%s: \"%s\" option cannot be inverted", p1, p2);
 		break;
@@ -1065,8 +1160,10 @@
 		break;
 	case XTF_ONE_ACTION:
 		b = va_arg(args, unsigned int);
-		if (!b)
+		if (!b) {
+			va_end(args);
 			return;
+		}
 		xt_params->exit_err(PARAMETER_PROBLEM,
 		           "%s: At most one action is possible", p1);
 		break;
@@ -1119,28 +1216,43 @@
 	return xtables_ipaddr_to_numeric(addr);
 }
 
-const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
+int xtables_ipmask_to_cidr(const struct in_addr *mask)
 {
-	static char buf[20];
 	uint32_t maskaddr, bits;
 	int i;
 
 	maskaddr = ntohl(mask->s_addr);
-
+	/* shortcut for /32 networks */
 	if (maskaddr == 0xFFFFFFFFL)
-		/* we don't want to see "/32" */
-		return "";
+		return 32;
 
 	i = 32;
 	bits = 0xFFFFFFFEL;
 	while (--i >= 0 && maskaddr != bits)
 		bits <<= 1;
 	if (i >= 0)
-		sprintf(buf, "/%d", i);
-	else
+		return i;
+
+	/* this mask cannot be converted to CIDR notation */
+	return -1;
+}
+
+const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
+{
+	static char buf[20];
+	uint32_t cidr;
+
+	cidr = xtables_ipmask_to_cidr(mask);
+	if (cidr < 0) {
 		/* mask was not a decent combination of 1's and 0's */
 		sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
+		return buf;
+	} else if (cidr == 32) {
+		/* we don't want to see "/32" */
+		return "";
+	}
 
+	sprintf(buf, "/%d", cidr);
 	return buf;
 }
 
@@ -1287,7 +1399,7 @@
                               struct in_addr **maskpp, unsigned int *naddrs)
 {
 	struct in_addr *addrp;
-	char buf[256], *p;
+	char buf[256], *p, *next;
 	unsigned int len, i, j, n, count = 1;
 	const char *loop = name;
 
@@ -1302,23 +1414,19 @@
 	loop = name;
 
 	for (i = 0; i < count; ++i) {
-		if (loop == NULL)
-			break;
-		if (*loop == ',')
+		while (isspace(*loop))
 			++loop;
-		if (*loop == '\0')
-			break;
-		p = strchr(loop, ',');
-		if (p != NULL)
-			len = p - loop;
+		next = strchr(loop, ',');
+		if (next != NULL)
+			len = next - loop;
 		else
 			len = strlen(loop);
-		if (len == 0 || sizeof(buf) - 1 < len)
-			break;
+		if (len > sizeof(buf) - 1)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Hostname too long");
 
 		strncpy(buf, loop, len);
 		buf[len] = '\0';
-		loop += len;
 		if ((p = strrchr(buf, '/')) != NULL) {
 			*p = '\0';
 			addrp = parse_ipmask(p + 1);
@@ -1356,6 +1464,9 @@
 		}
 		/* free what ipparse_hostnetwork had allocated: */
 		free(addrp);
+		if (next == NULL)
+			break;
+		loop = next + 1;
 	}
 	*naddrs = count;
 	for (i = 0; i < count; ++i)
@@ -1452,7 +1563,7 @@
 	return xtables_ip6addr_to_numeric(addr);
 }
 
-static int ip6addr_prefix_length(const struct in6_addr *k)
+int xtables_ip6mask_to_cidr(const struct in6_addr *k)
 {
 	unsigned int bits = 0;
 	uint32_t a, b, c, d;
@@ -1479,14 +1590,18 @@
 const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
 {
 	static char buf[50+2];
-	int l = ip6addr_prefix_length(addrp);
+	int l = xtables_ip6mask_to_cidr(addrp);
 
 	if (l == -1) {
 		strcpy(buf, "/");
 		strcat(buf, xtables_ip6addr_to_numeric(addrp));
 		return buf;
 	}
-	sprintf(buf, "/%d", l);
+	/* we don't want to see "/128" */
+	if (l == 128)
+		return "";
+	else
+		sprintf(buf, "/%d", l);
 	return buf;
 }
 
@@ -1604,7 +1719,7 @@
 {
 	static const struct in6_addr zero_addr;
 	struct in6_addr *addrp;
-	char buf[256], *p;
+	char buf[256], *p, *next;
 	unsigned int len, i, j, n, count = 1;
 	const char *loop = name;
 
@@ -1619,23 +1734,19 @@
 	loop = name;
 
 	for (i = 0; i < count /*NB: count can grow*/; ++i) {
-		if (loop == NULL)
-			break;
-		if (*loop == ',')
+		while (isspace(*loop))
 			++loop;
-		if (*loop == '\0')
-			break;
-		p = strchr(loop, ',');
-		if (p != NULL)
-			len = p - loop;
+		next = strchr(loop, ',');
+		if (next != NULL)
+			len = next - loop;
 		else
 			len = strlen(loop);
-		if (len == 0 || sizeof(buf) - 1 < len)
-			break;
+		if (len > sizeof(buf) - 1)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Hostname too long");
 
 		strncpy(buf, loop, len);
 		buf[len] = '\0';
-		loop += len;
 		if ((p = strrchr(buf, '/')) != NULL) {
 			*p = '\0';
 			addrp = parse_ip6mask(p + 1);
@@ -1669,6 +1780,9 @@
 		}
 		/* free what ip6parse_hostnetwork had allocated: */
 		free(addrp);
+		if (next == NULL)
+			break;
+		loop = next + 1;
 	}
 	*naddrs = count;
 	for (i = 0; i < count; ++i)
@@ -1755,35 +1869,6 @@
 	}
 }
 
-/**
- * Check for option-intrapositional negation.
- * Do not use in new code.
- */
-int xtables_check_inverse(const char option[], int *invert,
-			  int *my_optind, int argc, char **argv)
-{
-	if (option == NULL || strcmp(option, "!") != 0)
-		return false;
-
-	fprintf(stderr, "Using intrapositioned negation "
-	        "(`--option ! this`) is deprecated in favor of "
-	        "extrapositioned (`! --option this`).\n");
-
-	if (*invert)
-		xt_params->exit_err(PARAMETER_PROBLEM,
-			   "Multiple `!' flags not allowed");
-	*invert = true;
-	if (my_optind != NULL) {
-		optarg = argv[*my_optind];
-		++*my_optind;
-		if (argc && *my_optind > argc)
-			xt_params->exit_err(PARAMETER_PROBLEM,
-				   "no argument following `!'");
-	}
-
-	return true;
-}
-
 const struct xtables_pprot xtables_chain_protos[] = {
 	{"tcp",       IPPROTO_TCP},
 	{"sctp",      IPPROTO_SCTP},
@@ -1830,3 +1915,49 @@
 		"unknown protocol \"%s\" specified", s);
 	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)
+{
+	static struct utsname uts;
+	int x = 0, y = 0, z = 0;
+
+	if (uname(&uts) == -1) {
+		fprintf(stderr, "Unable to retrieve kernel version.\n");
+		xtables_free_opts(1);
+		exit(1);
+	}
+
+	sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
+	kernel_version = LINUX_VERSION(x, y, z);
+}
diff --git a/iptables/xtoptions.c b/libxtables/xtoptions.c
similarity index 95%
rename from iptables/xtoptions.c
rename to libxtables/xtoptions.c
index ac0601f..78e9abd 100644
--- a/iptables/xtoptions.c
+++ b/libxtables/xtoptions.c
@@ -667,6 +667,33 @@
 	free(lo_arg);
 }
 
+static int xtopt_parse_mask(struct xt_option_call *cb)
+{
+	struct addrinfo hints = {.ai_family = afinfo->family,
+				 .ai_flags = AI_NUMERICHOST };
+	struct addrinfo *res;
+	int ret;
+
+	ret = getaddrinfo(cb->arg, NULL, &hints, &res);
+	if (ret < 0)
+		return 0;
+
+	memcpy(&cb->val.hmask, xtables_sa_host(res->ai_addr, res->ai_family),
+	       xtables_sa_hostlen(res->ai_family));
+
+	switch(afinfo->family) {
+	case AF_INET:
+		cb->val.hlen = xtables_ipmask_to_cidr(&cb->val.hmask.in);
+		break;
+	case AF_INET6:
+		cb->val.hlen = xtables_ip6mask_to_cidr(&cb->val.hmask.in6);
+		break;
+	}
+
+	freeaddrinfo(res);
+	return 1;
+}
+
 /**
  * Parse an integer and ensure it is within the address family's prefix length
  * limits. The result is stored in @cb->val.hlen.
@@ -677,12 +704,17 @@
 	unsigned int prefix_len = 128; /* happiness is a warm gcc */
 
 	cb->val.hlen = (afinfo->family == NFPROTO_IPV4) ? 32 : 128;
-	if (!xtables_strtoui(cb->arg, NULL, &prefix_len, 0, cb->val.hlen))
+	if (!xtables_strtoui(cb->arg, NULL, &prefix_len, 0, cb->val.hlen)) {
+		/* Is this mask expressed in full format? e.g. 255.255.255.0 */
+		if (xtopt_parse_mask(cb))
+			return;
+
 		xt_params->exit_err(PARAMETER_PROBLEM,
 			"%s: bad value for option \"--%s\", "
-			"or out of range (%u-%u).\n",
+			"neither a valid network mask "
+			"nor valid CIDR (%u-%u).\n",
 			cb->ext_name, entry->name, 0, cb->val.hlen);
-
+	}
 	cb->val.hlen = prefix_len;
 }
 
@@ -757,13 +789,13 @@
 
 	for (i = 0; i < ARRAY_SIZE(cb->val.ethermac) - 1; ++i) {
 		cb->val.ethermac[i] = strtoul(arg, &end, 16);
-		if (cb->val.ethermac[i] > UINT8_MAX || *end != ':')
+		if (*end != ':' || end - arg > 2)
 			goto out;
 		arg = end + 1;
 	}
 	i = ARRAY_SIZE(cb->val.ethermac) - 1;
 	cb->val.ethermac[i] = strtoul(arg, &end, 16);
-	if (cb->val.ethermac[i] > UINT8_MAX || *end != '\0')
+	if (*end != '\0' || end - arg > 2)
 		goto out;
 	if (cb->entry->flags & XTOPT_PUT)
 		memcpy(XTOPT_MKPTR(cb), cb->val.ethermac,
@@ -826,6 +858,12 @@
 		xt_params->exit_err(PARAMETER_PROBLEM,
 			"%s: option \"--%s\" requires an argument.\n",
 			cb->ext_name, entry->name);
+	/*
+	 * Fill in fallback value for "nvals", in case an extension (as it
+	 * happened with libxt_conntrack.2) tries to read it, despite not using
+	 * a *RC option type.
+	 */
+	cb->nvals = 1;
 	if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
 	    xtopt_subparse[entry->type] != NULL)
 		xtopt_subparse[entry->type](cb);
@@ -847,8 +885,14 @@
 			xt_params->exit_err(OTHER_PROBLEM,
 				"Extension %s uses invalid ID %u\n",
 				name, entry->id);
-		if (!(entry->flags & XTOPT_PUT))
+		if (!(entry->flags & XTOPT_PUT)) {
+			if (entry->ptroff != 0)
+				xt_params->exit_err(OTHER_PROBLEM,
+					"%s: ptroff for \"--%s\" is non-"
+					"zero but no XTOPT_PUT is specified. "
+					"Oversight?", name, entry->name);
 			continue;
+		}
 		if (entry->type >= ARRAY_SIZE(xtopt_psize) ||
 		    xtopt_psize[entry->type] == 0)
 			xt_params->exit_err(OTHER_PROBLEM,
@@ -908,6 +952,7 @@
 	cb.xflags   = t->tflags;
 	cb.target   = &t->t;
 	cb.xt_entry = fw;
+	cb.udata    = t->udata;
 	t->x6_parse(&cb);
 	t->tflags = cb.xflags;
 }
@@ -943,6 +988,7 @@
 	cb.xflags   = m->mflags;
 	cb.match    = &m->m;
 	cb.xt_entry = fw;
+	cb.udata    = m->udata;
 	m->x6_parse(&cb);
 	m->mflags = cb.xflags;
 }
@@ -1028,6 +1074,7 @@
 		cb.ext_name = t->name;
 		cb.data     = t->t->data;
 		cb.xflags   = t->tflags;
+		cb.udata    = t->udata;
 		t->x6_fcheck(&cb);
 	} else if (t->final_check != NULL) {
 		t->final_check(t->tflags);
@@ -1048,6 +1095,7 @@
 		cb.ext_name = m->name;
 		cb.data     = m->m->data;
 		cb.xflags   = m->mflags;
+		cb.udata    = m->udata;
 		m->x6_fcheck(&cb);
 	} else if (m->final_check != NULL) {
 		m->final_check(m->mflags);
@@ -1123,6 +1171,7 @@
 	fclose(fp);
 	return lmap_head;
  out:
+	fclose(fp);
 	xtables_lmap_free(lmap_head);
 	return NULL;
 }
diff --git a/tests/options-most.rules b/tests/options-most.rules
index 6c4a831..ef4e7f1 100644
--- a/tests/options-most.rules
+++ b/tests/options-most.rules
@@ -1,4 +1,3 @@
-# Generated by ip6tables-save v1.4.10 on Mon Jan 31 02:19:53 2011
 *filter
 :INPUT ACCEPT [0:0]
 :FORWARD ACCEPT [0:0]
@@ -21,8 +20,8 @@
 -A INPUT -p tcp -m connmark --mark 0x99
 -A INPUT -p tcp -m conntrack --ctstate INVALID --ctproto 6 --ctorigsrc fe80::/64 --ctorigdst fe80::/64 --ctreplsrc fe80::/64 --ctrepldst fe80::/64 --ctorigsrcport 12 --ctorigdstport 13 --ctreplsrcport 14 --ctrepldstport 15 --ctstatus EXPECTED --ctexpire 1:2 --ctdir REPLY
 -A INPUT -p tcp -m cpu --cpu 2
--A INPUT -p tcp -m dscp --dscp 0x04
--A INPUT -p tcp -m dscp --dscp 0x00
+-A INPUT -p tcp -m dscp --dscp 0x04 -m dscp ! --dscp 0x04
+-A INPUT -p tcp -m dscp --dscp 0x00 -m dscp ! --dscp 0x00
 -A INPUT -p tcp -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-mode srcip,dstip --hashlimit-name f1 --hashlimit-htable-size 64 --hashlimit-htable-max 128 --hashlimit-htable-gcinterval 60 --hashlimit-htable-expire 120 --hashlimit-srcmask 24 --hashlimit-dstmask 24
 -A INPUT -p tcp -m hashlimit --hashlimit-above 5/sec --hashlimit-burst 5 --hashlimit-name f1
 -A INPUT -p tcp -m helper --helper ftp
@@ -38,14 +37,16 @@
 -A INPUT -p tcp -m recent --rcheck --name DEFAULT --rsource
 -A INPUT -p tcp -m socket --transparent
 -A INPUT -p tcp -m string --string "foobar" --algo kmp --from 1 --to 2 --icase
+-A INPUT -p tcp -m string --hex-string "|00|" --algo kmp --from 1 --to 2 --icase
 -A INPUT -p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN
 -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN
 -A INPUT -p tcp -m tos --tos 0xff/0x01
--A INPUT -p tcp -m u32 --u32 "0x0=0x0" -m u32 --u32 "0x0=0x0"
+-A INPUT -p tcp -m u32 ! --u32 "0x0=0x0" -m u32 ! --u32 "0x0=0x0"
 -A INPUT -p tcp -m hbh -m hbh -m hl --hl-eq 1 -m ipv6header --header hop-by-hop --soft
 -A INPUT -m ipv6header --header hop-by-hop --soft -m rt --rt-type 2 --rt-segsleft 2 --rt-len 5 -m rt --rt-type 0 --rt-segsleft 2 --rt-len 5 --rt-0-res --rt-0-addrs ::1 --rt-0-not-strict -m rt --rt-type 0 --rt-segsleft 2 --rt-len 5 --rt-0-res --rt-0-addrs ::1,::2 --rt-0-not-strict
 -A INPUT -p tcp -m cpu --cpu 1 -m tcp --sport 1:2 --dport 1:2 --tcp-option 1 --tcp-flags FIN,SYN,RST,ACK SYN -m cpu --cpu 1
 -A INPUT -p dccp -m cpu --cpu 1 -m dccp --sport 1:2 --dport 3:4 -m cpu --cpu 1
+-A INPUT -p dccp -m dccp ! --sport 1:2 ! --dport 3:4 ! --dccp-types REQUEST,RESPONSE ! --dccp-option 1
 -A INPUT -p udp -m cpu --cpu 1 -m udp --sport 1:2 --dport 3:4 -m cpu --cpu 1
 -A INPUT -p sctp -m cpu --cpu 1 -m sctp --sport 1:2 --dport 3:4 --chunk-types all INIT,SACK -m cpu --cpu 1
 -A INPUT -p esp -m esp --espspi 1:2
@@ -53,9 +54,9 @@
 -A INPUT -p tcp -m tcpmss --mss 1:2 -m tcp --tcp-flags FIN,SYN,RST,ACK SYN
 -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 4/0
 -A INPUT
--A INPUT -p mobility
--A INPUT -p mobility -m mh --mh-type 3
+-A INPUT -p ipv6-mh -m mh --mh-type 3
 -A OUTPUT -m owner --socket-exists --uid-owner 1-2 --gid-owner 2-3
+-A OUTPUT -m owner ! --socket-exists ! --uid-owner 0 ! --gid-owner 0
 -A matches -m connbytes --connbytes 1 --connbytes-mode bytes --connbytes-dir both
 -A matches
 -A matches -m connbytes --connbytes :2 --connbytes-mode bytes --connbytes-dir both
@@ -76,6 +77,10 @@
 -A matches
 -A matches -m conntrack --ctexpire 5:4294967295
 -A matches
+-A matches -m conntrack ! --ctstate NEW ! --ctproto tcp ! --ctorigsrc ::1/127 ! --ctorigdst ::2/127 ! --ctreplsrc ::2/127 ! --ctrepldst ::2/127 ! --ctorigsrcport 3 ! --ctorigdstport 4 ! --ctreplsrcport 5 ! --ctrepldstport 6 ! --ctstatus ASSURED ! --ctexpire 8:9
+-A matches
+-A matches -m dst ! --dst-len 12
+-A matches
 -A matches -p esp -m esp --espspi 1
 -A matches
 -A matches -p esp -m esp --espspi :2
@@ -86,6 +91,17 @@
 -A matches
 -A matches -p esp -m esp --espspi 5:4294967295
 -A matches
+-A matches -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-name mini1 --hashlimit-htable-expire 2000
+-A matches -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 1 --hashlimit-name mini1
+-A matches -m hashlimit --hashlimit-upto 1/min --hashlimit-burst 1 --hashlimit-name mini2
+-A matches -m hashlimit --hashlimit-upto 1/hour --hashlimit-burst 1 --hashlimit-name mini3
+-A matches -m hashlimit --hashlimit-upto 1/day --hashlimit-burst 1 --hashlimit-name mini4
+-A matches -m hashlimit --hashlimit-upto 4kb/s --hashlimit-burst 400kb --hashlimit-name mini5
+-A matches -m hashlimit --hashlimit-upto 10mb/s --hashlimit-name mini6
+-A matches -m hashlimit --hashlimit-upto 123456b/s --hashlimit-burst 1mb --hashlimit-name mini7
+-A matches
+-A matches -m hbh ! --hbh-len 5
+-A matches
 -A matches -m ipvs --vaddr fe80::/64 --vport 1 --vdir REPLY --vmethod GATE --vportctl 21
 -A matches
 -A matches -m length --length 1
@@ -98,6 +114,8 @@
 -A matches
 -A matches -m length --length 5:65535
 -A matches
+-A matches -m physdev ! --physdev-is-in ! --physdev-is-out ! --physdev-is-bridged
+-A matches
 -A matches -p tcp -m tcpmss --mss 1
 -A matches
 -A matches -p tcp -m tcpmss --mss :2
@@ -108,6 +126,14 @@
 -A matches
 -A matches -p tcp -m tcpmss --mss 5:65535
 -A matches
+-A matches -m statistic --mode random ! --probability 0.4
+-A matches
+-A matches -m statistic --mode nth ! --every 5 --packet 2
+-A matches
+-A matches -m string --hex-string "action=|5C22|http|3A|" --algo bm
+-A matches
+-A matches -m string --hex-string "action=|5C|http|3A|" --algo bm
+-A matches
 -A matches -m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --localtz
 -A matches
 -A matches -m time --timestart 01:02:03 --timestop 04:05:06 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --kerneltz
@@ -136,6 +162,8 @@
 -A matches
 -A matches -m frag --fragid 5:4294967295
 -A matches
+-A matches -m frag ! --fragid 9:10 ! --fraglen 12
+-A matches
 -A matches -m rt --rt-segsleft 1
 -A matches
 -A matches -m rt --rt-segsleft :2
@@ -146,27 +174,41 @@
 -A matches
 -A matches -m rt --rt-segsleft 5:4294967295
 -A matches
+-A ntarg -j LOG --log-tcp-sequence --log-tcp-options --log-ip-options
+-A ntarg
 -A ntarg -j NFQUEUE --queue-num 1
 -A ntarg
 -A ntarg -j NFQUEUE --queue-balance 8:99
 -A ntarg
+-A ntarg -j NFQUEUE --queue-num 0 --queue-bypass
+-A ntarg
 -A ntarg -j RATEEST --rateest-name RE1 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
 -A ntarg
 -A ntarg -j RATEEST --rateest-name RE2 --rateest-interval 250.0ms --rateest-ewmalog 500.0ms
 -A ntarg
-#-A zmatches -m rateest --rateest RE1 --rateest-lt --rateest-bps 8bit
-#-A zmatches -m rateest --rateest RE1 --rateest-eq --rateest-bps 8bit
-#-A zmatches -m rateest --rateest RE1 --rateest-gt --rateest-bps 8bit
-#-A zmatches -m rateest --rateest RE1 --rateest-lt --rateest-pps 5
-#-A zmatches -m rateest --rateest RE1 --rateest-eq --rateest-pps 5
-#-A zmatches -m rateest --rateest RE1 --rateest-gt --rateest-pps 5
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-lt --rateest-bps2 16bit
-#-A zmatches -m rateest --rateest1 RE1 --rateest-lt --rateest2 RE2 --bytes
-#-A zmatches -m rateest --rateest1 RE1 --rateest-lt --rateest2 RE2 --packets
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-eq --rateest-bps2 16bit
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-gt --rateest-bps2 16bit
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-lt --rateest-pps2 9
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-eq --rateest-pps2 9
-#-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-gt --rateest-pps2 9
+-A zmatches -m rateest --rateest RE1 --rateest-lt --rateest-bps 8bit
+-A zmatches -m rateest --rateest RE1 --rateest-eq --rateest-pps 5
+-A zmatches -m rateest --rateest RE1 --rateest-gt --rateest-bps 5kbit
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-lt --rateest-bps2 16bit
+-A zmatches -m rateest --rateest1 RE1 --rateest-lt --rateest-bps --rateest2 RE2
+-A zmatches -m rateest --rateest-delta --rateest1 RE1 --rateest-lt --rateest2 RE2 --rateest-pps2 42
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-eq --rateest-bps2 16bit
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-bps1 8bit --rateest-gt --rateest-bps2 16bit
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-lt --rateest-pps2 9
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-eq --rateest-pps2 9
+-A zmatches -m rateest --rateest-delta --rateest RE1 --rateest-pps1 8 --rateest-gt --rateest-pps2 9
 COMMIT
-# Completed on Mon Jan 31 02:19:54 2011
+*mangle
+:PREROUTING ACCEPT [0:0]
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+:POSTROUTING ACCEPT [0:0]
+:matches - -
+:ntarg - -
+:zmatches - -
+-A INPUT -m u32 --u32 "0x0=0x0&&0x0=0x1" -j ntarg
+-A ntarg -j HL --hl-inc 1
+-A ntarg -j HL --hl-dec 1
+-A ntarg
+COMMIT
diff --git a/utils/.gitignore b/utils/.gitignore
index ccfd2ec..216d1e4 100644
--- a/utils/.gitignore
+++ b/utils/.gitignore
@@ -1 +1,2 @@
 /nfnl_osf
+/nfbpf_compile
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 306d993..c26aa64 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,9 +1,20 @@
 # -*- Makefile -*-
 
 AM_CFLAGS = ${regular_CFLAGS}
-AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include \
+              -I${top_srcdir}/include ${libnfnetlink_CFLAGS}
 
-sbin_PROGRAMS = nfnl_osf
-pkgdata_DATA = pf.os
+sbin_PROGRAMS =
+pkgdata_DATA =
 
-nfnl_osf_LDADD = -lnfnetlink
+if HAVE_LIBNFNETLINK
+sbin_PROGRAMS += nfnl_osf
+pkgdata_DATA += pf.os
+
+nfnl_osf_LDADD = ${libnfnetlink_LIBS}
+endif
+
+if ENABLE_BPFC
+sbin_PROGRAMS += nfbpf_compile
+nfbpf_compile_LDADD = -lpcap
+endif
diff --git a/utils/nfbpf_compile.c b/utils/nfbpf_compile.c
new file mode 100644
index 0000000..2c46c7b
--- /dev/null
+++ b/utils/nfbpf_compile.c
@@ -0,0 +1,55 @@
+/*
+ * BPF program compilation tool
+ *
+ * Generates decimal output, similar to `tcpdump -ddd ...`.
+ * Unlike tcpdump, will generate for any given link layer type.
+ *
+ * Written by Willem de Bruijn (willemb@google.com)
+ * Copyright Google, Inc. 2013
+ * Licensed under the GNU General Public License version 2 (GPLv2)
+*/
+
+#include <pcap.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+	struct bpf_program program;
+	struct bpf_insn *ins;
+	int i, dlt = DLT_RAW;
+
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "Usage:    %s [link] '<program>'\n\n"
+				"          link is a pcap linklayer type:\n"
+				"          one of EN10MB, RAW, SLIP, ...\n\n"
+				"Examples: %s RAW 'tcp and greater 100'\n"
+				"          %s EN10MB 'ip proto 47'\n'",
+				argv[0], argv[0], argv[0]);
+		return 1;
+	}
+
+	if (argc == 3) {
+		dlt = pcap_datalink_name_to_val(argv[1]);
+		if (dlt == -1) {
+			fprintf(stderr, "Unknown datalinktype: %s\n", argv[1]);
+			return 1;
+		}
+	}
+
+	if (pcap_compile_nopcap(65535, dlt, &program, argv[argc - 1], 1,
+				PCAP_NETMASK_UNKNOWN)) {
+		fprintf(stderr, "Compilation error\n");
+		return 1;
+	}
+
+	printf("%d,", program.bf_len);
+	ins = program.bf_insns;
+	for (i = 0; i < program.bf_len-1; ++ins, ++i)
+		printf("%u %u %u %u,", ins->code, ins->jt, ins->jf, ins->k);
+
+	printf("%u %u %u %u\n", ins->code, ins->jt, ins->jf, ins->k);
+
+	pcap_freecode(&program);
+	return 0;
+}
+