Project import generated by Copybara.

NOKEYCHECK=True
GitOrigin-RevId: 482c6d3731e2681cb4baae835c294840300197e6
diff --git a/COMMIT_NOTES b/COMMIT_NOTES
new file mode 100644
index 0000000..592808c
--- /dev/null
+++ b/COMMIT_NOTES
@@ -0,0 +1,19 @@
+A quick list of rules for committing stuff into netfilter git:
+
+- Always add an appropriate description, in git format
+  (i.e. first line is a summary)
+
+- Please try to include references to bugs when the description does not
+  include total discussion coverage or when the bug report is external to
+  netfilter-devel, e.g.
+  "Closes: netfilter bugzilla #123", or
+  "Reference: http://bugs.{debian,gentoo}.org/..."
+
+- If you touch any parts of libxtables (xtables.c, include/xtables.h.in),
+  make sure the so-version is updated _appropriately_ (i.e. read the
+  libtool manual about Versioning:: first, if need be) in configure.ac.
+  Adding fields to a struct always entails a vcurrent bump.
+
+  - Check, whether a bump (vcurrent,vage) has already been made since the
+    last release (no more than one per release), e.g.:
+            git log v1.4.4.. configure.ac
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/INCOMPATIBILITIES b/INCOMPATIBILITIES
new file mode 100644
index 0000000..ddb2408
--- /dev/null
+++ b/INCOMPATIBILITIES
@@ -0,0 +1,14 @@
+INCOMPATIBILITIES:
+
+- The REJECT target has an '--reject-with admin-prohib' option which used
+  with kernels that do not support it, will result in a plain DROP instead
+  of REJECT.  Use with caution.
+  Kernels that do support it:
+  	2.4 - since 2.4.22-pre9
+	2.6 - all
+
+- There are some issues related to upgrading from 1.2.x to 1.3.x on a system
+  with dynamic ruleset changes during runtime. (Please see 
+  https://bugzilla.netfilter.org/bugzilla/show_bug.cgi?id=334).
+  After upgrading from 1.2 to 1.3, it suggest go do an iptables-save, then
+  iptables-restore to ensure your dynamic rule changes continue to work.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..d62b428
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,101 @@
+Installation instructions for iptables
+======================================
+
+iptables uses the well-known configure(autotools) infrastructure.
+
+	$ ./configure
+	$ make
+	# make install
+
+
+Prerequisites
+=============
+
+	* no kernel-source required
+
+	* but obviously a compiler, glibc-devel and linux-kernel-headers
+	  (/usr/include/linux)
+
+
+Configuring and compiling
+=========================
+
+./configure [options]
+
+--prefix=
+
+	The prefix to put all installed files under. It defaults to
+	/usr/local, so the binaries will go into /usr/local/bin, sbin,
+	manpages into /usr/local/share/man, etc.
+
+--with-xtlibdir=
+
+	The path to where Xtables extensions should be installed to. It
+	defaults to ${libdir}/xtables.
+
+--enable-devel (or --disable-devel)
+
+	This option causes development files to be installed to
+	${includedir}, which is needed for building additional packages,
+	such as Xtables-addons or other 3rd-party extensions.
+
+	It is enabled by default.
+
+--enable-static
+
+	Produce additional binaries, iptables-static/ip6tables-static,
+	which have all shipped extensions compiled in.
+
+--disable-shared
+
+	Produce binaries that have dynamic loading of extensions disabled.
+	This implies --enable-static.
+	(See some details below.)
+
+--enable-libipq
+
+	This option causes libipq to be installed into ${libdir} and
+	${includedir}.
+
+--with-ksource=
+
+	Xtables does not depend on kernel headers anymore, but you can
+	optionally specify a search path to include anyway. This is
+	probably only useful for development.
+
+If you want to enable debugging, use
+
+	./configure CFLAGS="-ggdb3 -O0"
+
+(-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
+===========
+
+The make process will automatically build multipurpose binaries.
+These have the core (iptables), -save, -restore and -xml code
+compiled into one binary, but extensions remain as modules.
+
+
+Static and shared
+=================
+
+Basically there are three configuration modes defined:
+
+ --disable-static --enable-shared (this is the default)
+
+	Build a binary that relies upon dynamic loading of extensions.
+
+ --enable-static --enable-shared
+
+	Build a binary that has the shipped extensions built-in, but
+	is still capable of loading additional extensions.
+
+ --enable-static --disable-shared
+
+	Shipped extensions are built-in, and dynamic loading is
+	deactivated.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..c38d360
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,28 @@
+# -*- Makefile -*-
+
+ACLOCAL_AMFLAGS  = -I m4
+AUTOMAKE_OPTIONS = foreign subdir-objects
+
+SUBDIRS          = libiptc libxtables
+if ENABLE_DEVEL
+SUBDIRS         += include
+endif
+if ENABLE_LIBIPQ
+SUBDIRS         += libipq
+endif
+SUBDIRS         += utils
+# Depends on libxtables:
+SUBDIRS         += extensions
+# Depends on extensions/libext.a:
+SUBDIRS         += iptables
+
+.PHONY: tarball
+tarball:
+	rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
+	pushd ${top_srcdir} && git archive --prefix=${PACKAGE_TARNAME}-${PACKAGE_VERSION}/ HEAD | tar -C /tmp -x && popd;
+	pushd /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION} && ./autogen.sh && popd;
+	tar -C /tmp -cjf ${PACKAGE_TARNAME}-${PACKAGE_VERSION}.tar.bz2 --owner=root --group=root ${PACKAGE_TARNAME}-${PACKAGE_VERSION}/;
+	rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
+
+config.status: extensions/GNUmakefile.in \
+	include/xtables-version.h.in include/iptables/internal.h.in
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..a0c4395
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+autoreconf -fi;
+rm -Rf autom4te*.cache;
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..e83304c
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,204 @@
+
+AC_INIT([iptables], [1.4.21])
+
+# See libtool.info "Libtool's versioning system"
+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
+AM_INIT_AUTOMAKE([-Wall])
+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],
+	AS_HELP_STRING([--with-kernel=PATH],
+	[Path to kernel source/build directory]),
+	[kbuilddir="$withval"; ksourcedir="$withval";])
+AC_ARG_WITH([kbuild],
+	AS_HELP_STRING([--with-kbuild=PATH],
+	[Path to kernel build directory [[/lib/modules/CURRENT/build]]]),
+	[kbuilddir="$withval"])
+AC_ARG_WITH([ksource],
+	AS_HELP_STRING([--with-ksource=PATH],
+	[Path to kernel source directory [[/lib/modules/CURRENT/source]]]),
+	[ksourcedir="$withval"])
+AC_ARG_WITH([xtlibdir],
+	AS_HELP_STRING([--with-xtlibdir=PATH],
+	[Path where to install Xtables extensions [[LIBEXECDIR/xtables]]]),
+	[xtlibdir="$withval"],
+	[xtlibdir="${libdir}/xtables"])
+AC_ARG_ENABLE([ipv4],
+	AS_HELP_STRING([--disable-ipv4], [Do not build iptables]),
+	[enable_ipv4="$enableval"], [enable_ipv4="yes"])
+AC_ARG_ENABLE([ipv6],
+	AS_HELP_STRING([--disable-ipv6], [Do not build ip6tables]),
+	[enable_ipv6="$enableval"], [enable_ipv6="yes"])
+AC_ARG_ENABLE([largefile],
+	AS_HELP_STRING([--disable-largefile], [Do not build largefile support]),
+	[enable_largefile="$enableval"],
+	[enable_largefile="yes";
+	largefile_cppflags='-D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64'])
+AC_ARG_ENABLE([devel],
+	AS_HELP_STRING([--enable-devel],
+	[Install Xtables development headers]),
+	[enable_devel="$enableval"], [enable_devel="yes"])
+AC_ARG_ENABLE([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_ENABLE([nfsynproxy],
+	AS_HELP_STRING([--enable-nfsynproxy], [Build SYNPROXY configuration tool]),
+	[enable_nfsynproxy="yes"], [enable_nfsynproxy="no"])
+AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
+	[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
+	[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+
+libiptc_LDFLAGS2="";
+AX_CHECK_LINKER_FLAGS([-Wl,--no-as-needed],
+	[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_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;
+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"])
+AM_CONDITIONAL([ENABLE_IPV4], [test "$enable_ipv4" = "yes"])
+AM_CONDITIONAL([ENABLE_IPV6], [test "$enable_ipv6" = "yes"])
+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"])
+AM_CONDITIONAL([ENABLE_SYNCONF], [test "$enable_nfsynproxy" = "yes"])
+
+if test "x$enable_bpfc" = "xyes" || test "x$enable_nfsynproxy" = "xyes"; then
+	AC_CHECK_LIB(pcap, pcap_compile,, AC_MSG_ERROR(missing libpcap library required by bpf compiler or nfsynproxy tool))
+fi
+
+PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
+	[nfnetlink=1], [nfnetlink=0])
+AM_CONDITIONAL([HAVE_LIBNFNETLINK], [test "$nfnetlink" = 1])
+
+regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
+	-Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
+	-Winline -pipe";
+regular_CPPFLAGS="${largefile_cppflags} -D_REENTRANT \
+	-DXTABLES_LIBDIR=\\\"\${xtlibdir}\\\" -DXTABLES_INTERNAL";
+kinclude_CPPFLAGS="";
+if [[ -n "$kbuilddir" ]]; then
+	kinclude_CPPFLAGS="$kinclude_CPPFLAGS -I$kbuilddir/include/uapi -I$kbuilddir/include";
+fi;
+if [[ -n "$ksourcedir" ]]; then
+	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])
+AC_SUBST([xtlibdir])
+AC_SUBST([pkgconfigdir])
+AC_SUBST([pkgdatadir])
+AC_SUBST([libxtables_vcurrent])
+AC_SUBST([libxtables_vage])
+libxtables_vmajor=$(($libxtables_vcurrent - $libxtables_vage));
+AC_SUBST([libxtables_vmajor])
+
+AC_CONFIG_FILES([Makefile extensions/GNUmakefile include/Makefile
+	iptables/Makefile iptables/xtables.pc
+	iptables/iptables.8 iptables/iptables-extensions.8.tmpl
+	iptables/iptables-save.8 iptables/iptables-restore.8
+	iptables/iptables-apply.8 iptables/iptables-xml.1
+	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}
+  nfsynproxy util support:		${enable_nfsynproxy}
+
+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
new file mode 100644
index 0000000..c5d8844
--- /dev/null
+++ b/extensions/GNUmakefile.in
@@ -0,0 +1,228 @@
+# -*- 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@
+
+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_builddir} -I${top_srcdir}/include ${kinclude_CPPFLAGS} ${CPPFLAGS}
+AM_DEPFLAGS     = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@
+AM_LDFLAGS      = @noundef_LDFLAGS@
+
+ifeq (${V},)
+AM_LIBTOOL_SILENT = --silent
+AM_VERBOSE_CC     = @echo "  CC      " $@;
+AM_VERBOSE_CCLD   = @echo "  CCLD    " $@;
+AM_VERBOSE_CXX    = @echo "  CXX     " $@;
+AM_VERBOSE_CXXLD  = @echo "  CXXLD   " $@;
+AM_VERBOSE_AR     = @echo "  AR      " $@;
+AM_VERBOSE_GEN    = @echo "  GEN     " $@;
+endif
+
+#
+#	Wildcard module list
+#
+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_symlinks})
+pf4_solibs    := $(patsubst %,libipt_%.so,${pf4_build_mod})
+pf6_solibs    := $(patsubst %,libip6t_%.so,${pf6_build_mod})
+
+
+#
+# Building blocks
+#
+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}
+@ENABLE_STATIC_TRUE@ libext6_objs := ${pf6_objs}
+@ENABLE_STATIC_FALSE@ targets += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs}
+@ENABLE_STATIC_FALSE@ targets_install += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs}
+
+.SECONDARY:
+
+.PHONY: all install clean distclean FORCE
+
+all: ${targets}
+
+install: ${targets_install}
+	@mkdir -p "${DESTDIR}${xtlibdir}";
+	if test -n "${targets_install}"; then install -pm0755 $^ "${DESTDIR}${xtlibdir}/"; fi;
+
+clean:
+	rm -f *.o *.oo *.so *.a {matches,targets}.man initext.c initext4.c initext6.c;
+	rm -f .*.d .*.dd;
+
+distclean: clean
+
+init%.o: init%.c
+	${AM_VERBOSE_CC} ${CC} ${AM_CPPFLAGS} ${AM_DEPFLAGS} ${AM_CFLAGS} -D_INIT=$*_init ${CFLAGS} -o $@ -c $<;
+
+-include .*.d
+
+
+#
+#	Shared libraries
+#
+lib%.so: lib%.oo
+	${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} ${$*_CFLAGADD} -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
+@HAVE_LIBNETFILTER_CONNTRACK_TRUE@xt_connlabel_LIBADD = @libnetfilter_conntrack_LIBS@
+
+@HAVE_LIBNETFILTER_CONNTRACK_TRUE@xt_connlabel_CFLAGADD = @libnetfilter_conntrack_CFLAGS@
+
+#
+#	Static bits
+#
+#	If static building is disabled, libext*.a will still be generated,
+#	but will be empty. This is good since we can do with less case
+#	handling code in the Makefiles.
+#
+lib%.o: ${srcdir}/lib%.c
+	${AM_VERBOSE_CC} ${CC} ${AM_CPPFLAGS} ${AM_DEPFLAGS} ${AM_CFLAGS} -DNO_SHARED_LIBS=1 -D_INIT=lib$*_init ${CFLAGS} -o $@ -c $<;
+
+libext.a: initext.o ${libext_objs}
+	${AM_VERBOSE_AR} ${AR} crs $@ $^;
+
+libext4.a: initext4.o ${libext4_objs}
+	${AM_VERBOSE_AR} ${AR} crs $@ $^;
+
+libext6.a: initext6.o ${libext6_objs}
+	${AM_VERBOSE_AR} ${AR} crs $@ $^;
+
+initext_func  := $(addprefix xt_,${pfx_build_mod})
+initext4_func := $(addprefix ipt_,${pf4_build_mod})
+initext6_func := $(addprefix ip6t_,${pf6_build_mod})
+
+.initext.dd: FORCE
+	@echo "${initext_func}" >$@.tmp; \
+	cmp -s $@ $@.tmp || mv $@.tmp $@; \
+	rm -f $@.tmp;
+
+.initext4.dd: FORCE
+	@echo "${initext4_func}" >$@.tmp; \
+	cmp -s $@ $@.tmp || mv $@.tmp $@; \
+	rm -f $@.tmp;
+
+.initext6.dd: FORCE
+	@echo "${initext6_func}" >$@.tmp; \
+	cmp -s $@ $@.tmp || mv $@.tmp $@; \
+	rm -f $@.tmp;
+
+initext.c: .initext.dd
+	${AM_VERBOSE_GEN}
+	@( \
+	echo "" >$@; \
+	for i in ${initext_func}; do \
+		echo "extern void lib$${i}_init(void);" >>$@; \
+	done; \
+	echo "void init_extensions(void);" >>$@; \
+	echo "void init_extensions(void)" >>$@; \
+	echo "{" >>$@; \
+	for i in ${initext_func}; do \
+		echo  " ""lib$${i}_init();" >>$@; \
+	done; \
+	echo "}" >>$@; \
+	);
+
+initext4.c: .initext4.dd
+	${AM_VERBOSE_GEN}
+	@( \
+	echo "" >$@; \
+	for i in ${initext4_func}; do \
+		echo "extern void lib$${i}_init(void);" >>$@; \
+	done; \
+	echo "void init_extensions4(void);" >>$@; \
+	echo "void init_extensions4(void)" >>$@; \
+	echo "{" >>$@; \
+	for i in ${initext4_func}; do \
+		echo  " ""lib$${i}_init();" >>$@; \
+	done; \
+	echo "}" >>$@; \
+	);
+
+initext6.c: .initext6.dd
+	${AM_VERBOSE_GEN}
+	@( \
+	echo "" >$@; \
+	for i in ${initext6_func}; do \
+		echo "extern void lib$${i}_init(void);" >>$@; \
+	done; \
+	echo "void init_extensions6(void);" >>$@; \
+	echo "void init_extensions6(void)" >>$@; \
+	echo "{" >>$@; \
+	for i in ${initext6_func}; do \
+		echo " ""lib$${i}_init();" >>$@; \
+	done; \
+	echo "}" >>$@; \
+	);
+
+#
+#	Manual pages
+#
+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 $(sort ${1}); do \
+		f="${srcdir}/libxt_$$ext.man"; \
+		if [ -f "$$f" ]; then \
+			echo -e "\t+ $$f" >&2; \
+			echo ".SS $$ext"; \
+			cat "$$f" || exit $$?; \
+		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 >$@;
+
+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}))
+
+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/dscp_helper.c b/extensions/dscp_helper.c
new file mode 100644
index 0000000..75b1fec
--- /dev/null
+++ b/extensions/dscp_helper.c
@@ -0,0 +1,79 @@
+/*
+ * DiffServ classname <-> DiffServ codepoint mapping functions.
+ *
+ * The latest list of the mappings can be found at:
+ * <http://www.iana.org/assignments/dscp-registry>
+ *
+ * This code is released under the GNU GPL v2, 1991
+ *
+ * Author: Iain Barnes
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+
+
+static const struct ds_class
+{
+	const char *name;
+	unsigned int dscp;
+} ds_classes[] =
+{
+	{ "CS0", 0x00 },
+	{ "CS1", 0x08 },
+	{ "CS2", 0x10 },
+	{ "CS3", 0x18 },
+	{ "CS4", 0x20 },
+	{ "CS5", 0x28 },
+	{ "CS6", 0x30 },
+	{ "CS7", 0x38 },
+	{ "BE", 0x00 },
+	{ "AF11", 0x0a },
+	{ "AF12", 0x0c },
+	{ "AF13", 0x0e },
+	{ "AF21", 0x12 },
+	{ "AF22", 0x14 },
+	{ "AF23", 0x16 },
+	{ "AF31", 0x1a },
+	{ "AF32", 0x1c },
+	{ "AF33", 0x1e },
+	{ "AF41", 0x22 },
+	{ "AF42", 0x24 },
+	{ "AF43", 0x26 },
+	{ "EF", 0x2e }
+};
+
+
+
+static unsigned int
+class_to_dscp(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ds_classes); i++) {
+		if (!strncasecmp(name, ds_classes[i].name,
+					strlen(ds_classes[i].name)))
+			return ds_classes[i].dscp;
+	}
+
+	xtables_error(PARAMETER_PROBLEM,
+			"Invalid DSCP value `%s'\n", name);
+}
+
+
+#if 0
+static const char *
+dscp_to_name(unsigned int dscp)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ds_classes); ++i) 
+		if (dscp == ds_classes[i].dscp)
+			return ds_classes[i].name;
+
+	xtables_error(PARAMETER_PROBLEM,
+			"Invalid DSCP value `%d'\n", dscp);
+}
+#endif
+
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
new file mode 100644
index 0000000..52ca5d3
--- /dev/null
+++ b/extensions/libip6t_HL.c
@@ -0,0 +1,127 @@
+/*
+ * IPv6 Hop Limit Target module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl target
+ * This program is distributed under the terms of GNU GPL
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_HL.h>
+
+enum {
+	O_HL_SET = 0,
+	O_HL_INC,
+	O_HL_DEC,
+	F_HL_SET = 1 << O_HL_SET,
+	F_HL_INC = 1 << O_HL_INC,
+	F_HL_DEC = 1 << O_HL_DEC,
+	F_ANY    = F_HL_SET | F_HL_INC | F_HL_DEC,
+};
+
+#define s struct ip6t_HL_info
+static const struct xt_option_entry HL_opts[] = {
+	{.name = "hl-set", .type = XTTYPE_UINT8, .id = O_HL_SET,
+	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
+	{.name = "hl-dec", .type = XTTYPE_UINT8, .id = O_HL_DEC,
+	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit),
+	 .min = 1},
+	{.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,
+};
+#undef s
+
+static void HL_help(void)
+{
+	printf(
+"HL target options\n"
+"  --hl-set value		Set HL to <value 0-255>\n"
+"  --hl-dec value		Decrement HL by <value 1-255>\n"
+"  --hl-inc value		Increment HL by <value 1-255>\n");
+}
+
+static void HL_parse(struct xt_option_call *cb)
+{
+	struct ip6t_HL_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_HL_SET:
+		info->mode = IP6T_HL_SET;
+		break;
+	case O_HL_INC:
+		info->mode = IP6T_HL_INC;
+		break;
+	case O_HL_DEC:
+		info->mode = IP6T_HL_DEC;
+		break;
+	}
+}
+
+static void HL_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+				"HL: You must specify an action");
+}
+
+static void HL_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ip6t_HL_info *info = 
+		(struct ip6t_HL_info *) target->data;
+
+	switch (info->mode) {
+		case IP6T_HL_SET:
+			printf(" --hl-set");
+			break;
+		case IP6T_HL_DEC:
+			printf(" --hl-dec");
+			break;
+
+		case IP6T_HL_INC:
+			printf(" --hl-inc");
+			break;
+	}
+	printf(" %u", info->hop_limit);
+}
+
+static void HL_print(const void *ip, const struct xt_entry_target *target,
+                     int numeric)
+{
+	const struct ip6t_HL_info *info =
+		(struct ip6t_HL_info *) target->data;
+
+	printf(" HL ");
+	switch (info->mode) {
+		case IP6T_HL_SET:
+			printf("set to");
+			break;
+		case IP6T_HL_DEC:
+			printf("decrement by");
+			break;
+		case IP6T_HL_INC:
+			printf("increment by");
+			break;
+	}
+	printf(" %u", info->hop_limit);
+}
+
+static struct xtables_target hl_tg6_reg = {
+	.name 		= "HL",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_HL_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_HL_info)),
+	.help		= HL_help,
+	.print		= HL_print,
+	.save		= HL_save,
+	.x6_parse	= HL_parse,
+	.x6_fcheck	= HL_check,
+	.x6_options	= HL_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&hl_tg6_reg);
+}
diff --git a/extensions/libip6t_HL.man b/extensions/libip6t_HL.man
new file mode 100644
index 0000000..0f3afb1
--- /dev/null
+++ b/extensions/libip6t_HL.man
@@ -0,0 +1,17 @@
+This is used to modify the Hop Limit field in IPv6 header. The Hop Limit field
+is similar to what is known as TTL value in IPv4.  Setting or incrementing the
+Hop Limit field can potentially be very dangerous, so it should be avoided at
+any cost. This target is only valid in
+.B mangle
+table.
+.PP
+.B Don't ever set or increment the value on packets that leave your local network!
+.TP
+\fB\-\-hl\-set\fP \fIvalue\fP
+Set the Hop Limit to `value'.
+.TP
+\fB\-\-hl\-dec\fP \fIvalue\fP
+Decrement the Hop Limit `value' times.
+.TP
+\fB\-\-hl\-inc\fP \fIvalue\fP
+Increment the Hop Limit `value' times.
diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c
new file mode 100644
index 0000000..4639268
--- /dev/null
+++ b/extensions/libip6t_LOG.c
@@ -0,0 +1,186 @@
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_LOG.h>
+
+#ifndef IP6T_LOG_UID	/* Old kernel */
+#define IP6T_LOG_UID	0x08
+#undef  IP6T_LOG_MASK
+#define IP6T_LOG_MASK	0x0f
+#endif
+
+#define LOG_DEFAULT_LEVEL LOG_WARNING
+
+enum {
+	O_LOG_LEVEL = 0,
+	O_LOG_PREFIX,
+	O_LOG_TCPSEQ,
+	O_LOG_TCPOPTS,
+	O_LOG_IPOPTS,
+	O_LOG_UID,
+	O_LOG_MAC,
+};
+
+static void LOG_help(void)
+{
+	printf(
+"LOG target options:\n"
+" --log-level level		Level of logging (numeric or see syslog.conf)\n"
+" --log-prefix prefix		Prefix log messages with this prefix.\n"
+" --log-tcp-sequence		Log TCP sequence numbers.\n"
+" --log-tcp-options		Log TCP options.\n"
+" --log-ip-options		Log IP options.\n"
+" --log-uid			Log UID owning the local socket.\n"
+" --log-macdecode		Decode MAC addresses and protocol.\n");
+}
+
+#define s struct ip6t_log_info
+static const struct xt_option_entry LOG_opts[] = {
+	{.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, level)},
+	{.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1},
+	{.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE},
+	{.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE},
+	{.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE},
+	{.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE},
+	{.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void LOG_init(struct xt_entry_target *t)
+{
+	struct ip6t_log_info *loginfo = (struct ip6t_log_info *)t->data;
+
+	loginfo->level = LOG_DEFAULT_LEVEL;
+
+}
+
+struct ip6t_log_names {
+	const char *name;
+	unsigned int level;
+};
+
+static const struct ip6t_log_names ip6t_log_names[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },		/* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },		/* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static void LOG_parse(struct xt_option_call *cb)
+{
+	struct ip6t_log_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_LOG_PREFIX:
+		if (strchr(cb->arg, '\n') != NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Newlines not allowed in --log-prefix");
+		break;
+	case O_LOG_TCPSEQ:
+		info->logflags |= IP6T_LOG_TCPSEQ;
+		break;
+	case O_LOG_TCPOPTS:
+		info->logflags |= IP6T_LOG_TCPOPT;
+		break;
+	case O_LOG_IPOPTS:
+		info->logflags |= IP6T_LOG_IPOPT;
+		break;
+	case O_LOG_UID:
+		info->logflags |= IP6T_LOG_UID;
+		break;
+	case O_LOG_MAC:
+		info->logflags |= IP6T_LOG_MACDECODE;
+		break;
+	}
+}
+
+static void LOG_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct ip6t_log_info *loginfo
+		= (const struct ip6t_log_info *)target->data;
+	unsigned int i = 0;
+
+	printf(" LOG");
+	if (numeric)
+		printf(" flags %u level %u",
+		       loginfo->logflags, loginfo->level);
+	else {
+		for (i = 0; i < ARRAY_SIZE(ip6t_log_names); ++i)
+			if (loginfo->level == ip6t_log_names[i].level) {
+				printf(" level %s", ip6t_log_names[i].name);
+				break;
+			}
+		if (i == ARRAY_SIZE(ip6t_log_names))
+			printf(" UNKNOWN level %u", loginfo->level);
+		if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+			printf(" tcp-sequence");
+		if (loginfo->logflags & IP6T_LOG_TCPOPT)
+			printf(" tcp-options");
+		if (loginfo->logflags & IP6T_LOG_IPOPT)
+			printf(" ip-options");
+		if (loginfo->logflags & IP6T_LOG_UID)
+			printf(" uid");
+		if (loginfo->logflags & IP6T_LOG_MACDECODE)
+			printf(" macdecode");
+		if (loginfo->logflags & ~(IP6T_LOG_MASK))
+			printf(" unknown-flags");
+	}
+
+	if (strcmp(loginfo->prefix, "") != 0)
+		printf(" prefix \"%s\"", loginfo->prefix);
+}
+
+static void LOG_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ip6t_log_info *loginfo
+		= (const struct ip6t_log_info *)target->data;
+
+	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);
+
+	if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+		printf(" --log-tcp-sequence");
+	if (loginfo->logflags & IP6T_LOG_TCPOPT)
+		printf(" --log-tcp-options");
+	if (loginfo->logflags & IP6T_LOG_IPOPT)
+		printf(" --log-ip-options");
+	if (loginfo->logflags & IP6T_LOG_UID)
+		printf(" --log-uid");
+	if (loginfo->logflags & IP6T_LOG_MACDECODE)
+		printf(" --log-macdecode");
+}
+
+static struct xtables_target log_tg6_reg = {
+	.name          = "LOG",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV6,
+	.size          = XT_ALIGN(sizeof(struct ip6t_log_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct ip6t_log_info)),
+	.help          = LOG_help,
+	.init          = LOG_init,
+	.print         = LOG_print,
+	.save          = LOG_save,
+	.x6_parse      = LOG_parse,
+	.x6_options    = LOG_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&log_tg6_reg);
+}
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_REJECT.c b/extensions/libip6t_REJECT.c
new file mode 100644
index 0000000..8085321
--- /dev/null
+++ b/extensions/libip6t_REJECT.c
@@ -0,0 +1,140 @@
+/* Shared library add-on to ip6tables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * 
+ * ported to IPv6 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_REJECT.h>
+
+struct reject_names {
+	const char *name;
+	const char *alias;
+	enum ip6t_reject_with with;
+	const char *desc;
+};
+
+enum {
+	O_REJECT_WITH = 0,
+};
+
+static const struct reject_names reject_table[] = {
+	{"icmp6-no-route", "no-route",
+		IP6T_ICMP6_NO_ROUTE, "ICMPv6 no route"},
+	{"icmp6-adm-prohibited", "adm-prohibited",
+		IP6T_ICMP6_ADM_PROHIBITED, "ICMPv6 administratively prohibited"},
+#if 0
+	{"icmp6-not-neighbor", "not-neighbor"},
+		IP6T_ICMP6_NOT_NEIGHBOR, "ICMPv6 not a neighbor"},
+#endif
+	{"icmp6-addr-unreachable", "addr-unreach",
+		IP6T_ICMP6_ADDR_UNREACH, "ICMPv6 address unreachable"},
+	{"icmp6-port-unreachable", "port-unreach",
+		IP6T_ICMP6_PORT_UNREACH, "ICMPv6 port unreachable"},
+	{"tcp-reset", "tcp-reset",
+		IP6T_TCP_RESET, "TCP RST packet"}
+};
+
+static void
+print_reject_types(void)
+{
+	unsigned int i;
+
+	printf("Valid reject types:\n");
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i) {
+		printf("    %-25s\t%s\n", reject_table[i].name, reject_table[i].desc);
+		printf("    %-25s\talias\n", reject_table[i].alias);
+	}
+	printf("\n");
+}
+
+static void REJECT_help(void)
+{
+	printf(
+"REJECT target options:\n"
+"--reject-with type              drop input packet and send back\n"
+"                                a reply packet according to type:\n");
+
+	print_reject_types();
+}
+
+static const struct xt_option_entry REJECT_opts[] = {
+	{.name = "reject-with", .id = O_REJECT_WITH, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static void REJECT_init(struct xt_entry_target *t)
+{
+	struct ip6t_reject_info *reject = (struct ip6t_reject_info *)t->data;
+
+	/* default */
+	reject->with = IP6T_ICMP6_PORT_UNREACH;
+
+}
+
+static void REJECT_parse(struct xt_option_call *cb)
+{
+	struct ip6t_reject_info *reject = cb->data;
+	unsigned int i;
+
+	xtables_option_parse(cb);
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (strncasecmp(reject_table[i].name,
+		      cb->arg, strlen(cb->arg)) == 0 ||
+		    strncasecmp(reject_table[i].alias,
+		      cb->arg, strlen(cb->arg)) == 0) {
+			reject->with = reject_table[i].with;
+			return;
+		}
+	xtables_error(PARAMETER_PROBLEM,
+		"unknown reject type \"%s\"", cb->arg);
+}
+
+static void REJECT_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct ip6t_reject_info *reject
+		= (const struct ip6t_reject_info *)target->data;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (reject_table[i].with == reject->with)
+			break;
+	printf(" reject-with %s", reject_table[i].name);
+}
+
+static void REJECT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ip6t_reject_info *reject
+		= (const struct ip6t_reject_info *)target->data;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (reject_table[i].with == reject->with)
+			break;
+
+	printf(" --reject-with %s", reject_table[i].name);
+}
+
+static struct xtables_target reject_tg6_reg = {
+	.name = "REJECT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size 		= XT_ALIGN(sizeof(struct ip6t_reject_info)),
+	.userspacesize 	= XT_ALIGN(sizeof(struct ip6t_reject_info)),
+	.help		= REJECT_help,
+	.init		= REJECT_init,
+	.print		= REJECT_print,
+	.save		= REJECT_save,
+	.x6_parse	= REJECT_parse,
+	.x6_options	= REJECT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&reject_tg6_reg);
+}
diff --git a/extensions/libip6t_REJECT.man b/extensions/libip6t_REJECT.man
new file mode 100644
index 0000000..0030a51
--- /dev/null
+++ b/extensions/libip6t_REJECT.man
@@ -0,0 +1,32 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to 
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+\fB\-\-reject\-with\fP \fItype\fP
+The type given can be
+\fBicmp6\-no\-route\fP,
+\fBno\-route\fP,
+\fBicmp6\-adm\-prohibited\fP,
+\fBadm\-prohibited\fP,
+\fBicmp6\-addr\-unreachable\fP,
+\fBaddr\-unreach\fP, or
+\fBicmp6\-port\-unreachable\fP,
+which return the appropriate ICMPv6 error message (\fBicmp6\-port\-unreachable\fP is
+the default). Finally, the option
+\fBtcp\-reset\fP
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+\fBtcp\-reset\fP
+can only be used with kernel versions 2.6.14 or later.
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_ah.c b/extensions/libip6t_ah.c
new file mode 100644
index 0000000..26f8140
--- /dev/null
+++ b/extensions/libip6t_ah.c
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_ah.h>
+
+enum {
+	O_AHSPI = 0,
+	O_AHLEN,
+	O_AHRES,
+};
+
+static void ah_help(void)
+{
+	printf(
+"ah match options:\n"
+"[!] --ahspi spi[:spi]          match spi (range)\n"
+"[!] --ahlen length             total length of this header\n"
+" --ahres                       check the reserved field too\n");
+}
+
+#define s struct ip6t_ah
+static const struct xt_option_entry ah_opts[] = {
+	{.name = "ahspi", .id = O_AHSPI, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spis)},
+	{.name = "ahlen", .id = O_AHLEN, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, hdrlen)},
+	{.name = "ahres", .id = O_AHRES, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void ah_parse(struct xt_option_call *cb)
+{
+	struct ip6t_ah *ahinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_AHSPI:
+		if (cb->nvals == 1)
+			ahinfo->spis[1] = ahinfo->spis[0];
+		if (cb->invert)
+			ahinfo->invflags |= IP6T_AH_INV_SPI;
+		break;
+	case O_AHLEN:
+		if (cb->invert)
+			ahinfo->invflags |= IP6T_AH_INV_LEN;
+		break;
+	case O_AHRES:
+		ahinfo->hdrres = 1;
+		break;
+	}
+}
+
+static void
+print_spis(const char *name, uint32_t min, uint32_t max,
+	    int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFFFFFF || invert) {
+		if (min == max)
+			printf("%s:%s%u", name, inv, min);
+		else
+			printf("%ss:%s%u:%u", name, inv, min, max);
+	}
+}
+
+static void
+print_len(const char *name, uint32_t len, int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (len != 0 || invert)
+		printf("%s:%s%u", name, inv, len);
+}
+
+static void ah_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct ip6t_ah *ah = (struct ip6t_ah *)match->data;
+
+	printf(" ah ");
+	print_spis("spi", ah->spis[0], ah->spis[1],
+		    ah->invflags & IP6T_AH_INV_SPI);
+	print_len("length", ah->hdrlen, 
+		    ah->invflags & IP6T_AH_INV_LEN);
+
+	if (ah->hdrres)
+		printf(" reserved");
+
+	if (ah->invflags & ~IP6T_AH_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       ah->invflags & ~IP6T_AH_INV_MASK);
+}
+
+static void ah_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_ah *ahinfo = (struct ip6t_ah *)match->data;
+
+	if (!(ahinfo->spis[0] == 0
+	    && ahinfo->spis[1] == 0xFFFFFFFF)) {
+		printf("%s --ahspi ",
+			(ahinfo->invflags & IP6T_AH_INV_SPI) ? " !" : "");
+		if (ahinfo->spis[0]
+		    != ahinfo->spis[1])
+			printf("%u:%u",
+			       ahinfo->spis[0],
+			       ahinfo->spis[1]);
+		else
+			printf("%u",
+			       ahinfo->spis[0]);
+	}
+
+	if (ahinfo->hdrlen != 0 || (ahinfo->invflags & IP6T_AH_INV_LEN) ) {
+		printf("%s --ahlen %u",
+			(ahinfo->invflags & IP6T_AH_INV_LEN) ? " !" : "",
+			ahinfo->hdrlen);
+	}
+
+	if (ahinfo->hdrres != 0 )
+		printf(" --ahres");
+}
+
+static struct xtables_match ah_mt6_reg = {
+	.name          = "ah",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV6,
+	.size          = XT_ALIGN(sizeof(struct ip6t_ah)),
+	.userspacesize = XT_ALIGN(sizeof(struct ip6t_ah)),
+	.help          = ah_help,
+	.print         = ah_print,
+	.save          = ah_save,
+	.x6_parse      = ah_parse,
+	.x6_options    = ah_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&ah_mt6_reg);
+}
diff --git a/extensions/libip6t_ah.man b/extensions/libip6t_ah.man
new file mode 100644
index 0000000..9c24dcf
--- /dev/null
+++ b/extensions/libip6t_ah.man
@@ -0,0 +1,10 @@
+This module matches the parameters in Authentication header of IPsec packets.
+.TP
+[\fB!\fP] \fB\-\-ahspi\fP \fIspi\fP[\fB:\fP\fIspi\fP]
+Matches SPI.
+.TP
+[\fB!\fP] \fB\-\-ahlen\fP \fIlength\fP
+Total length of this header in octets.
+.TP
+\fB\-\-ahres\fP
+Matches if the reserved field is filled with zero.
diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c
new file mode 100644
index 0000000..3fd4c01
--- /dev/null
+++ b/extensions/libip6t_dst.c
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+enum {
+	O_DSTLEN = 0,
+	O_DSTOPTS,
+};
+
+static void dst_help(void)
+{
+	printf(
+"dst match options:\n"
+"[!] --dst-len length            total length of this header\n"
+"  --dst-opts TYPE[:LEN][,TYPE[:LEN]...]\n"
+"                                Options and its length (list, max: %d)\n",
+IP6T_OPTS_OPTSNR);
+}
+
+static const struct xt_option_entry dst_opts[] = {
+	{.name = "dst-len", .id = O_DSTLEN, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct ip6t_opts, hdrlen)},
+	{.name = "dst-opts", .id = O_DSTOPTS, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static uint32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+	unsigned long int id;
+	char* ep;
+
+	id = strtoul(idstr, &ep, 0);
+
+	if ( idstr == ep ) {
+		xtables_error(PARAMETER_PROBLEM,
+		           "dst: no valid digits in %s `%s'", typestr, idstr);
+	}
+	if ( id == ULONG_MAX  && errno == ERANGE ) {
+		xtables_error(PARAMETER_PROBLEM,
+			   "%s `%s' specified too big: would overflow",
+			   typestr, idstr);
+	}
+	if ( *idstr != '\0'  && *ep != '\0' ) {
+		xtables_error(PARAMETER_PROBLEM,
+		           "dst: error parsing %s `%s'", typestr, idstr);
+	}
+	return id;
+}
+
+static int
+parse_options(const char *optsstr, uint16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+	
+	buffer = strdup(optsstr);
+        if (!buffer)
+		xtables_error(OTHER_PROBLEM, "strdup failed");
+			
+        for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
+        {
+                next = strchr(cp, ',');
+
+                if (next)
+			*next++='\0';
+
+                range = strchr(cp, ':');
+
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+				xtables_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+
+		opts[i] = (parse_opts_num(cp, "opt") & 0xFF) << 8;
+                if (range) {
+			if (opts[i] == 0)
+				xtables_error(PARAMETER_PROBLEM,
+					"PAD0 hasn't got length");
+			opts[i] |= parse_opts_num(range, "length") & 0xFF;
+                } else
+                        opts[i] |= (0x00FF);
+
+#ifdef DEBUG
+		printf("opts str: %s %s\n", cp, range);
+		printf("opts opt: %04X\n", opts[i]);
+#endif
+	}
+
+        if (cp)
+		xtables_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+	free(buffer);
+
+#ifdef DEBUG
+	printf("addr nr: %d\n", i);
+#endif
+
+	return i;
+}
+
+static void dst_parse(struct xt_option_call *cb)
+{
+	struct ip6t_opts *optinfo = cb->data;
+
+	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;
+		break;
+	}
+}
+
+static void
+print_options(unsigned int optsnr, uint16_t *optsp)
+{
+	unsigned int i;
+
+	printf(" ");
+	for(i = 0; i < optsnr; i++) {
+		printf("%d", (optsp[i] & 0xFF00) >> 8);
+
+		if ((optsp[i] & 0x00FF) != 0x00FF)
+			printf(":%d", (optsp[i] & 0x00FF));
+
+		printf("%c", (i != optsnr - 1) ? ',' : ' ');
+	}
+}
+
+static void dst_print(const void *ip, const struct xt_entry_match *match,
+                      int numeric)
+{
+	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+	printf(" dst");
+	if (optinfo->flags & IP6T_OPTS_LEN)
+		printf(" length:%s%u",
+			optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
+			optinfo->hdrlen);
+
+	if (optinfo->flags & IP6T_OPTS_OPTS)
+		printf(" opts");
+
+	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
+
+	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+static void dst_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+	if (optinfo->flags & IP6T_OPTS_LEN) {
+		printf("%s --dst-len %u",
+			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? " !" : "",
+			optinfo->hdrlen);
+	}
+
+	if (optinfo->flags & IP6T_OPTS_OPTS)
+		printf(" --dst-opts");
+
+	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
+}
+
+static struct xtables_match dst_mt6_reg = {
+	.name          = "dst",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV6,
+	.size          = XT_ALIGN(sizeof(struct ip6t_opts)),
+	.userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)),
+	.help          = dst_help,
+	.print         = dst_print,
+	.save          = dst_save,
+	.x6_parse      = dst_parse,
+	.x6_options    = dst_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&dst_mt6_reg);
+}
diff --git a/extensions/libip6t_dst.man b/extensions/libip6t_dst.man
new file mode 100644
index 0000000..bfbb501
--- /dev/null
+++ b/extensions/libip6t_dst.man
@@ -0,0 +1,7 @@
+This module matches the parameters in Destination Options header
+.TP
+[\fB!\fP] \fB\-\-dst\-len\fP \fIlength\fP
+Total length of this header in octets.
+.TP
+\fB\-\-dst\-opts\fP \fItype\fP[\fB:\fP\fIlength\fP][\fB,\fP\fItype\fP[\fB:\fP\fIlength\fP]...]
+numeric type of option and the length of the option data in octets.
diff --git a/extensions/libip6t_eui64.c b/extensions/libip6t_eui64.c
new file mode 100644
index 0000000..607bf86
--- /dev/null
+++ b/extensions/libip6t_eui64.c
@@ -0,0 +1,15 @@
+/* Shared library add-on to ip6tables to add EUI64 address checking support. */
+#include <xtables.h>
+
+static struct xtables_match eui64_mt6_reg = {
+	.name 		= "eui64",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(int)),
+	.userspacesize	= XT_ALIGN(sizeof(int)),
+};
+
+void _init(void)
+{
+	xtables_register_match(&eui64_mt6_reg);
+}
diff --git a/extensions/libip6t_eui64.man b/extensions/libip6t_eui64.man
new file mode 100644
index 0000000..cd80b98
--- /dev/null
+++ b/extensions/libip6t_eui64.man
@@ -0,0 +1,10 @@
+This module matches the EUI-64 part of a stateless autoconfigured IPv6 address.
+It compares the EUI-64 derived from the source MAC address in Ethernet frame
+with the lower 64 bits of the IPv6 source address. But "Universal/Local"
+bit is not compared. This module doesn't match other link layer frame, and
+is only valid in the
+.BR PREROUTING ,
+.BR INPUT
+and
+.BR FORWARD
+chains.
diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c
new file mode 100644
index 0000000..023df62
--- /dev/null
+++ b/extensions/libip6t_frag.c
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_frag.h>
+
+enum {
+	O_FRAGID = 0,
+	O_FRAGLEN,
+	O_FRAGRES,
+	O_FRAGFIRST,
+	O_FRAGMORE,
+	O_FRAGLAST,
+	F_FRAGMORE = 1 << O_FRAGMORE,
+	F_FRAGLAST = 1 << O_FRAGLAST,
+};
+
+static void frag_help(void)
+{
+	printf(
+"frag match options:\n"
+"[!] --fragid id[:id]           match the id (range)\n"
+"[!] --fraglen length           total length of this header\n"
+" --fragres                     check the reserved field too\n"
+" --fragfirst                   matches on the first fragment\n"
+" [--fragmore|--fraglast]       there are more fragments or this\n"
+"                               is the last one\n");
+}
+
+#define s struct ip6t_frag
+static const struct xt_option_entry frag_opts[] = {
+	{.name = "fragid", .id = O_FRAGID, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, ids)},
+	{.name = "fraglen", .id = O_FRAGLEN, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, hdrlen)},
+	{.name = "fragres", .id = O_FRAGRES, .type = XTTYPE_NONE},
+	{.name = "fragfirst", .id = O_FRAGFIRST, .type = XTTYPE_NONE},
+	{.name = "fragmore", .id = O_FRAGMORE, .type = XTTYPE_NONE,
+	 .excl = F_FRAGLAST},
+	{.name = "fraglast", .id = O_FRAGLAST, .type = XTTYPE_NONE,
+	 .excl = F_FRAGMORE},
+	XTOPT_TABLEEND,
+};
+#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;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	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;
+		break;
+	case O_FRAGFIRST:
+		fraginfo->flags |= IP6T_FRAG_FST;
+		break;
+	case O_FRAGMORE:
+		fraginfo->flags |= IP6T_FRAG_MF;
+		break;
+	case O_FRAGLAST:
+		fraginfo->flags |= IP6T_FRAG_NMF;
+		break;
+	}
+}
+
+static void
+print_ids(const char *name, uint32_t min, uint32_t max,
+	    int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFFFFFF || invert) {
+		printf("%s", name);
+		if (min == max)
+			printf(":%s%u", inv, min);
+		else
+			printf("s:%s%u:%u", inv, min, max);
+	}
+}
+
+static void frag_print(const void *ip, const struct xt_entry_match *match,
+                       int numeric)
+{
+	const struct ip6t_frag *frag = (struct ip6t_frag *)match->data;
+
+	printf(" frag ");
+	print_ids("id", frag->ids[0], frag->ids[1],
+		    frag->invflags & IP6T_FRAG_INV_IDS);
+
+	if (frag->flags & IP6T_FRAG_LEN) {
+		printf(" length:%s%u",
+			frag->invflags & IP6T_FRAG_INV_LEN ? "!" : "",
+			frag->hdrlen);
+	}
+
+	if (frag->flags & IP6T_FRAG_RES)
+		printf(" reserved");
+
+	if (frag->flags & IP6T_FRAG_FST)
+		printf(" first");
+
+	if (frag->flags & IP6T_FRAG_MF)
+		printf(" more");
+
+	if (frag->flags & IP6T_FRAG_NMF)
+		printf(" last");
+
+	if (frag->invflags & ~IP6T_FRAG_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       frag->invflags & ~IP6T_FRAG_INV_MASK);
+}
+
+static void frag_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_frag *fraginfo = (struct ip6t_frag *)match->data;
+
+	if (!(fraginfo->ids[0] == 0
+	    && fraginfo->ids[1] == 0xFFFFFFFF)) {
+		printf("%s --fragid ",
+			(fraginfo->invflags & IP6T_FRAG_INV_IDS) ? " !" : "");
+		if (fraginfo->ids[0]
+		    != fraginfo->ids[1])
+			printf("%u:%u",
+			       fraginfo->ids[0],
+			       fraginfo->ids[1]);
+		else
+			printf("%u",
+			       fraginfo->ids[0]);
+	}
+
+	if (fraginfo->flags & IP6T_FRAG_LEN) {
+		printf("%s --fraglen %u",
+			(fraginfo->invflags & IP6T_FRAG_INV_LEN) ? " !" : "",
+			fraginfo->hdrlen);
+	}
+
+	if (fraginfo->flags & IP6T_FRAG_RES)
+		printf(" --fragres");
+
+	if (fraginfo->flags & IP6T_FRAG_FST)
+		printf(" --fragfirst");
+
+	if (fraginfo->flags & IP6T_FRAG_MF)
+		printf(" --fragmore");
+
+	if (fraginfo->flags & IP6T_FRAG_NMF)
+		printf(" --fraglast");
+}
+
+static struct xtables_match frag_mt6_reg = {
+	.name          = "frag",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV6,
+	.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,
+	.x6_options    = frag_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&frag_mt6_reg);
+}
diff --git a/extensions/libip6t_frag.man b/extensions/libip6t_frag.man
new file mode 100644
index 0000000..7bfa227
--- /dev/null
+++ b/extensions/libip6t_frag.man
@@ -0,0 +1,20 @@
+This module matches the parameters in Fragment header.
+.TP
+[\fB!\fP] \fB\-\-fragid\fP \fIid\fP[\fB:\fP\fIid\fP]
+Matches the given Identification or range of it.
+.TP
+[\fB!\fP] \fB\-\-fraglen\fP \fIlength\fP
+This option cannot be used with kernel version 2.6.10 or later. The length of
+Fragment header is static and this option doesn't make sense.
+.TP
+\fB\-\-fragres\fP
+Matches if the reserved fields are filled with zero.
+.TP
+\fB\-\-fragfirst\fP
+Matches on the first fragment.
+.TP
+\fB\-\-fragmore\fP
+Matches if there are more fragments.
+.TP
+\fB\-\-fraglast\fP
+Matches if this is the last fragment.
diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c
new file mode 100644
index 0000000..c0389ed
--- /dev/null
+++ b/extensions/libip6t_hbh.c
@@ -0,0 +1,184 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+
+#define DEBUG		0
+
+enum {
+	O_HBH_LEN = 0,
+	O_HBH_OPTS,
+};
+
+static void hbh_help(void)
+{
+	printf(
+"hbh match options:\n"
+"[!] --hbh-len length            total length of this header\n"
+"  --hbh-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
+"                                Options and its length (list, max: %d)\n",
+IP6T_OPTS_OPTSNR);
+}
+
+static const struct xt_option_entry hbh_opts[] = {
+	{.name = "hbh-len", .id = O_HBH_LEN, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct ip6t_opts, hdrlen)},
+	{.name = "hbh-opts", .id = O_HBH_OPTS, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static uint32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+	unsigned long int id;
+	char* ep;
+
+	id =  strtoul(idstr,&ep,0) ;
+
+	if ( idstr == ep ) {
+		xtables_error(PARAMETER_PROBLEM,
+			   "hbh: no valid digits in %s `%s'", typestr, idstr);
+	}
+	if ( id == ULONG_MAX  && errno == ERANGE ) {
+		xtables_error(PARAMETER_PROBLEM,
+			   "%s `%s' specified too big: would overflow",
+			   typestr, idstr);
+	}	
+	if ( *idstr != '\0'  && *ep != '\0' ) {
+		xtables_error(PARAMETER_PROBLEM,
+			   "hbh: error parsing %s `%s'", typestr, idstr);
+	}
+	return id;
+}
+
+static int
+parse_options(const char *optsstr, uint16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+	
+	buffer = strdup(optsstr);
+	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
+			
+        for (cp=buffer, i=0; cp && i<IP6T_OPTS_OPTSNR; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+                range = strchr(cp, ':');
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+				xtables_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+		opts[i] = (parse_opts_num(cp, "opt") & 0xFF) << 8;
+                if (range) {
+			if (opts[i] == 0)
+				xtables_error(PARAMETER_PROBLEM, "PAD0 has not got length");
+			opts[i] |= parse_opts_num(range, "length") & 0xFF;
+                } else {
+                        opts[i] |= (0x00FF);
+		}
+
+#if DEBUG
+		printf("opts str: %s %s\n", cp, range);
+		printf("opts opt: %04X\n", opts[i]);
+#endif
+	}
+	if (cp) xtables_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+	free(buffer);
+
+#if DEBUG
+	printf("addr nr: %d\n", i);
+#endif
+
+	return i;
+}
+
+static void hbh_parse(struct xt_option_call *cb)
+{
+	struct ip6t_opts *optinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	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);
+		optinfo->flags |= IP6T_OPTS_OPTS;
+		break;
+	}
+}
+
+static void
+print_options(unsigned int optsnr, uint16_t *optsp)
+{
+	unsigned int i;
+
+	for(i=0; i<optsnr; i++){
+		printf("%c", (i==0)?' ':',');
+		printf("%d", (optsp[i] & 0xFF00)>>8);
+		if ((optsp[i] & 0x00FF) != 0x00FF){
+			printf(":%d", (optsp[i] & 0x00FF));
+		} 
+	}
+}
+
+static void hbh_print(const void *ip, const struct xt_entry_match *match,
+                      int numeric)
+{
+	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+	printf(" hbh");
+	if (optinfo->flags & IP6T_OPTS_LEN) {
+		printf(" length");
+		printf(":%s", optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "");
+		printf("%u", optinfo->hdrlen);
+	}
+	if (optinfo->flags & IP6T_OPTS_OPTS) printf(" opts");
+	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
+	if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+static void hbh_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+	if (optinfo->flags & IP6T_OPTS_LEN) {
+		printf("%s --hbh-len %u",
+			(optinfo->invflags & IP6T_OPTS_INV_LEN) ? " !" : "",
+			optinfo->hdrlen);
+	}
+
+	if (optinfo->flags & IP6T_OPTS_OPTS)
+		printf(" --hbh-opts");
+	print_options(optinfo->optsnr, (uint16_t *)optinfo->opts);
+}
+
+static struct xtables_match hbh_mt6_reg = {
+	.name 		= "hbh",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_opts)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_opts)),
+	.help		= hbh_help,
+	.print		= hbh_print,
+	.save		= hbh_save,
+	.x6_parse	= hbh_parse,
+	.x6_options	= hbh_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&hbh_mt6_reg);
+}
diff --git a/extensions/libip6t_hbh.man b/extensions/libip6t_hbh.man
new file mode 100644
index 0000000..2d92e04
--- /dev/null
+++ b/extensions/libip6t_hbh.man
@@ -0,0 +1,7 @@
+This module matches the parameters in Hop-by-Hop Options header
+.TP
+[\fB!\fP] \fB\-\-hbh\-len\fP \fIlength\fP
+Total length of this header in octets.
+.TP
+\fB\-\-hbh\-opts\fP \fItype\fP[\fB:\fP\fIlength\fP][\fB,\fP\fItype\fP[\fB:\fP\fIlength\fP]...]
+numeric type of option and the length of the option data in octets.
diff --git a/extensions/libip6t_hl.c b/extensions/libip6t_hl.c
new file mode 100644
index 0000000..3559db4
--- /dev/null
+++ b/extensions/libip6t_hl.c
@@ -0,0 +1,118 @@
+/*
+ * IPv6 Hop Limit matching module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl match
+ * This program is released under the terms of GNU GPL
+ * Cleanups by Stephane Ouellette <ouellettes@videotron.ca>
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+
+enum {
+	O_HL_EQ = 0,
+	O_HL_LT,
+	O_HL_GT,
+	F_HL_EQ = 1 << O_HL_EQ,
+	F_HL_LT = 1 << O_HL_LT,
+	F_HL_GT = 1 << O_HL_GT,
+	F_ANY  = F_HL_EQ | F_HL_LT | F_HL_GT,
+};
+
+static void hl_help(void)
+{
+	printf(
+"hl match options:\n"
+"[!] --hl-eq value	Match hop limit value\n"
+"  --hl-lt value	Match HL < value\n"
+"  --hl-gt value	Match HL > value\n");
+}
+
+static void hl_parse(struct xt_option_call *cb)
+{
+	struct ip6t_hl_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_HL_EQ:
+		info->mode = cb->invert ? IP6T_HL_NE : IP6T_HL_EQ;
+		break;
+	case O_HL_LT:
+		info->mode = IP6T_HL_LT;
+		break;
+	case O_HL_GT:
+		info->mode = IP6T_HL_GT;
+		break;
+	}
+}
+
+static void hl_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+			"HL match: You must specify one of "
+			"`--hl-eq', `--hl-lt', `--hl-gt'");
+}
+
+static void hl_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	static const char *const op[] = {
+		[IP6T_HL_EQ] = "==",
+		[IP6T_HL_NE] = "!=",
+		[IP6T_HL_LT] = "<",
+		[IP6T_HL_GT] = ">" };
+
+	const struct ip6t_hl_info *info = 
+		(struct ip6t_hl_info *) match->data;
+
+	printf(" HL match HL %s %u", op[info->mode], info->hop_limit);
+}
+
+static void hl_save(const void *ip, const struct xt_entry_match *match)
+{
+	static const char *const op[] = {
+		[IP6T_HL_EQ] = "--hl-eq",
+		[IP6T_HL_NE] = "! --hl-eq",
+		[IP6T_HL_LT] = "--hl-lt",
+		[IP6T_HL_GT] = "--hl-gt" };
+
+	const struct ip6t_hl_info *info =
+		(struct ip6t_hl_info *) match->data;
+
+	printf(" %s %u", op[info->mode], info->hop_limit);
+}
+
+#define s struct ip6t_hl_info
+static const struct xt_option_entry hl_opts[] = {
+	{.name = "hl-lt", .id = O_HL_LT, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
+	{.name = "hl-gt", .id = O_HL_GT, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
+	{.name = "hl-eq", .id = O_HL_EQ, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
+	{.name = "hl", .id = O_HL_EQ, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hop_limit)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static struct xtables_match hl_mt6_reg = {
+	.name          = "hl",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV6,
+	.size          = XT_ALIGN(sizeof(struct ip6t_hl_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct ip6t_hl_info)),
+	.help          = hl_help,
+	.print         = hl_print,
+	.save          = hl_save,
+	.x6_parse      = hl_parse,
+	.x6_fcheck     = hl_check,
+	.x6_options    = hl_opts,
+};
+
+
+void _init(void) 
+{
+	xtables_register_match(&hl_mt6_reg);
+}
diff --git a/extensions/libip6t_hl.man b/extensions/libip6t_hl.man
new file mode 100644
index 0000000..dfbfaf8
--- /dev/null
+++ b/extensions/libip6t_hl.man
@@ -0,0 +1,10 @@
+This module matches the Hop Limit field in the IPv6 header.
+.TP
+[\fB!\fP] \fB\-\-hl\-eq\fP \fIvalue\fP
+Matches if Hop Limit equals \fIvalue\fP.
+.TP
+\fB\-\-hl\-lt\fP \fIvalue\fP
+Matches if Hop Limit is less than \fIvalue\fP.
+.TP
+\fB\-\-hl\-gt\fP \fIvalue\fP
+Matches if Hop Limit is greater than \fIvalue\fP.
diff --git a/extensions/libip6t_icmp6.c b/extensions/libip6t_icmp6.c
new file mode 100644
index 0000000..68b940b
--- /dev/null
+++ b/extensions/libip6t_icmp6.c
@@ -0,0 +1,239 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip6_tables.h */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+enum {
+	O_ICMPV6_TYPE = 0,
+};
+
+struct icmpv6_names {
+	const char *name;
+	uint8_t type;
+	uint8_t code_min, code_max;
+};
+
+static const struct icmpv6_names icmpv6_codes[] = {
+	{ "destination-unreachable", 1, 0, 0xFF },
+	{   "no-route", 1, 0, 0 },
+	{   "communication-prohibited", 1, 1, 1 },
+	{   "address-unreachable", 1, 3, 3 },
+	{   "port-unreachable", 1, 4, 4 },
+
+	{ "packet-too-big", 2, 0, 0xFF },
+
+	{ "time-exceeded", 3, 0, 0xFF },
+	/* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+	{   "ttl-zero-during-transit", 3, 0, 0 },
+	{   "ttl-zero-during-reassembly", 3, 1, 1 },
+
+	{ "parameter-problem", 4, 0, 0xFF },
+	{   "bad-header", 4, 0, 0 },
+	{   "unknown-header-type", 4, 1, 1 },
+	{   "unknown-option", 4, 2, 2 },
+
+	{ "echo-request", 128, 0, 0xFF },
+	/* Alias */ { "ping", 128, 0, 0xFF },
+
+	{ "echo-reply", 129, 0, 0xFF },
+	/* Alias */ { "pong", 129, 0, 0xFF },
+
+	{ "router-solicitation", 133, 0, 0xFF },
+
+	{ "router-advertisement", 134, 0, 0xFF },
+
+	{ "neighbour-solicitation", 135, 0, 0xFF },
+	/* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+	{ "neighbour-advertisement", 136, 0, 0xFF },
+	/* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+	{ "redirect", 137, 0, 0xFF },
+
+};
+
+static void
+print_icmpv6types(void)
+{
+	unsigned int i;
+	printf("Valid ICMPv6 Types:");
+
+	for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) {
+		if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
+			if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
+			    && (icmpv6_codes[i].code_max
+				== icmpv6_codes[i-1].code_max))
+				printf(" (%s)", icmpv6_codes[i].name);
+			else
+				printf("\n   %s", icmpv6_codes[i].name);
+		}
+		else
+			printf("\n%s", icmpv6_codes[i].name);
+	}
+	printf("\n");
+}
+
+static void icmp6_help(void)
+{
+	printf(
+"icmpv6 match options:\n"
+"[!] --icmpv6-type typename	match icmpv6 type\n"
+"				(or numeric type or type/code)\n");
+	print_icmpv6types();
+}
+
+static const struct xt_option_entry icmp6_opts[] = {
+	{.name = "icmpv6-type", .id = O_ICMPV6_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void
+parse_icmpv6(const char *icmpv6type, uint8_t *type, uint8_t code[])
+{
+	static const unsigned int limit = ARRAY_SIZE(icmpv6_codes);
+	unsigned int match = limit;
+	unsigned int i;
+
+	for (i = 0; i < limit; i++) {
+		if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type))
+		    == 0) {
+			if (match != limit)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Ambiguous ICMPv6 type `%s':"
+					   " `%s' or `%s'?",
+					   icmpv6type,
+					   icmpv6_codes[match].name,
+					   icmpv6_codes[i].name);
+			match = i;
+		}
+	}
+
+	if (match != limit) {
+		*type = icmpv6_codes[match].type;
+		code[0] = icmpv6_codes[match].code_min;
+		code[1] = icmpv6_codes[match].code_max;
+	} else {
+		char *slash;
+		char buffer[strlen(icmpv6type) + 1];
+		unsigned int number;
+
+		strcpy(buffer, icmpv6type);
+		slash = strchr(buffer, '/');
+
+		if (slash)
+			*slash = '\0';
+
+		if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid ICMPv6 type `%s'\n", buffer);
+		*type = number;
+		if (slash) {
+			if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX))
+				xtables_error(PARAMETER_PROBLEM,
+					   "Invalid ICMPv6 code `%s'\n",
+					   slash+1);
+			code[0] = code[1] = number;
+		} else {
+			code[0] = 0;
+			code[1] = 0xFF;
+		}
+	}
+}
+
+static void icmp6_init(struct xt_entry_match *m)
+{
+	struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data;
+
+	icmpv6info->code[1] = 0xFF;
+}
+
+static void icmp6_parse(struct xt_option_call *cb)
+{
+	struct ip6t_icmp *icmpv6info = cb->data;
+
+	xtables_option_parse(cb);
+	parse_icmpv6(cb->arg, &icmpv6info->type, icmpv6info->code);
+	if (cb->invert)
+		icmpv6info->invflags |= IP6T_ICMP_INV;
+}
+
+static void print_icmpv6type(uint8_t type,
+			   uint8_t code_min, uint8_t code_max,
+			   int invert,
+			   int numeric)
+{
+	if (!numeric) {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i)
+			if (icmpv6_codes[i].type == type
+			    && icmpv6_codes[i].code_min == code_min
+			    && icmpv6_codes[i].code_max == code_max)
+				break;
+
+		if (i != ARRAY_SIZE(icmpv6_codes)) {
+			printf(" %s%s",
+			       invert ? "!" : "",
+			       icmpv6_codes[i].name);
+			return;
+		}
+	}
+
+	if (invert)
+		printf(" !");
+
+	printf("type %u", type);
+	if (code_min == code_max)
+		printf(" code %u", code_min);
+	else if (code_min != 0 || code_max != 0xFF)
+		printf(" codes %u-%u", code_min, code_max);
+}
+
+static void icmp6_print(const void *ip, const struct xt_entry_match *match,
+                        int numeric)
+{
+	const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+	printf(" ipv6-icmp");
+	print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1],
+		       icmpv6->invflags & IP6T_ICMP_INV,
+		       numeric);
+
+	if (icmpv6->invflags & ~IP6T_ICMP_INV)
+		printf(" Unknown invflags: 0x%X",
+		       icmpv6->invflags & ~IP6T_ICMP_INV);
+}
+
+static void icmp6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+	if (icmpv6->invflags & IP6T_ICMP_INV)
+		printf(" !");
+
+	printf(" --icmpv6-type %u", icmpv6->type);
+	if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF)
+		printf("/%u", icmpv6->code[0]);
+}
+
+static struct xtables_match icmp6_mt6_reg = {
+	.name 		= "icmp6",
+	.version 	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_icmp)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_icmp)),
+	.help		= icmp6_help,
+	.init		= icmp6_init,
+	.print		= icmp6_print,
+	.save		= icmp6_save,
+	.x6_parse	= icmp6_parse,
+	.x6_options	= icmp6_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&icmp6_mt6_reg);
+}
diff --git a/extensions/libip6t_icmp6.man b/extensions/libip6t_icmp6.man
new file mode 100644
index 0000000..817e21c
--- /dev/null
+++ b/extensions/libip6t_icmp6.man
@@ -0,0 +1,14 @@
+This extension can be used if `\-\-protocol ipv6\-icmp' or `\-\-protocol icmpv6' is
+specified. It provides the following option:
+.TP
+[\fB!\fP] \fB\-\-icmpv6\-type\fP \fItype\fP[\fB/\fP\fIcode\fP]|\fItypename\fP
+This allows specification of the ICMPv6 type, which can be a numeric
+ICMPv6
+.IR type ,
+.IR type
+and
+.IR code ,
+or one of the ICMPv6 type names shown by the command
+.nf
+ ip6tables \-p ipv6\-icmp \-h
+.fi
diff --git a/extensions/libip6t_ipv6header.c b/extensions/libip6t_ipv6header.c
new file mode 100644
index 0000000..00d5d5b
--- /dev/null
+++ b/extensions/libip6t_ipv6header.c
@@ -0,0 +1,245 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
+
+enum {
+	O_HEADER = 0,
+	O_SOFT,
+};
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+ *    /etc/protocols */
+struct pprot {
+	char *name;
+	uint8_t num;
+};
+
+struct numflag {
+	uint8_t proto;
+	uint8_t flag;
+};
+
+static const struct pprot chain_protos[] = {
+	{ "hop-by-hop", IPPROTO_HOPOPTS },
+	{ "protocol", IPPROTO_RAW },
+	{ "hop", IPPROTO_HOPOPTS },
+	{ "dst", IPPROTO_DSTOPTS },
+	{ "route", IPPROTO_ROUTING },
+	{ "frag", IPPROTO_FRAGMENT },
+	{ "auth", IPPROTO_AH },
+	{ "esp", IPPROTO_ESP },
+	{ "none", IPPROTO_NONE },
+	{ "prot", IPPROTO_RAW },
+	{ "0", IPPROTO_HOPOPTS },
+	{ "60", IPPROTO_DSTOPTS },
+	{ "43", IPPROTO_ROUTING },
+	{ "44", IPPROTO_FRAGMENT },
+	{ "51", IPPROTO_AH },
+	{ "50", IPPROTO_ESP },
+	{ "59", IPPROTO_NONE },
+	{ "255", IPPROTO_RAW },
+	/* { "all", 0 }, */
+};
+
+static const struct numflag chain_flags[] = {
+	{ IPPROTO_HOPOPTS, MASK_HOPOPTS },
+	{ IPPROTO_DSTOPTS, MASK_DSTOPTS },
+	{ IPPROTO_ROUTING, MASK_ROUTING },
+	{ IPPROTO_FRAGMENT, MASK_FRAGMENT },
+	{ IPPROTO_AH, MASK_AH },
+	{ IPPROTO_ESP, MASK_ESP },
+	{ IPPROTO_NONE, MASK_NONE },
+	{ IPPROTO_RAW, MASK_PROTO },
+};
+
+static const char *
+proto_to_name(uint8_t proto, int nolookup)
+{
+        unsigned int i;
+
+        if (proto && !nolookup) {
+		const struct protoent *pent = getprotobynumber(proto);
+                if (pent)
+                        return pent->p_name;
+        }
+
+        for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
+                if (chain_protos[i].num == proto)
+                        return chain_protos[i].name;
+
+        return NULL;
+}
+
+static uint16_t
+name_to_proto(const char *s)
+{
+        unsigned int proto=0;
+	const struct protoent *pent;
+
+        if ((pent = getprotobyname(s)))
+        	proto = pent->p_proto;
+        else {
+        	unsigned int i;
+        	for (i = 0; i < ARRAY_SIZE(chain_protos); ++i)
+        		if (strcmp(s, chain_protos[i].name) == 0) {
+        			proto = chain_protos[i].num;
+        			break;
+        		}
+
+		if (i == ARRAY_SIZE(chain_protos))
+			xtables_error(PARAMETER_PROBLEM,
+        			"unknown header `%s' specified",
+        			s);
+        }
+
+        return proto;
+}
+
+static unsigned int 
+add_proto_to_mask(int proto){
+	unsigned int i=0, flag=0;
+
+	for (i = 0; i < ARRAY_SIZE(chain_flags); ++i)
+			if (proto == chain_flags[i].proto){
+				flag = chain_flags[i].flag;
+				break;
+			}
+
+	if (i == ARRAY_SIZE(chain_flags))
+		xtables_error(PARAMETER_PROBLEM,
+		"unknown header `%d' specified",
+		proto);
+	
+	return flag;
+}	
+
+static void ipv6header_help(void)
+{
+	printf(
+"ipv6header match options:\n"
+"[!] --header headers     Type of header to match, by name\n"
+"                         names: hop,dst,route,frag,auth,esp,none,proto\n"
+"                    long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
+"                                ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
+"                       numbers: 0,60,43,44,51,50,59\n"
+"--soft                    The header CONTAINS the specified extensions\n");
+}
+
+static const struct xt_option_entry ipv6header_opts[] = {
+	{.name = "header", .id = O_HEADER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	{.name = "soft", .id = O_SOFT, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static unsigned int
+parse_header(const char *flags) {
+        unsigned int ret = 0;
+        char *ptr;
+        char *buffer;
+
+        buffer = strdup(flags);
+
+        for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) 
+		ret |= add_proto_to_mask(name_to_proto(ptr));
+                
+        free(buffer);
+        return ret;
+}
+
+static void ipv6header_parse(struct xt_option_call *cb)
+{
+	struct ip6t_ipv6header_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_HEADER:
+		if (!(info->matchflags = parse_header(cb->arg)))
+			xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names");
+		if (cb->invert) 
+			info->invflags |= 0xFF;
+		break;
+	case O_SOFT:
+		info->modeflag |= 0xFF;
+		break;
+	}
+}
+
+static void
+print_header(uint8_t flags){
+        int have_flag = 0;
+
+        while (flags) {
+                unsigned int i;
+
+                for (i = 0; (flags & chain_flags[i].flag) == 0; i++);
+
+                if (have_flag)
+                        printf(",");
+
+                printf("%s", proto_to_name(chain_flags[i].proto,0));
+                have_flag = 1;
+
+                flags &= ~chain_flags[i].flag;
+        }
+
+        if (!have_flag)
+                printf("NONE");
+}
+
+static void ipv6header_print(const void *ip,
+                             const struct xt_entry_match *match, int numeric)
+{
+	const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+	printf(" ipv6header");
+
+        if (info->matchflags || info->invflags) {
+		printf(" flags:%s", info->invflags ? "!" : "");
+                if (numeric)
+			printf("0x%02X", info->matchflags);
+                else {
+                        print_header(info->matchflags);
+                }
+        }
+
+	if (info->modeflag)
+		printf(" soft");
+}
+
+static void ipv6header_save(const void *ip, const struct xt_entry_match *match)
+{
+
+	const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+
+	printf("%s --header ", info->invflags ? " !" : "");
+	print_header(info->matchflags);
+	if (info->modeflag)
+		printf(" --soft");
+}
+
+static struct xtables_match ipv6header_mt6_reg = {
+	.name		= "ipv6header",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+	.help		= ipv6header_help,
+	.print		= ipv6header_print,
+	.save		= ipv6header_save,
+	.x6_parse	= ipv6header_parse,
+	.x6_options	= ipv6header_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&ipv6header_mt6_reg);
+}
diff --git a/extensions/libip6t_ipv6header.man b/extensions/libip6t_ipv6header.man
new file mode 100644
index 0000000..a998861
--- /dev/null
+++ b/extensions/libip6t_ipv6header.man
@@ -0,0 +1,37 @@
+This module matches IPv6 extension headers and/or upper layer header.
+.TP
+\fB\-\-soft\fP
+Matches if the packet includes \fBany\fP of the headers specified with
+\fB\-\-header\fP.
+.TP
+[\fB!\fP] \fB\-\-header\fP \fIheader\fP[\fB,\fP\fIheader\fP...]
+Matches the packet which EXACTLY includes all specified headers. The headers
+encapsulated with ESP header are out of scope.
+Possible \fIheader\fP types can be:
+.TP
+\fBhop\fP|\fBhop\-by\-hop\fP
+Hop-by-Hop Options header
+.TP
+\fBdst\fP
+Destination Options header
+.TP
+\fBroute\fP
+Routing header
+.TP
+\fBfrag\fP
+Fragment header
+.TP
+\fBauth\fP
+Authentication header
+.TP
+\fBesp\fP
+Encapsulating Security Payload header
+.TP
+\fBnone\fP
+No Next header which matches 59 in the 'Next Header field' of IPv6 header or
+any IPv6 extension headers
+.TP
+\fBproto\fP
+which matches any upper layer protocol header. A protocol name from
+/etc/protocols and numeric value also allowed. The number 255 is equivalent to
+\fBproto\fP.
diff --git a/extensions/libip6t_mh.c b/extensions/libip6t_mh.c
new file mode 100644
index 0000000..686a293
--- /dev/null
+++ b/extensions/libip6t_mh.c
@@ -0,0 +1,228 @@
+/* Shared library add-on to ip6tables to add mobility header support. */
+/*
+ * Copyright (C)2006 USAGI/WIDE Project
+ *
+ * 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.
+ *
+ * Author:
+ *	Masahide NAKAMURA @USAGI <masahide.nakamura.cz@hitachi.com>
+ *
+ * Based on libip6t_{icmpv6,udp}.c
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_mh.h>
+
+enum {
+	O_MH_TYPE = 0,
+};
+
+struct mh_name {
+	const char *name;
+	uint8_t type;
+};
+
+static const struct mh_name mh_names[] = {
+	{ "binding-refresh-request", 0, },
+	/* Alias */ { "brr", 0, },
+	{ "home-test-init", 1, },
+	/* Alias */ { "hoti", 1, },
+	{ "careof-test-init", 2, },
+	/* Alias */ { "coti", 2, },
+	{ "home-test", 3, },
+	/* Alias */ { "hot", 3, },
+	{ "careof-test", 4, },
+	/* Alias */ { "cot", 4, },
+	{ "binding-update", 5, },
+	/* Alias */ { "bu", 5, },
+	{ "binding-acknowledgement", 6, },
+	/* Alias */ { "ba", 6, },
+	{ "binding-error", 7, },
+	/* Alias */ { "be", 7, },
+};
+
+static void print_types_all(void)
+{
+	unsigned int i;
+	printf("Valid MH types:");
+
+	for (i = 0; i < ARRAY_SIZE(mh_names); ++i) {
+		if (i && mh_names[i].type == mh_names[i-1].type)
+			printf(" (%s)", mh_names[i].name);
+		else
+			printf("\n%s", mh_names[i].name);
+	}
+	printf("\n");
+}
+
+static void mh_help(void)
+{
+	printf(
+"mh match options:\n"
+"[!] --mh-type type[:type]	match mh type\n");
+	print_types_all();
+}
+
+static void mh_init(struct xt_entry_match *m)
+{
+	struct ip6t_mh *mhinfo = (struct ip6t_mh *)m->data;
+
+	mhinfo->types[1] = 0xFF;
+}
+
+static unsigned int name_to_type(const char *name)
+{
+	int namelen = strlen(name);
+	static const unsigned int limit = ARRAY_SIZE(mh_names);
+	unsigned int match = limit;
+	unsigned int i;
+
+	for (i = 0; i < limit; i++) {
+		if (strncasecmp(mh_names[i].name, name, namelen) == 0) {
+			int len = strlen(mh_names[i].name);
+			if (match == limit || len == namelen)
+				match = i;
+		}
+	}
+
+	if (match != limit) {
+		return mh_names[match].type;
+	} else {
+		unsigned int number;
+
+		if (!xtables_strtoui(name, NULL, &number, 0, UINT8_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid MH type `%s'\n", name);
+		return number;
+	}
+}
+
+static void parse_mh_types(const char *mhtype, uint8_t *types)
+{
+	char *buffer;
+	char *cp;
+
+	buffer = strdup(mhtype);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		types[0] = types[1] = name_to_type(buffer);
+	else {
+		*cp = '\0';
+		cp++;
+
+		types[0] = buffer[0] ? name_to_type(buffer) : 0;
+		types[1] = cp[0] ? name_to_type(cp) : 0xFF;
+
+		if (types[0] > types[1])
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid MH type range (min > max)");
+	}
+	free(buffer);
+}
+
+static void mh_parse(struct xt_option_call *cb)
+{
+	struct ip6t_mh *mhinfo = cb->data;
+
+	xtables_option_parse(cb);
+	parse_mh_types(cb->arg, mhinfo->types);
+	if (cb->invert)
+		mhinfo->invflags |= IP6T_MH_INV_TYPE;
+}
+
+static const char *type_to_name(uint8_t type)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(mh_names); ++i)
+		if (mh_names[i].type == type)
+			return mh_names[i].name;
+
+	return NULL;
+}
+
+static void print_type(uint8_t type, int numeric)
+{
+	const char *name;
+	if (numeric || !(name = type_to_name(type)))
+		printf("%u", type);
+	else
+		printf("%s", name);
+}
+
+static void print_types(uint8_t min, uint8_t max, int invert, int numeric)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFF || invert) {
+		printf(" ");
+		if (min == max) {
+			printf("%s", inv);
+			print_type(min, numeric);
+		} else {
+			printf("%s", inv);
+			print_type(min, numeric);
+			printf(":");
+			print_type(max, numeric);
+		}
+	}
+}
+
+static void mh_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data;
+
+	printf(" mh");
+	print_types(mhinfo->types[0], mhinfo->types[1],
+		    mhinfo->invflags & IP6T_MH_INV_TYPE,
+		    numeric);
+	if (mhinfo->invflags & ~IP6T_MH_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       mhinfo->invflags & ~IP6T_MH_INV_MASK);
+}
+
+static void mh_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data;
+
+	if (mhinfo->types[0] == 0 && mhinfo->types[1] == 0xFF)
+		return;
+
+	if (mhinfo->invflags & IP6T_MH_INV_TYPE)
+		printf(" !");
+
+	if (mhinfo->types[0] != mhinfo->types[1])
+		printf(" --mh-type %u:%u", mhinfo->types[0], mhinfo->types[1]);
+	else
+		printf(" --mh-type %u", mhinfo->types[0]);
+}
+
+static const struct xt_option_entry mh_opts[] = {
+	{.name = "mh-type", .id = O_MH_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static struct xtables_match mh_mt6_reg = {
+	.name		= "mh",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_mh)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_mh)),
+	.help		= mh_help,
+	.init		= mh_init,
+	.x6_parse	= mh_parse,
+	.print		= mh_print,
+	.save		= mh_save,
+	.x6_options	= mh_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&mh_mt6_reg);
+}
diff --git a/extensions/libip6t_mh.man b/extensions/libip6t_mh.man
new file mode 100644
index 0000000..8ec08c6
--- /dev/null
+++ b/extensions/libip6t_mh.man
@@ -0,0 +1,12 @@
+This extension is loaded if `\-\-protocol ipv6\-mh' or `\-\-protocol mh' is
+specified. It provides the following option:
+.TP
+[\fB!\fP] \fB\-\-mh\-type\fP \fItype\fP[\fB:\fP\fItype\fP]
+This allows specification of the Mobility Header(MH) type, which can be
+a numeric MH
+.IR type ,
+.IR type
+or one of the MH type names shown by the command
+.nf
+ ip6tables \-p mh \-h
+.fi
diff --git a/extensions/libip6t_rt.c b/extensions/libip6t_rt.c
new file mode 100644
index 0000000..d470488
--- /dev/null
+++ b/extensions/libip6t_rt.c
@@ -0,0 +1,258 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv6/ip6t_rt.h>
+#include <arpa/inet.h>
+
+enum {
+	O_RT_TYPE = 0,
+	O_RT_SEGSLEFT,
+	O_RT_LEN,
+	O_RT0RES,
+	O_RT0ADDRS,
+	O_RT0NSTRICT,
+	F_RT_TYPE  = 1 << O_RT_TYPE,
+	F_RT0ADDRS = 1 << O_RT0ADDRS,
+};
+
+static void rt_help(void)
+{
+	printf(
+"rt match options:\n"
+"[!] --rt-type type             match the type\n"
+"[!] --rt-segsleft num[:num]    match the Segments Left field (range)\n"
+"[!] --rt-len length            total length of this header\n"
+" --rt-0-res                    check the reserved field too (type 0)\n"
+" --rt-0-addrs ADDR[,ADDR...]   Type=0 addresses (list, max: %d)\n"
+" --rt-0-not-strict             List of Type=0 addresses not a strict list\n",
+IP6T_RT_HOPS);
+}
+
+#define s struct ip6t_rt
+static const struct xt_option_entry rt_opts[] = {
+	{.name = "rt-type", .id = O_RT_TYPE, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, rt_type)},
+	{.name = "rt-segsleft", .id = O_RT_SEGSLEFT, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, segsleft)},
+	{.name = "rt-len", .id = O_RT_LEN, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, hdrlen)},
+	{.name = "rt-0-res", .id = O_RT0RES, .type = XTTYPE_NONE},
+	{.name = "rt-0-addrs", .id = O_RT0ADDRS, .type = XTTYPE_STRING},
+	{.name = "rt-0-not-strict", .id = O_RT0NSTRICT, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static const char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+	static char buf[50+1];
+	return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+	static struct in6_addr ap;
+	int err;
+
+	if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+		return &ap;
+#ifdef DEBUG
+	fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+	xtables_error(PARAMETER_PROBLEM, "bad address: %s", num);
+
+	return (struct in6_addr *)NULL;
+}
+
+
+static int
+parse_addresses(const char *addrstr, struct in6_addr *addrp)
+{
+        char *buffer, *cp, *next;
+        unsigned int i;
+	
+	buffer = strdup(addrstr);
+	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
+			
+        for (cp=buffer, i=0; cp && i<IP6T_RT_HOPS; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+		memcpy(&(addrp[i]), numeric_to_addr(cp), sizeof(struct in6_addr));
+#if DEBUG
+		printf("addr str: %s\n", cp);
+		printf("addr ip6: %s\n", addr_to_numeric((numeric_to_addr(cp))));
+		printf("addr [%d]: %s\n", i, addr_to_numeric(&(addrp[i])));
+#endif
+	}
+	if (cp) xtables_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+	free(buffer);
+
+#if DEBUG
+	printf("addr nr: %d\n", i);
+#endif
+
+	return i;
+}
+
+static void rt_parse(struct xt_option_call *cb)
+{
+	struct ip6t_rt *rtinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_RT_TYPE:
+		if (cb->invert)
+			rtinfo->invflags |= IP6T_RT_INV_TYP;
+		rtinfo->flags |= IP6T_RT_TYP;
+		break;
+	case O_RT_SEGSLEFT:
+		if (cb->nvals == 1)
+			rtinfo->segsleft[1] = rtinfo->segsleft[0];
+		if (cb->invert)
+			rtinfo->invflags |= IP6T_RT_INV_SGS;
+		rtinfo->flags |= IP6T_RT_SGS;
+		break;
+	case O_RT_LEN:
+		if (cb->invert)
+			rtinfo->invflags |= IP6T_RT_INV_LEN;
+		rtinfo->flags |= IP6T_RT_LEN;
+		break;
+	case O_RT0RES:
+		if (!(cb->xflags & F_RT_TYPE) || rtinfo->rt_type != 0 ||
+		    rtinfo->invflags & IP6T_RT_INV_TYP)
+			xtables_error(PARAMETER_PROBLEM,
+				   "`--rt-type 0' required before `--rt-0-res'");
+		rtinfo->flags |= IP6T_RT_RES;
+		break;
+	case O_RT0ADDRS:
+		if (!(cb->xflags & F_RT_TYPE) || rtinfo->rt_type != 0 ||
+		    rtinfo->invflags & IP6T_RT_INV_TYP)
+			xtables_error(PARAMETER_PROBLEM,
+				   "`--rt-type 0' required before `--rt-0-addrs'");
+		rtinfo->addrnr = parse_addresses(cb->arg, rtinfo->addrs);
+		rtinfo->flags |= IP6T_RT_FST;
+		break;
+	case O_RT0NSTRICT:
+		if (!(cb->xflags & F_RT0ADDRS))
+			xtables_error(PARAMETER_PROBLEM,
+				   "`--rt-0-addr ...' required before `--rt-0-not-strict'");
+		rtinfo->flags |= IP6T_RT_FST_NSTRICT;
+		break;
+	}
+}
+
+static void
+print_nums(const char *name, uint32_t min, uint32_t max,
+	    int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFFFFFF || invert) {
+		printf(" %s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			printf("%u", min);
+		} else {
+			printf("s:%s", inv);
+			printf("%u",min);
+			printf(":");
+			printf("%u",max);
+		}
+	}
+}
+
+static void
+print_addresses(unsigned int addrnr, struct in6_addr *addrp)
+{
+	unsigned int i;
+
+	for(i=0; i<addrnr; i++){
+		printf("%c%s", (i==0)?' ':',', addr_to_numeric(&(addrp[i])));
+	}
+}
+
+static void rt_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+	printf(" rt");
+	if (rtinfo->flags & IP6T_RT_TYP)
+	    printf(" type:%s%d", rtinfo->invflags & IP6T_RT_INV_TYP ? "!" : "",
+		    rtinfo->rt_type);
+	print_nums("segsleft", rtinfo->segsleft[0], rtinfo->segsleft[1],
+		    rtinfo->invflags & IP6T_RT_INV_SGS);
+	if (rtinfo->flags & IP6T_RT_LEN) {
+		printf(" length");
+		printf(":%s", rtinfo->invflags & IP6T_RT_INV_LEN ? "!" : "");
+		printf("%u", rtinfo->hdrlen);
+	}
+	if (rtinfo->flags & IP6T_RT_RES) printf(" reserved");
+	if (rtinfo->flags & IP6T_RT_FST) printf(" 0-addrs");
+	print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+	if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf(" 0-not-strict");
+	if (rtinfo->invflags & ~IP6T_RT_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       rtinfo->invflags & ~IP6T_RT_INV_MASK);
+}
+
+static void rt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+	if (rtinfo->flags & IP6T_RT_TYP) {
+		printf("%s --rt-type %u",
+			(rtinfo->invflags & IP6T_RT_INV_TYP) ? " !" : "",
+			rtinfo->rt_type);
+	}
+
+	if (!(rtinfo->segsleft[0] == 0
+	    && rtinfo->segsleft[1] == 0xFFFFFFFF)) {
+		printf("%s --rt-segsleft ",
+			(rtinfo->invflags & IP6T_RT_INV_SGS) ? " !" : "");
+		if (rtinfo->segsleft[0]
+		    != rtinfo->segsleft[1])
+			printf("%u:%u",
+			       rtinfo->segsleft[0],
+			       rtinfo->segsleft[1]);
+		else
+			printf("%u",
+			       rtinfo->segsleft[0]);
+	}
+
+	if (rtinfo->flags & IP6T_RT_LEN) {
+		printf("%s --rt-len %u",
+			(rtinfo->invflags & IP6T_RT_INV_LEN) ? " !" : "", 
+			rtinfo->hdrlen);
+	}
+
+	if (rtinfo->flags & IP6T_RT_RES) printf(" --rt-0-res");
+	if (rtinfo->flags & IP6T_RT_FST) printf(" --rt-0-addrs");
+	print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+	if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf(" --rt-0-not-strict");
+
+}
+
+static struct xtables_match rt_mt6_reg = {
+	.name		= "rt",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV6,
+	.size		= XT_ALIGN(sizeof(struct ip6t_rt)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ip6t_rt)),
+	.help		= rt_help,
+	.x6_parse	= rt_parse,
+	.print		= rt_print,
+	.save		= rt_save,
+	.x6_options	= rt_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&rt_mt6_reg);
+}
diff --git a/extensions/libip6t_rt.man b/extensions/libip6t_rt.man
new file mode 100644
index 0000000..0443e0a
--- /dev/null
+++ b/extensions/libip6t_rt.man
@@ -0,0 +1,19 @@
+Match on IPv6 routing header
+.TP
+[\fB!\fP] \fB\-\-rt\-type\fP \fItype\fP
+Match the type (numeric).
+.TP
+[\fB!\fP] \fB\-\-rt\-segsleft\fP \fInum\fP[\fB:\fP\fInum\fP]
+Match the `segments left' field (range).
+.TP
+[\fB!\fP] \fB\-\-rt\-len\fP \fIlength\fP
+Match the length of this header.
+.TP
+\fB\-\-rt\-0\-res\fP
+Match the reserved field, too (type=0)
+.TP
+\fB\-\-rt\-0\-addrs\fP \fIaddr\fP[\fB,\fP\fIaddr\fP...]
+Match type=0 addresses (list).
+.TP
+\fB\-\-rt\-0\-not\-strict\fP
+List of type=0 addresses is not a strict list.
diff --git a/extensions/libipt_CLUSTERIP.c b/extensions/libipt_CLUSTERIP.c
new file mode 100644
index 0000000..f4b638b
--- /dev/null
+++ b/extensions/libipt_CLUSTERIP.c
@@ -0,0 +1,195 @@
+/* Shared library add-on to iptables to add CLUSTERIP target support. 
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Development of this code was funded by SuSE AG, http://www.suse.com/
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
+
+enum {
+	O_NEW = 0,
+	O_HASHMODE,
+	O_CLUSTERMAC,
+	O_TOTAL_NODES,
+	O_LOCAL_NODE,
+	O_HASH_INIT,
+	F_NEW         = 1 << O_NEW,
+	F_HASHMODE    = 1 << O_HASHMODE,
+	F_CLUSTERMAC  = 1 << O_CLUSTERMAC,
+	F_TOTAL_NODES = 1 << O_TOTAL_NODES,
+	F_LOCAL_NODE  = 1 << O_LOCAL_NODE,
+	F_FULL        = F_NEW | F_HASHMODE | F_CLUSTERMAC |
+	                F_TOTAL_NODES | F_LOCAL_NODE,
+};
+
+static void CLUSTERIP_help(void)
+{
+	printf(
+"CLUSTERIP target options:\n"
+"  --new			 Create a new ClusterIP\n"
+"  --hashmode <mode>		 Specify hashing mode\n"
+"					sourceip\n"
+"					sourceip-sourceport\n"
+"					sourceip-sourceport-destport\n"
+"  --clustermac <mac>		 Set clusterIP MAC address\n"
+"  --total-nodes <num>		 Set number of total nodes in cluster\n"
+"  --local-node <num>		 Set the local node number\n"
+"  --hash-init <num>		 Set init value of the Jenkins hash\n");
+}
+
+#define s struct ipt_clusterip_tgt_info
+static const struct xt_option_entry CLUSTERIP_opts[] = {
+	{.name = "new", .id = O_NEW, .type = XTTYPE_NONE},
+	{.name = "hashmode", .id = O_HASHMODE, .type = XTTYPE_STRING,
+	 .also = O_NEW},
+	{.name = "clustermac", .id = O_CLUSTERMAC, .type = XTTYPE_ETHERMAC,
+	 .also = O_NEW, .flags = XTOPT_PUT, XTOPT_POINTER(s, clustermac)},
+	{.name = "total-nodes", .id = O_TOTAL_NODES, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, num_total_nodes),
+	 .also = O_NEW, .max = CLUSTERIP_MAX_NODES},
+	{.name = "local-node", .id = O_LOCAL_NODE, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, local_nodes[0]),
+	 .also = O_NEW, .max = CLUSTERIP_MAX_NODES},
+	{.name = "hash-init", .id = O_HASH_INIT, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hash_initval),
+	 .also = O_NEW, .max = UINT_MAX},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void CLUSTERIP_parse(struct xt_option_call *cb)
+{
+	struct ipt_clusterip_tgt_info *cipinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_NEW:
+		cipinfo->flags |= CLUSTERIP_FLAG_NEW;
+		break;
+	case O_HASHMODE:
+		if (strcmp(cb->arg, "sourceip") == 0)
+			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP;
+		else if (strcmp(cb->arg, "sourceip-sourceport") == 0)
+			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT;
+		else if (strcmp(cb->arg, "sourceip-sourceport-destport") == 0)
+			cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT_DPT;
+		else
+			xtables_error(PARAMETER_PROBLEM, "Unknown hashmode \"%s\"\n",
+				   cb->arg);
+		break;
+	case O_CLUSTERMAC:
+		if (!(cipinfo->clustermac[0] & 0x01))
+			xtables_error(PARAMETER_PROBLEM, "MAC has to be a multicast ethernet address\n");
+		break;
+	case O_LOCAL_NODE:
+		cipinfo->num_local_nodes = 1;
+		break;
+	}
+}
+
+static void CLUSTERIP_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		return;
+	if ((cb->xflags & F_FULL) == F_FULL)
+		return;
+
+	xtables_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n");
+}
+
+static const char *hashmode2str(enum clusterip_hashmode mode)
+{
+	const char *retstr;
+	switch (mode) {
+		case CLUSTERIP_HASHMODE_SIP:
+			retstr = "sourceip";
+			break;
+		case CLUSTERIP_HASHMODE_SIP_SPT:
+			retstr = "sourceip-sourceport";
+			break;
+		case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
+			retstr = "sourceip-sourceport-destport";
+			break;
+		default:
+			retstr = "unknown-error";
+			break;
+	}
+	return retstr;
+}
+
+static const char *mac2str(const uint8_t mac[ETH_ALEN])
+{
+	static char buf[ETH_ALEN*3];
+	sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return buf;
+}
+
+static void CLUSTERIP_print(const void *ip,
+                            const struct xt_entry_target *target, int numeric)
+{
+	const struct ipt_clusterip_tgt_info *cipinfo =
+		(const struct ipt_clusterip_tgt_info *)target->data;
+	
+	if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
+		printf(" CLUSTERIP");
+		return;
+	}
+
+	printf(" CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u hash_init=%u",
+		hashmode2str(cipinfo->hash_mode),
+		mac2str(cipinfo->clustermac),
+		cipinfo->num_total_nodes,
+		cipinfo->local_nodes[0],
+		cipinfo->hash_initval);
+}
+
+static void CLUSTERIP_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_clusterip_tgt_info *cipinfo =
+		(const struct ipt_clusterip_tgt_info *)target->data;
+
+	/* if this is not a new entry, we don't need to save target
+	 * parameters */
+	if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW))
+		return;
+
+	printf(" --new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u",
+	       hashmode2str(cipinfo->hash_mode),
+	       mac2str(cipinfo->clustermac),
+	       cipinfo->num_total_nodes,
+	       cipinfo->local_nodes[0],
+	       cipinfo->hash_initval);
+}
+
+static struct xtables_target clusterip_tg_reg = {
+	.name		= "CLUSTERIP",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)),
+	.userspacesize	= offsetof(struct ipt_clusterip_tgt_info, config),
+ 	.help		= CLUSTERIP_help,
+	.x6_parse	= CLUSTERIP_parse,
+	.x6_fcheck	= CLUSTERIP_check,
+	.print		= CLUSTERIP_print,
+	.save		= CLUSTERIP_save,
+	.x6_options	= CLUSTERIP_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&clusterip_tg_reg);
+}
diff --git a/extensions/libipt_CLUSTERIP.man b/extensions/libipt_CLUSTERIP.man
new file mode 100644
index 0000000..8ec6d6b
--- /dev/null
+++ b/extensions/libipt_CLUSTERIP.man
@@ -0,0 +1,24 @@
+This module allows you to configure a simple cluster of nodes that share
+a certain IP and MAC address without an explicit load balancer in front of
+them.  Connections are statically distributed between the nodes in this
+cluster.
+.TP
+\fB\-\-new\fP
+Create a new ClusterIP.  You always have to set this on the first rule
+for a given ClusterIP.
+.TP
+\fB\-\-hashmode\fP \fImode\fP
+Specify the hashing mode.  Has to be one of
+\fBsourceip\fP, \fBsourceip\-sourceport\fP, \fBsourceip\-sourceport\-destport\fP.
+.TP
+\fB\-\-clustermac\fP \fImac\fP
+Specify the ClusterIP MAC address. Has to be a link\-layer multicast address
+.TP
+\fB\-\-total\-nodes\fP \fInum\fP
+Number of total nodes within this cluster.
+.TP
+\fB\-\-local\-node\fP \fInum\fP
+Local node number within this cluster.
+.TP
+\fB\-\-hash\-init\fP \fIrnd\fP
+Specify the random seed used for hash initialization.
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
new file mode 100644
index 0000000..ff18799
--- /dev/null
+++ b/extensions/libipt_DNAT.c
@@ -0,0 +1,262 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <iptables.h> /* get_kernel_version */
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/nf_nat.h>
+
+enum {
+	O_TO_DEST = 0,
+	O_RANDOM,
+	O_PERSISTENT,
+	O_X_TO_DEST, /* hidden flag */
+	F_TO_DEST   = 1 << O_TO_DEST,
+	F_RANDOM    = 1 << O_RANDOM,
+	F_X_TO_DEST = 1 << O_X_TO_DEST,
+};
+
+/* Dest NAT data consists of a multi-range, indicating where to map
+   to. */
+struct ipt_natinfo
+{
+	struct xt_entry_target t;
+	struct nf_nat_ipv4_multi_range_compat mr;
+};
+
+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,
+};
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
+{
+	unsigned int size;
+
+	/* One rangesize already in struct ipt_natinfo */
+	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+	info = realloc(info, size);
+	if (!info)
+		xtables_error(OTHER_PROBLEM, "Out of memory\n");
+
+	info->t.u.target_size = size;
+	info->mr.range[info->mr.rangesize] = *range;
+	info->mr.rangesize++;
+
+	return info;
+}
+
+/* Ranges expected in network order. */
+static struct xt_entry_target *
+parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
+{
+	struct nf_nat_ipv4_range range;
+	char *arg, *colon, *dash, *error;
+	const struct in_addr *ip;
+
+	arg = strdup(orig_arg);
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+	memset(&range, 0, sizeof(range));
+	colon = strchr(arg, ':');
+
+	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.tcp.port
+				= range.max.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.tcp.port = htons(port);
+			range.max.tcp.port = htons(maxport);
+		}
+		/* Starts with a colon? No IP info...*/
+		if (colon == arg) {
+			free(arg);
+			return &(append_range(info, &range)->t);
+		}
+		*colon = '\0';
+	}
+
+	range.flags |= NF_NAT_RANGE_MAP_IPS;
+	dash = strchr(arg, '-');
+	if (colon && dash && dash > colon)
+		dash = NULL;
+
+	if (dash)
+		*dash = '\0';
+
+	ip = xtables_numeric_to_ipaddr(arg);
+	if (!ip)
+		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+			   arg);
+	range.min_ip = ip->s_addr;
+	if (dash) {
+		ip = xtables_numeric_to_ipaddr(dash+1);
+		if (!ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+				   dash+1);
+		range.max_ip = ip->s_addr;
+	} else
+		range.max_ip = range.min_ip;
+
+	free(arg);
+	return &(append_range(info, &range)->t);
+}
+
+static void DNAT_parse(struct xt_option_call *cb)
+{
+	const struct ipt_entry *entry = cb->xt_entry;
+	struct ipt_natinfo *info = (void *)(*cb->target);
+	int portok;
+
+	if (entry->ip.proto == IPPROTO_TCP
+	    || entry->ip.proto == IPPROTO_UDP
+	    || entry->ip.proto == IPPROTO_SCTP
+	    || entry->ip.proto == IPPROTO_DCCP
+	    || entry->ip.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");
+		}
+		*cb->target = parse_to(cb->arg, portok, info);
+		cb->xflags |= F_X_TO_DEST;
+		break;
+	case O_PERSISTENT:
+		info->mr.range[0].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_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;
+		printf("%s", xtables_ipaddr_to_numeric(&a));
+		if (r->max_ip != r->min_ip) {
+			a.s_addr = r->max_ip;
+			printf("-%s", xtables_ipaddr_to_numeric(&a));
+		}
+	}
+	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)
+			printf("-%hu", ntohs(r->max.tcp.port));
+	}
+}
+
+static void DNAT_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct ipt_natinfo *info = (const void *)target;
+	unsigned int i = 0;
+
+	printf(" to:");
+	for (i = 0; i < info->mr.rangesize; i++) {
+		print_range(&info->mr.range[i]);
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" random");
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
+			printf(" persistent");
+	}
+}
+
+static void DNAT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_natinfo *info = (const void *)target;
+	unsigned int i = 0;
+
+	for (i = 0; i < info->mr.rangesize; i++) {
+		printf(" --to-destination ");
+		print_range(&info->mr.range[i]);
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" --random");
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
+			printf(" --persistent");
+	}
+}
+
+static struct xtables_target dnat_tg_reg = {
+	.name		= "DNAT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.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,
+};
+
+void _init(void)
+{
+	xtables_register_target(&dnat_tg_reg);
+}
diff --git a/extensions/libipt_ECN.c b/extensions/libipt_ECN.c
new file mode 100644
index 0000000..ee09f29
--- /dev/null
+++ b/extensions/libipt_ECN.c
@@ -0,0 +1,145 @@
+/* Shared library add-on to iptables for ECN, $Version$
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ECN.c borrowed heavily from libipt_DSCP.c
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_ECN.h>
+
+enum {
+	O_ECN_TCP_REMOVE = 0,
+	O_ECN_TCP_CWR,
+	O_ECN_TCP_ECE,
+	O_ECN_IP_ECT,
+	F_ECN_TCP_REMOVE = 1 << O_ECN_TCP_REMOVE,
+	F_ECN_TCP_CWR    = 1 << O_ECN_TCP_CWR,
+	F_ECN_TCP_ECE    = 1 << O_ECN_TCP_ECE,
+};
+
+static void ECN_help(void)
+{
+	printf(
+"ECN target options\n"
+"  --ecn-tcp-remove		Remove all ECN bits from TCP header\n");
+}
+
+#if 0
+"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n"
+"  --ecn-ip-ect			Set the IPv4 ECT codepoint (0 to 3)\n"
+"  --ecn-tcp-cwr		Set the IPv4 CWR bit (0 or 1)\n"
+"  --ecn-tcp-ece		Set the IPv4 ECE bit (0 or 1)\n",
+#endif
+
+static const struct xt_option_entry ECN_opts[] = {
+	{.name = "ecn-tcp-remove", .id = O_ECN_TCP_REMOVE, .type = XTTYPE_NONE,
+	 .excl = F_ECN_TCP_CWR | F_ECN_TCP_ECE},
+	{.name = "ecn-tcp-cwr", .id = O_ECN_TCP_CWR, .type = XTTYPE_UINT8,
+	 .min = 0, .max = 1, .excl = F_ECN_TCP_REMOVE},
+	{.name = "ecn-tcp-ece", .id = O_ECN_TCP_ECE, .type = XTTYPE_UINT8,
+	 .min = 0, .max = 1, .excl = F_ECN_TCP_REMOVE},
+	{.name = "ecn-ip-ect", .id = O_ECN_IP_ECT, .type = XTTYPE_UINT8,
+	 .min = 0, .max = 3},
+	XTOPT_TABLEEND,
+};
+
+static void ECN_parse(struct xt_option_call *cb)
+{
+	struct ipt_ECN_info *einfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_ECN_TCP_REMOVE:
+		einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR;
+		einfo->proto.tcp.ece = 0;
+		einfo->proto.tcp.cwr = 0;
+		break;
+	case O_ECN_TCP_CWR:
+		einfo->operation |= IPT_ECN_OP_SET_CWR;
+		einfo->proto.tcp.cwr = cb->val.u8;
+		break;
+	case O_ECN_TCP_ECE:
+		einfo->operation |= IPT_ECN_OP_SET_ECE;
+		einfo->proto.tcp.ece = cb->val.u8;
+		break;
+	case O_ECN_IP_ECT:
+		einfo->operation |= IPT_ECN_OP_SET_IP;
+		einfo->ip_ect = cb->val.u8;
+		break;
+	}
+}
+
+static void ECN_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "ECN target: An operation is required");
+}
+
+static void ECN_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct ipt_ECN_info *einfo =
+		(const struct ipt_ECN_info *)target->data;
+
+	printf(" ECN");
+
+	if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+	    && einfo->proto.tcp.ece == 0
+	    && einfo->proto.tcp.cwr == 0)
+		printf(" TCP remove");
+	else {
+		if (einfo->operation & IPT_ECN_OP_SET_ECE)
+			printf(" ECE=%u", einfo->proto.tcp.ece);
+
+		if (einfo->operation & IPT_ECN_OP_SET_CWR)
+			printf(" CWR=%u", einfo->proto.tcp.cwr);
+
+		if (einfo->operation & IPT_ECN_OP_SET_IP)
+			printf(" ECT codepoint=%u", einfo->ip_ect);
+	}
+}
+
+static void ECN_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_ECN_info *einfo =
+		(const struct ipt_ECN_info *)target->data;
+
+	if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+	    && einfo->proto.tcp.ece == 0
+	    && einfo->proto.tcp.cwr == 0)
+		printf(" --ecn-tcp-remove");
+	else {
+
+		if (einfo->operation & IPT_ECN_OP_SET_ECE)
+			printf(" --ecn-tcp-ece %d", einfo->proto.tcp.ece);
+
+		if (einfo->operation & IPT_ECN_OP_SET_CWR)
+			printf(" --ecn-tcp-cwr %d", einfo->proto.tcp.cwr);
+
+		if (einfo->operation & IPT_ECN_OP_SET_IP)
+			printf(" --ecn-ip-ect %d", einfo->ip_ect);
+	}
+}
+
+static struct xtables_target ecn_tg_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)),
+	.help		= ECN_help,
+	.print		= ECN_print,
+	.save		= ECN_save,
+	.x6_parse	= ECN_parse,
+	.x6_fcheck	= ECN_check,
+	.x6_options	= ECN_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&ecn_tg_reg);
+}
diff --git a/extensions/libipt_ECN.man b/extensions/libipt_ECN.man
new file mode 100644
index 0000000..a9cbe10
--- /dev/null
+++ b/extensions/libipt_ECN.man
@@ -0,0 +1,7 @@
+This target allows to selectively work around known ECN blackholes.
+It can only be used in the mangle table.
+.TP
+\fB\-\-ecn\-tcp\-remove\fP
+Remove all ECN bits from the TCP header.  Of course, it can only be used
+in conjunction with
+\fB\-p tcp\fP.
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
new file mode 100644
index 0000000..77f16d1
--- /dev/null
+++ b/extensions/libipt_LOG.c
@@ -0,0 +1,186 @@
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#define LOG_DEFAULT_LEVEL LOG_WARNING
+
+#ifndef IPT_LOG_UID /* Old kernel */
+#define IPT_LOG_UID	0x08	/* Log UID owning local socket */
+#undef  IPT_LOG_MASK
+#define IPT_LOG_MASK	0x0f
+#endif
+
+enum {
+	O_LOG_LEVEL = 0,
+	O_LOG_PREFIX,
+	O_LOG_TCPSEQ,
+	O_LOG_TCPOPTS,
+	O_LOG_IPOPTS,
+	O_LOG_UID,
+	O_LOG_MAC,
+};
+
+static void LOG_help(void)
+{
+	printf(
+"LOG target options:\n"
+" --log-level level		Level of logging (numeric or see syslog.conf)\n"
+" --log-prefix prefix		Prefix log messages with this prefix.\n\n"
+" --log-tcp-sequence		Log TCP sequence numbers.\n\n"
+" --log-tcp-options		Log TCP options.\n\n"
+" --log-ip-options		Log IP options.\n\n"
+" --log-uid			Log UID owning the local socket.\n\n"
+" --log-macdecode		Decode MAC addresses and protocol.\n\n");
+}
+
+#define s struct ipt_log_info
+static const struct xt_option_entry LOG_opts[] = {
+	{.name = "log-level", .id = O_LOG_LEVEL, .type = XTTYPE_SYSLOGLEVEL,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, level)},
+	{.name = "log-prefix", .id = O_LOG_PREFIX, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix), .min = 1},
+	{.name = "log-tcp-sequence", .id = O_LOG_TCPSEQ, .type = XTTYPE_NONE},
+	{.name = "log-tcp-options", .id = O_LOG_TCPOPTS, .type = XTTYPE_NONE},
+	{.name = "log-ip-options", .id = O_LOG_IPOPTS, .type = XTTYPE_NONE},
+	{.name = "log-uid", .id = O_LOG_UID, .type = XTTYPE_NONE},
+	{.name = "log-macdecode", .id = O_LOG_MAC, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void LOG_init(struct xt_entry_target *t)
+{
+	struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data;
+
+	loginfo->level = LOG_DEFAULT_LEVEL;
+
+}
+
+struct ipt_log_names {
+	const char *name;
+	unsigned int level;
+};
+
+static const struct ipt_log_names ipt_log_names[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },		/* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },		/* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static void LOG_parse(struct xt_option_call *cb)
+{
+	struct ipt_log_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_LOG_PREFIX:
+		if (strchr(cb->arg, '\n') != NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Newlines not allowed in --log-prefix");
+		break;
+	case O_LOG_TCPSEQ:
+		info->logflags |= IPT_LOG_TCPSEQ;
+		break;
+	case O_LOG_TCPOPTS:
+		info->logflags |= IPT_LOG_TCPOPT;
+		break;
+	case O_LOG_IPOPTS:
+		info->logflags |= IPT_LOG_IPOPT;
+		break;
+	case O_LOG_UID:
+		info->logflags |= IPT_LOG_UID;
+		break;
+	case O_LOG_MAC:
+		info->logflags |= IPT_LOG_MACDECODE;
+		break;
+	}
+}
+
+static void LOG_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct ipt_log_info *loginfo
+		= (const struct ipt_log_info *)target->data;
+	unsigned int i = 0;
+
+	printf(" LOG");
+	if (numeric)
+		printf(" flags %u level %u",
+		       loginfo->logflags, loginfo->level);
+	else {
+		for (i = 0; i < ARRAY_SIZE(ipt_log_names); ++i)
+			if (loginfo->level == ipt_log_names[i].level) {
+				printf(" level %s", ipt_log_names[i].name);
+				break;
+			}
+		if (i == ARRAY_SIZE(ipt_log_names))
+			printf(" UNKNOWN level %u", loginfo->level);
+		if (loginfo->logflags & IPT_LOG_TCPSEQ)
+			printf(" tcp-sequence");
+		if (loginfo->logflags & IPT_LOG_TCPOPT)
+			printf(" tcp-options");
+		if (loginfo->logflags & IPT_LOG_IPOPT)
+			printf(" ip-options");
+		if (loginfo->logflags & IPT_LOG_UID)
+			printf(" uid");
+		if (loginfo->logflags & IPT_LOG_MACDECODE)
+			printf(" macdecode");
+		if (loginfo->logflags & ~(IPT_LOG_MASK))
+			printf(" unknown-flags");
+	}
+
+	if (strcmp(loginfo->prefix, "") != 0)
+		printf(" prefix \"%s\"", loginfo->prefix);
+}
+
+static void LOG_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_log_info *loginfo
+		= (const struct ipt_log_info *)target->data;
+
+	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);
+
+	if (loginfo->logflags & IPT_LOG_TCPSEQ)
+		printf(" --log-tcp-sequence");
+	if (loginfo->logflags & IPT_LOG_TCPOPT)
+		printf(" --log-tcp-options");
+	if (loginfo->logflags & IPT_LOG_IPOPT)
+		printf(" --log-ip-options");
+	if (loginfo->logflags & IPT_LOG_UID)
+		printf(" --log-uid");
+	if (loginfo->logflags & IPT_LOG_MACDECODE)
+		printf(" --log-macdecode");
+}
+
+static struct xtables_target log_tg_reg = {
+	.name          = "LOG",
+	.version       = XTABLES_VERSION,
+	.family        = NFPROTO_IPV4,
+	.size          = XT_ALIGN(sizeof(struct ipt_log_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct ipt_log_info)),
+	.help          = LOG_help,
+	.init          = LOG_init,
+	.print         = LOG_print,
+	.save          = LOG_save,
+	.x6_parse      = LOG_parse,
+	.x6_options    = LOG_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&log_tg_reg);
+}
diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c
new file mode 100644
index 0000000..ea07445
--- /dev/null
+++ b/extensions/libipt_MASQUERADE.c
@@ -0,0 +1,154 @@
+#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_ipv4/ip_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,
+};
+
+static void MASQUERADE_init(struct xt_entry_target *t)
+{
+	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;
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
+{
+	char *end;
+	unsigned int port, maxport;
+
+	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);
+
+	switch (*end) {
+	case '\0':
+		mr->range[0].min.tcp.port
+			= mr->range[0].max.tcp.port
+			= htons(port);
+		return;
+	case '-':
+		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX))
+			break;
+
+		if (maxport < port)
+			break;
+
+		mr->range[0].min.tcp.port = htons(port);
+		mr->range[0].max.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 ipt_entry *entry = cb->xt_entry;
+	int portok;
+	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
+
+	if (entry->ip.proto == IPPROTO_TCP
+	    || entry->ip.proto == IPPROTO_UDP
+	    || entry->ip.proto == IPPROTO_SCTP
+	    || entry->ip.proto == IPPROTO_DCCP
+	    || entry->ip.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, mr);
+		break;
+	case O_RANDOM:
+		mr->range[0].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_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
+
+	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 & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" random");
+}
+
+static void
+MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
+{
+	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 & 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 & NF_NAT_RANGE_PROTO_RANDOM)
+		printf(" --random");
+}
+
+static struct xtables_target masquerade_tg_reg = {
+	.name		= "MASQUERADE",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.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,
+	.print		= MASQUERADE_print,
+	.save		= MASQUERADE_save,
+	.x6_options	= MASQUERADE_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&masquerade_tg_reg);
+}
diff --git a/extensions/libipt_MIRROR.c b/extensions/libipt_MIRROR.c
new file mode 100644
index 0000000..fb78751
--- /dev/null
+++ b/extensions/libipt_MIRROR.c
@@ -0,0 +1,15 @@
+/* Shared library add-on to iptables to add MIRROR target support. */
+#include <xtables.h>
+
+static struct xtables_target mirror_tg_reg = {
+	.name		= "MIRROR",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(0),
+	.userspacesize	= XT_ALIGN(0),
+};
+
+void _init(void)
+{
+	xtables_register_target(&mirror_tg_reg);
+}
diff --git a/extensions/libipt_MIRROR.man b/extensions/libipt_MIRROR.man
new file mode 100644
index 0000000..7b720bc
--- /dev/null
+++ b/extensions/libipt_MIRROR.man
@@ -0,0 +1,12 @@
+This is an experimental demonstration target which inverts the source
+and destination fields in the IP header and retransmits the packet.
+It is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains, and user-defined chains which are only called from those
+chains.  Note that the outgoing packets are
+.B NOT
+seen by any packet filtering chains, connection tracking or NAT, to
+avoid loops and other problems.
diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c
new file mode 100644
index 0000000..dee7b01
--- /dev/null
+++ b/extensions/libipt_NETMAP.c
@@ -0,0 +1,106 @@
+/* Shared library add-on to iptables to add static NAT support.
+   Author: Svenning Soerensen <svenning@post5.tele.dk>
+*/
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <xtables.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 int
+netmask2bits(uint32_t netmask)
+{
+	uint32_t bm;
+	int bits;
+
+	netmask = ntohl(netmask);
+	for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
+		bits++;
+	if (netmask)
+		return -1; /* holes in netmask */
+	return bits;
+}
+
+static void NETMAP_init(struct xt_entry_target *t)
+{
+	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;
+}
+
+static void NETMAP_parse(struct xt_option_call *cb)
+{
+	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 |= 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;
+}
+
+static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	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;
+
+	a.s_addr = r->min_ip;
+	printf("%s", xtables_ipaddr_to_numeric(&a));
+	a.s_addr = ~(r->min_ip ^ r->max_ip);
+	bits = netmask2bits(a.s_addr);
+	if (bits < 0)
+		printf("/%s", xtables_ipaddr_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_IPV4,
+	.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,
+	.print		= NETMAP_print,
+	.save		= NETMAP_save,
+	.x6_options	= NETMAP_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&netmap_tg_reg);
+}
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
new file mode 100644
index 0000000..610a949
--- /dev/null
+++ b/extensions/libipt_REDIRECT.c
@@ -0,0 +1,155 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h */
+#include <linux/netfilter_ipv4/ip_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,
+};
+
+static void REDIRECT_init(struct xt_entry_target *t)
+{
+	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;
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
+{
+	char *end = "";
+	unsigned int port, maxport;
+
+	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)
+		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
+
+	switch (*end) {
+	case '\0':
+		mr->range[0].min.tcp.port
+			= mr->range[0].max.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;
+
+		mr->range[0].min.tcp.port = htons(port);
+		mr->range[0].max.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 ipt_entry *entry = cb->xt_entry;
+	struct nf_nat_ipv4_multi_range_compat *mr = (void *)(*cb->target)->data;
+	int portok;
+
+	if (entry->ip.proto == IPPROTO_TCP
+	    || entry->ip.proto == IPPROTO_UDP
+	    || entry->ip.proto == IPPROTO_SCTP
+	    || entry->ip.proto == IPPROTO_DCCP
+	    || entry->ip.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, mr);
+		if (cb->xflags & F_RANDOM)
+			mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+		break;
+	case O_RANDOM:
+		if (cb->xflags & F_TO_PORTS)
+			mr->range[0].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_ipv4_multi_range_compat *mr = (const void *)target->data;
+	const struct nf_nat_ipv4_range *r = &mr->range[0];
+
+	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 & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" random");
+	}
+}
+
+static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
+{
+	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 & 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 & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" --random");
+	}
+}
+
+static struct xtables_target redirect_tg_reg = {
+	.name		= "REDIRECT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.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,
+	.print		= REDIRECT_print,
+	.save		= REDIRECT_save,
+	.x6_options	= REDIRECT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&redirect_tg_reg);
+}
diff --git a/extensions/libipt_REJECT.c b/extensions/libipt_REJECT.c
new file mode 100644
index 0000000..362c65e
--- /dev/null
+++ b/extensions/libipt_REJECT.c
@@ -0,0 +1,160 @@
+/* Shared library add-on to iptables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_REJECT.h>
+#include <linux/version.h>
+
+/* If we are compiling against a kernel that does not support
+ * IPT_ICMP_ADMIN_PROHIBITED, we are emulating it.
+ * The result will be a plain DROP of the packet instead of
+ * reject. -- Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ */
+#ifndef IPT_ICMP_ADMIN_PROHIBITED
+#define IPT_ICMP_ADMIN_PROHIBITED	IPT_TCP_RESET + 1
+#endif
+
+struct reject_names {
+	const char *name;
+	const char *alias;
+	enum ipt_reject_with with;
+	const char *desc;
+};
+
+enum {
+	O_REJECT_WITH = 0,
+};
+
+static const struct reject_names reject_table[] = {
+	{"icmp-net-unreachable", "net-unreach",
+		IPT_ICMP_NET_UNREACHABLE, "ICMP network unreachable"},
+	{"icmp-host-unreachable", "host-unreach",
+		IPT_ICMP_HOST_UNREACHABLE, "ICMP host unreachable"},
+	{"icmp-proto-unreachable", "proto-unreach",
+		IPT_ICMP_PROT_UNREACHABLE, "ICMP protocol unreachable"},
+	{"icmp-port-unreachable", "port-unreach",
+		IPT_ICMP_PORT_UNREACHABLE, "ICMP port unreachable (default)"},
+#if 0
+	{"echo-reply", "echoreply",
+	 IPT_ICMP_ECHOREPLY, "for ICMP echo only: faked ICMP echo reply"},
+#endif
+	{"icmp-net-prohibited", "net-prohib",
+	 IPT_ICMP_NET_PROHIBITED, "ICMP network prohibited"},
+	{"icmp-host-prohibited", "host-prohib",
+	 IPT_ICMP_HOST_PROHIBITED, "ICMP host prohibited"},
+	{"tcp-reset", "tcp-rst",
+	 IPT_TCP_RESET, "TCP RST packet"},
+	{"icmp-admin-prohibited", "admin-prohib",
+	 IPT_ICMP_ADMIN_PROHIBITED, "ICMP administratively prohibited (*)"}
+};
+
+static void
+print_reject_types(void)
+{
+	unsigned int i;
+
+	printf("Valid reject types:\n");
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i) {
+		printf("    %-25s\t%s\n", reject_table[i].name, reject_table[i].desc);
+		printf("    %-25s\talias\n", reject_table[i].alias);
+	}
+	printf("\n");
+}
+
+static void REJECT_help(void)
+{
+	printf(
+"REJECT target options:\n"
+"--reject-with type              drop input packet and send back\n"
+"                                a reply packet according to type:\n");
+
+	print_reject_types();
+
+	printf("(*) See man page or read the INCOMPATIBILITES file for compatibility issues.\n");
+}
+
+static const struct xt_option_entry REJECT_opts[] = {
+	{.name = "reject-with", .id = O_REJECT_WITH, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static void REJECT_init(struct xt_entry_target *t)
+{
+	struct ipt_reject_info *reject = (struct ipt_reject_info *)t->data;
+
+	/* default */
+	reject->with = IPT_ICMP_PORT_UNREACHABLE;
+
+}
+
+static void REJECT_parse(struct xt_option_call *cb)
+{
+	struct ipt_reject_info *reject = cb->data;
+	unsigned int i;
+
+	xtables_option_parse(cb);
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (strncasecmp(reject_table[i].name,
+		      cb->arg, strlen(cb->arg)) == 0 ||
+		    strncasecmp(reject_table[i].alias,
+		      cb->arg, strlen(cb->arg)) == 0) {
+			reject->with = reject_table[i].with;
+			return;
+		}
+	/* This due to be dropped late in 2.4 pre-release cycle --RR */
+	if (strncasecmp("echo-reply", cb->arg, strlen(cb->arg)) == 0 ||
+	    strncasecmp("echoreply", cb->arg, strlen(cb->arg)) == 0)
+		fprintf(stderr, "--reject-with echo-reply no longer"
+			" supported\n");
+	xtables_error(PARAMETER_PROBLEM,
+		"unknown reject type \"%s\"", cb->arg);
+}
+
+static void REJECT_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct ipt_reject_info *reject
+		= (const struct ipt_reject_info *)target->data;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (reject_table[i].with == reject->with)
+			break;
+	printf(" reject-with %s", reject_table[i].name);
+}
+
+static void REJECT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_reject_info *reject
+		= (const struct ipt_reject_info *)target->data;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(reject_table); ++i)
+		if (reject_table[i].with == reject->with)
+			break;
+
+	printf(" --reject-with %s", reject_table[i].name);
+}
+
+static struct xtables_target reject_tg_reg = {
+	.name		= "REJECT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_reject_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ipt_reject_info)),
+	.help		= REJECT_help,
+	.init		= REJECT_init,
+	.print		= REJECT_print,
+	.save		= REJECT_save,
+	.x6_parse	= REJECT_parse,
+	.x6_options	= REJECT_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&reject_tg_reg);
+}
diff --git a/extensions/libipt_REJECT.man b/extensions/libipt_REJECT.man
new file mode 100644
index 0000000..926da03
--- /dev/null
+++ b/extensions/libipt_REJECT.man
@@ -0,0 +1,32 @@
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains.  The following option controls the nature of the error packet
+returned:
+.TP
+\fB\-\-reject\-with\fP \fItype\fP
+The type given can be
+\fBicmp\-net\-unreachable\fP,
+\fBicmp\-host\-unreachable\fP,
+\fBicmp\-port\-unreachable\fP,
+\fBicmp\-proto\-unreachable\fP,
+\fBicmp\-net\-prohibited\fP,
+\fBicmp\-host\-prohibited\fP, or
+\fBicmp\-admin\-prohibited\fP (*),
+which return the appropriate ICMP error message (\fBicmp\-port\-unreachable\fP is
+the default).  The option
+\fBtcp\-reset\fP
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back.  This is mainly useful for blocking 
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.PP
+(*) Using icmp\-admin\-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c
new file mode 100644
index 0000000..5d5bf63
--- /dev/null
+++ b/extensions/libipt_SAME.c
@@ -0,0 +1,186 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter/nf_nat.h>
+#include <linux/netfilter_ipv4/ipt_SAME.h>
+
+enum {
+	O_TO_ADDR = 0,
+	O_NODST,
+	O_RANDOM,
+	F_TO_ADDR = 1 << O_TO_ADDR,
+	F_RANDOM  = 1 << O_RANDOM,
+};
+
+static void SAME_help(void)
+{
+	printf(
+"SAME target options:\n"
+" --to <ipaddr>-<ipaddr>\n"
+"				Addresses to map source to.\n"
+"				 May be specified more than\n"
+"				  once for multiple ranges.\n"
+" --nodst\n"
+"				Don't use destination-ip in\n"
+"				           source selection\n"
+" --random\n"
+"				Randomize source port\n");
+}
+
+static const struct xt_option_entry SAME_opts[] = {
+	{.name = "to", .id = O_TO_ADDR, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	{.name = "nodst", .id = O_NODST, .type = XTTYPE_NONE},
+	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+/* Parses range of IPs */
+static void parse_to(const char *orig_arg, struct nf_nat_ipv4_range *range)
+{
+	char *dash, *arg;
+	const struct in_addr *ip;
+
+	arg = strdup(orig_arg);
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+	range->flags |= NF_NAT_RANGE_MAP_IPS;
+	dash = strchr(arg, '-');
+
+	if (dash)
+		*dash = '\0';
+
+	ip = xtables_numeric_to_ipaddr(arg);
+	if (!ip)
+		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+			   arg);
+	range->min_ip = ip->s_addr;
+
+	if (dash) {
+		ip = xtables_numeric_to_ipaddr(dash+1);
+		if (!ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+				   dash+1);
+	}
+	range->max_ip = ip->s_addr;
+	if (dash)
+		if (range->min_ip > range->max_ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP range \"%s-%s\"\n",
+				   arg, dash+1);
+	free(arg);
+}
+
+static void SAME_parse(struct xt_option_call *cb)
+{
+	struct ipt_same_info *mr = cb->data;
+	unsigned int count;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TO_ADDR:
+		if (mr->rangesize == IPT_SAME_MAX_RANGE)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Too many ranges specified, maximum "
+				   "is %i ranges.\n",
+				   IPT_SAME_MAX_RANGE);
+		parse_to(cb->arg, &mr->range[mr->rangesize]);
+		mr->rangesize++;
+		break;
+	case O_NODST:
+		mr->info |= IPT_SAME_NODST;
+		break;
+	case O_RANDOM:
+		for (count=0; count < mr->rangesize; count++)
+			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)
+{
+	unsigned int count;
+	const struct ipt_same_info *mr = (const void *)target->data;
+	int random_selection = 0;
+	
+	printf(" same:");
+
+	for (count = 0; count < mr->rangesize; count++) {
+		const struct nf_nat_ipv4_range *r = &mr->range[count];
+		struct in_addr a;
+
+		a.s_addr = r->min_ip;
+
+		printf("%s", xtables_ipaddr_to_numeric(&a));
+		a.s_addr = r->max_ip;
+		
+		if (r->min_ip != r->max_ip)
+			printf("-%s", xtables_ipaddr_to_numeric(&a));
+		if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+			random_selection = 1;
+	}
+	
+	if (mr->info & IPT_SAME_NODST)
+		printf(" nodst");
+
+	if (random_selection)
+		printf(" random");
+}
+
+static void SAME_save(const void *ip, const struct xt_entry_target *target)
+{
+	unsigned int count;
+	const struct ipt_same_info *mr = (const void *)target->data;
+	int random_selection = 0;
+
+	for (count = 0; count < mr->rangesize; count++) {
+		const struct nf_nat_ipv4_range *r = &mr->range[count];
+		struct in_addr a;
+
+		a.s_addr = r->min_ip;
+		printf(" --to %s", xtables_ipaddr_to_numeric(&a));
+		a.s_addr = r->max_ip;
+
+		if (r->min_ip != r->max_ip)
+			printf("-%s", xtables_ipaddr_to_numeric(&a));
+		if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
+			random_selection = 1;
+	}
+	
+	if (mr->info & IPT_SAME_NODST)
+		printf(" --nodst");
+
+	if (random_selection)
+		printf(" --random");
+}
+
+static struct xtables_target same_tg_reg = {
+	.name		= "SAME",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_same_info)),
+	.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,
+};
+
+void _init(void)
+{
+	xtables_register_target(&same_tg_reg);
+}
diff --git a/extensions/libipt_SAME.man b/extensions/libipt_SAME.man
new file mode 100644
index 0000000..a99dc73
--- /dev/null
+++ b/extensions/libipt_SAME.man
@@ -0,0 +1,17 @@
+Similar to SNAT/DNAT depending on chain: it takes a range of addresses
+(`\-\-to 1.2.3.4\-1.2.3.7') and gives a client the same
+source-/destination-address for each connection.
+.PP
+N.B.: The DNAT target's \fB\-\-persistent\fP option replaced the SAME target.
+.TP
+\fB\-\-to\fP \fIipaddr\fP[\fB\-\fP\fIipaddr\fP]
+Addresses to map source to. May be specified more than once for
+multiple ranges.
+.TP
+\fB\-\-nodst\fP
+Don't use the destination-ip in the calculations when selecting the
+new source-ip
+.TP
+\fB\-\-random\fP
+Port mapping will be forcibly randomized to avoid attacks based on 
+port prediction (kernel >= 2.6.21).
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
new file mode 100644
index 0000000..1a24f3d
--- /dev/null
+++ b/extensions/libipt_SNAT.c
@@ -0,0 +1,262 @@
+#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_ipv4/ip_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,
+};
+
+/* Source NAT data consists of a multi-range, indicating where to map
+   to. */
+struct ipt_natinfo
+{
+	struct xt_entry_target t;
+	struct nf_nat_ipv4_multi_range_compat mr;
+};
+
+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,
+};
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
+{
+	unsigned int size;
+
+	/* One rangesize already in struct ipt_natinfo */
+	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+	info = realloc(info, size);
+	if (!info)
+		xtables_error(OTHER_PROBLEM, "Out of memory\n");
+
+	info->t.u.target_size = size;
+	info->mr.range[info->mr.rangesize] = *range;
+	info->mr.rangesize++;
+
+	return info;
+}
+
+/* Ranges expected in network order. */
+static struct xt_entry_target *
+parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
+{
+	struct nf_nat_ipv4_range range;
+	char *arg, *colon, *dash, *error;
+	const struct in_addr *ip;
+
+	arg = strdup(orig_arg);
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+	memset(&range, 0, sizeof(range));
+	colon = strchr(arg, ':');
+
+	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.tcp.port
+				= range.max.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.tcp.port = htons(port);
+			range.max.tcp.port = htons(maxport);
+		}
+		/* Starts with a colon? No IP info...*/
+		if (colon == arg) {
+			free(arg);
+			return &(append_range(info, &range)->t);
+		}
+		*colon = '\0';
+	}
+
+	range.flags |= NF_NAT_RANGE_MAP_IPS;
+	dash = strchr(arg, '-');
+	if (colon && dash && dash > colon)
+		dash = NULL;
+
+	if (dash)
+		*dash = '\0';
+
+	ip = xtables_numeric_to_ipaddr(arg);
+	if (!ip)
+		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+			   arg);
+	range.min_ip = ip->s_addr;
+	if (dash) {
+		ip = xtables_numeric_to_ipaddr(dash+1);
+		if (!ip)
+			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
+				   dash+1);
+		range.max_ip = ip->s_addr;
+	} else
+		range.max_ip = range.min_ip;
+
+	free(arg);
+	return &(append_range(info, &range)->t);
+}
+
+static void SNAT_parse(struct xt_option_call *cb)
+{
+	const struct ipt_entry *entry = cb->xt_entry;
+	struct ipt_natinfo *info = (void *)(*cb->target);
+	int portok;
+
+	if (entry->ip.proto == IPPROTO_TCP
+	    || entry->ip.proto == IPPROTO_UDP
+	    || entry->ip.proto == IPPROTO_SCTP
+	    || entry->ip.proto == IPPROTO_DCCP
+	    || entry->ip.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");
+		}
+		*cb->target = parse_to(cb->arg, portok, info);
+		cb->xflags |= F_X_TO_SRC;
+		break;
+	case O_PERSISTENT:
+		info->mr.range[0].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_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;
+		printf("%s", xtables_ipaddr_to_numeric(&a));
+		if (r->max_ip != r->min_ip) {
+			a.s_addr = r->max_ip;
+			printf("-%s", xtables_ipaddr_to_numeric(&a));
+		}
+	}
+	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)
+			printf("-%hu", ntohs(r->max.tcp.port));
+	}
+}
+
+static void SNAT_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct ipt_natinfo *info = (const void *)target;
+	unsigned int i = 0;
+
+	printf(" to:");
+	for (i = 0; i < info->mr.rangesize; i++) {
+		print_range(&info->mr.range[i]);
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" random");
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
+			printf(" persistent");
+	}
+}
+
+static void SNAT_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_natinfo *info = (const void *)target;
+	unsigned int i = 0;
+
+	for (i = 0; i < info->mr.rangesize; i++) {
+		printf(" --to-source ");
+		print_range(&info->mr.range[i]);
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
+			printf(" --random");
+		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
+			printf(" --persistent");
+	}
+}
+
+static struct xtables_target snat_tg_reg = {
+	.name		= "SNAT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.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,
+};
+
+void _init(void)
+{
+	xtables_register_target(&snat_tg_reg);
+}
diff --git a/extensions/libipt_TTL.c b/extensions/libipt_TTL.c
new file mode 100644
index 0000000..0f81280
--- /dev/null
+++ b/extensions/libipt_TTL.c
@@ -0,0 +1,126 @@
+/* Shared library add-on to iptables for the TTL target
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_TTL.h>
+
+enum {
+	O_TTL_SET = 0,
+	O_TTL_INC,
+	O_TTL_DEC,
+	F_TTL_SET = 1 << O_TTL_SET,
+	F_TTL_INC = 1 << O_TTL_INC,
+	F_TTL_DEC = 1 << O_TTL_DEC,
+	F_ANY     = F_TTL_SET | F_TTL_INC | F_TTL_DEC,
+};
+
+#define s struct ipt_TTL_info
+static const struct xt_option_entry TTL_opts[] = {
+	{.name = "ttl-set", .type = XTTYPE_UINT8, .id = O_TTL_SET,
+	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl)},
+	{.name = "ttl-dec", .type = XTTYPE_UINT8, .id = O_TTL_DEC,
+	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl),
+	 .min = 1},
+	{.name = "ttl-inc", .type = XTTYPE_UINT8, .id = O_TTL_INC,
+	 .excl = F_ANY, .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl),
+	 .min = 1},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void TTL_help(void)
+{
+	printf(
+"TTL target options\n"
+"  --ttl-set value		Set TTL to <value 0-255>\n"
+"  --ttl-dec value		Decrement TTL by <value 1-255>\n"
+"  --ttl-inc value		Increment TTL by <value 1-255>\n");
+}
+
+static void TTL_parse(struct xt_option_call *cb)
+{
+	struct ipt_TTL_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TTL_SET:
+		info->mode = IPT_TTL_SET;
+		break;
+	case O_TTL_DEC:
+		info->mode = IPT_TTL_DEC;
+		break;
+	case O_TTL_INC:
+		info->mode = IPT_TTL_INC;
+		break;
+	}
+}
+
+static void TTL_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+				"TTL: You must specify an action");
+}
+
+static void TTL_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_TTL_info *info = 
+		(struct ipt_TTL_info *) target->data;
+
+	switch (info->mode) {
+		case IPT_TTL_SET:
+			printf(" --ttl-set");
+			break;
+		case IPT_TTL_DEC:
+			printf(" --ttl-dec");
+			break;
+
+		case IPT_TTL_INC:
+			printf(" --ttl-inc");
+			break;
+	}
+	printf(" %u", info->ttl);
+}
+
+static void TTL_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct ipt_TTL_info *info =
+		(struct ipt_TTL_info *) target->data;
+
+	printf(" TTL ");
+	switch (info->mode) {
+		case IPT_TTL_SET:
+			printf("set to");
+			break;
+		case IPT_TTL_DEC:
+			printf("decrement by");
+			break;
+		case IPT_TTL_INC:
+			printf("increment by");
+			break;
+	}
+	printf(" %u", info->ttl);
+}
+
+static struct xtables_target ttl_tg_reg = {
+	.name		= "TTL",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_TTL_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ipt_TTL_info)),
+	.help		= TTL_help,
+	.print		= TTL_print,
+	.save		= TTL_save,
+	.x6_parse	= TTL_parse,
+	.x6_fcheck	= TTL_check,
+	.x6_options	= TTL_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&ttl_tg_reg);
+}
diff --git a/extensions/libipt_TTL.man b/extensions/libipt_TTL.man
new file mode 100644
index 0000000..cf3d1a2
--- /dev/null
+++ b/extensions/libipt_TTL.man
@@ -0,0 +1,19 @@
+This is used to modify the IPv4 TTL header field.  The TTL field determines
+how many hops (routers) a packet can traverse until it's time to live is
+exceeded.
+.PP
+Setting or incrementing the TTL field can potentially be very dangerous,
+so it should be avoided at any cost. This target is only valid in
+.B mangle
+table.
+.PP
+.B Don't ever set or increment the value on packets that leave your local network!
+.TP
+\fB\-\-ttl\-set\fP \fIvalue\fP
+Set the TTL value to `value'.
+.TP
+\fB\-\-ttl\-dec\fP \fIvalue\fP
+Decrement the TTL value `value' times.
+.TP
+\fB\-\-ttl\-inc\fP \fIvalue\fP
+Increment the TTL value `value' times.
diff --git a/extensions/libipt_ULOG.c b/extensions/libipt_ULOG.c
new file mode 100644
index 0000000..fafb220
--- /dev/null
+++ b/extensions/libipt_ULOG.c
@@ -0,0 +1,128 @@
+/* Shared library add-on to iptables to add ULOG support.
+ * 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * multipart netlink support based on ideas by Sebastian Zander 
+ * 						<zander@fokus.gmd.de>
+ *
+ * This software is released under the terms of GNU GPL
+ * 
+ * libipt_ULOG.c,v 1.7 2001/01/30 11:55:02 laforge Exp
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+/* For 64bit kernel / 32bit userspace */
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+
+enum {
+	O_ULOG_NLGROUP = 0,
+	O_ULOG_PREFIX,
+	O_ULOG_CPRANGE,
+	O_ULOG_QTHR,
+};
+
+static void ULOG_help(void)
+{
+	printf("ULOG target options:\n"
+	       " --ulog-nlgroup nlgroup		NETLINK group used for logging\n"
+	       " --ulog-cprange size		Bytes of each packet to be passed\n"
+	       " --ulog-qthreshold		Threshold of in-kernel queue\n"
+	       " --ulog-prefix prefix		Prefix log messages with this prefix.\n");
+}
+
+static const struct xt_option_entry ULOG_opts[] = {
+	{.name = "ulog-nlgroup", .id = O_ULOG_NLGROUP, .type = XTTYPE_UINT8,
+	 .min = 1, .max = 32},
+	{.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-qthreshold", .id = O_ULOG_QTHR, .type = XTTYPE_UINT64,
+	 .min = 1, .max = ULOG_MAX_QLEN},
+	XTOPT_TABLEEND,
+};
+
+static void ULOG_init(struct xt_entry_target *t)
+{
+	struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) t->data;
+
+	loginfo->nl_group = ULOG_DEFAULT_NLGROUP;
+	loginfo->qthreshold = ULOG_DEFAULT_QTHRESHOLD;
+
+}
+
+static void ULOG_parse(struct xt_option_call *cb)
+{
+	struct ipt_ulog_info *loginfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_ULOG_NLGROUP:
+		loginfo->nl_group = 1 << (cb->val.u8 - 1);
+		break;
+	case O_ULOG_PREFIX:
+		if (strchr(cb->arg, '\n') != NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Newlines not allowed in --ulog-prefix");
+		break;
+	case O_ULOG_CPRANGE:
+		loginfo->copy_range = cb->val.u64;
+		break;
+	case O_ULOG_QTHR:
+		loginfo->qthreshold = cb->val.u64;
+		break;
+	}
+}
+
+static void ULOG_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_ulog_info *loginfo
+	    = (const struct ipt_ulog_info *) target->data;
+
+	if (strcmp(loginfo->prefix, "") != 0) {
+		fputs(" --ulog-prefix", stdout);
+		xtables_save_string(loginfo->prefix);
+	}
+
+	if (loginfo->nl_group != ULOG_DEFAULT_NLGROUP)
+		printf(" --ulog-nlgroup %d", ffs(loginfo->nl_group));
+	if (loginfo->copy_range)
+		printf(" --ulog-cprange %u", (unsigned int)loginfo->copy_range);
+
+	if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
+		printf(" --ulog-qthreshold %u", (unsigned int)loginfo->qthreshold);
+}
+
+static void ULOG_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct ipt_ulog_info *loginfo
+	    = (const struct ipt_ulog_info *) target->data;
+
+	printf(" ULOG ");
+	printf("copy_range %u nlgroup %d", (unsigned int)loginfo->copy_range,
+	       ffs(loginfo->nl_group));
+	if (strcmp(loginfo->prefix, "") != 0)
+		printf(" prefix \"%s\"", loginfo->prefix);
+	printf(" queue_threshold %u", (unsigned int)loginfo->qthreshold);
+}
+
+static struct xtables_target ulog_tg_reg = {
+	.name		= "ULOG",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_ulog_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ipt_ulog_info)),
+	.help		= ULOG_help,
+	.init		= ULOG_init,
+	.print		= ULOG_print,
+	.save		= ULOG_save,
+	.x6_parse	= ULOG_parse,
+	.x6_options	= ULOG_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&ulog_tg_reg);
+}
diff --git a/extensions/libipt_ULOG.man b/extensions/libipt_ULOG.man
new file mode 100644
index 0000000..c91f776
--- /dev/null
+++ b/extensions/libipt_ULOG.man
@@ -0,0 +1,28 @@
+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 
+socket. One or more userspace processes may then subscribe to various 
+multicast groups and receive the packets.
+Like LOG, this is a "non-terminating target", i.e. rule traversal
+continues at the next rule.
+.TP
+\fB\-\-ulog\-nlgroup\fP \fInlgroup\fP
+This specifies the netlink group (1-32) to which the packet is sent.
+Default value is 1.
+.TP
+\fB\-\-ulog\-prefix\fP \fIprefix\fP
+Prefix log messages with the specified prefix; up to 32 characters
+long, and useful for distinguishing messages in the logs.
+.TP
+\fB\-\-ulog\-cprange\fP \fIsize\fP
+Number of bytes to be copied to userspace.  A value of 0 always copies
+the entire packet, regardless of its size.  Default is 0.
+.TP
+\fB\-\-ulog\-qthreshold\fP \fIsize\fP
+Number of packet to queue inside kernel.  Setting this value to, e.g. 10
+accumulates ten packets inside the kernel and transmits them as one
+netlink multipart message to userspace.  Default is 1 (for backwards
+compatibility).
+.br
diff --git a/extensions/libipt_ah.c b/extensions/libipt_ah.c
new file mode 100644
index 0000000..8cf167c
--- /dev/null
+++ b/extensions/libipt_ah.c
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_ah.h>
+
+enum {
+	O_AHSPI = 0,
+};
+
+static void ah_help(void)
+{
+	printf(
+"ah match options:\n"
+"[!] --ahspi spi[:spi]\n"
+"				match spi (range)\n");
+}
+
+static const struct xt_option_entry ah_opts[] = {
+	{.name = "ahspi", .id = O_AHSPI, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct ipt_ah, spis)},
+	XTOPT_TABLEEND,
+};
+
+static void ah_parse(struct xt_option_call *cb)
+{
+	struct ipt_ah *ahinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->nvals == 1)
+		ahinfo->spis[1] = ahinfo->spis[0];
+	if (cb->invert)
+		ahinfo->invflags |= IPT_AH_INV_SPI;
+}
+
+static void
+print_spis(const char *name, uint32_t min, uint32_t max,
+	    int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFFFFFF || invert) {
+		printf("%s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			printf("%u", min);
+		} else {
+			printf("s:%s", inv);
+			printf("%u",min);
+			printf(":");
+			printf("%u",max);
+		}
+	}
+}
+
+static void ah_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct ipt_ah *ah = (struct ipt_ah *)match->data;
+
+	printf(" ah ");
+	print_spis("spi", ah->spis[0], ah->spis[1],
+		    ah->invflags & IPT_AH_INV_SPI);
+	if (ah->invflags & ~IPT_AH_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       ah->invflags & ~IPT_AH_INV_MASK);
+}
+
+static void ah_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_ah *ahinfo = (struct ipt_ah *)match->data;
+
+	if (!(ahinfo->spis[0] == 0
+	    && ahinfo->spis[1] == 0xFFFFFFFF)) {
+		printf("%s --ahspi ",
+			(ahinfo->invflags & IPT_AH_INV_SPI) ? " !" : "");
+		if (ahinfo->spis[0]
+		    != ahinfo->spis[1])
+			printf("%u:%u",
+			       ahinfo->spis[0],
+			       ahinfo->spis[1]);
+		else
+			printf("%u",
+			       ahinfo->spis[0]);
+	}
+
+}
+
+static struct xtables_match ah_mt_reg = {
+	.name 		= "ah",
+	.version 	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_ah)),
+	.userspacesize 	= XT_ALIGN(sizeof(struct ipt_ah)),
+	.help 		= ah_help,
+	.print 		= ah_print,
+	.save 		= ah_save,
+	.x6_parse	= ah_parse,
+	.x6_options	= ah_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&ah_mt_reg);
+}
diff --git a/extensions/libipt_ah.man b/extensions/libipt_ah.man
new file mode 100644
index 0000000..d26455e
--- /dev/null
+++ b/extensions/libipt_ah.man
@@ -0,0 +1,3 @@
+This module matches the SPIs in Authentication header of IPsec packets.
+.TP
+[\fB!\fP] \fB\-\-ahspi\fP \fIspi\fP[\fB:\fP\fIspi\fP]
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
new file mode 100644
index 0000000..666e7da
--- /dev/null
+++ b/extensions/libipt_icmp.c
@@ -0,0 +1,269 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip6_tables.h */
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* special hack for icmp-type 'any': 
+ * Up to kernel <=2.4.20 the problem was:
+ * '-p icmp ' matches all icmp packets
+ * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
+ * This is now fixed by initializing the field * to icmp type 0xFF
+ * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
+ */
+
+enum {
+	O_ICMP_TYPE = 0,
+};
+
+struct icmp_names {
+	const char *name;
+	uint8_t type;
+	uint8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+	{ "any", 0xFF, 0, 0xFF },
+	{ "echo-reply", 0, 0, 0xFF },
+	/* Alias */ { "pong", 0, 0, 0xFF },
+
+	{ "destination-unreachable", 3, 0, 0xFF },
+	{   "network-unreachable", 3, 0, 0 },
+	{   "host-unreachable", 3, 1, 1 },
+	{   "protocol-unreachable", 3, 2, 2 },
+	{   "port-unreachable", 3, 3, 3 },
+	{   "fragmentation-needed", 3, 4, 4 },
+	{   "source-route-failed", 3, 5, 5 },
+	{   "network-unknown", 3, 6, 6 },
+	{   "host-unknown", 3, 7, 7 },
+	{   "network-prohibited", 3, 9, 9 },
+	{   "host-prohibited", 3, 10, 10 },
+	{   "TOS-network-unreachable", 3, 11, 11 },
+	{   "TOS-host-unreachable", 3, 12, 12 },
+	{   "communication-prohibited", 3, 13, 13 },
+	{   "host-precedence-violation", 3, 14, 14 },
+	{   "precedence-cutoff", 3, 15, 15 },
+
+	{ "source-quench", 4, 0, 0xFF },
+
+	{ "redirect", 5, 0, 0xFF },
+	{   "network-redirect", 5, 0, 0 },
+	{   "host-redirect", 5, 1, 1 },
+	{   "TOS-network-redirect", 5, 2, 2 },
+	{   "TOS-host-redirect", 5, 3, 3 },
+
+	{ "echo-request", 8, 0, 0xFF },
+	/* Alias */ { "ping", 8, 0, 0xFF },
+
+	{ "router-advertisement", 9, 0, 0xFF },
+
+	{ "router-solicitation", 10, 0, 0xFF },
+
+	{ "time-exceeded", 11, 0, 0xFF },
+	/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+	{   "ttl-zero-during-transit", 11, 0, 0 },
+	{   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+	{ "parameter-problem", 12, 0, 0xFF },
+	{   "ip-header-bad", 12, 0, 0 },
+	{   "required-option-missing", 12, 1, 1 },
+
+	{ "timestamp-request", 13, 0, 0xFF },
+
+	{ "timestamp-reply", 14, 0, 0xFF },
+
+	{ "address-mask-request", 17, 0, 0xFF },
+
+	{ "address-mask-reply", 18, 0, 0xFF }
+};
+
+static void
+print_icmptypes(void)
+{
+	unsigned int i;
+	printf("Valid ICMP Types:");
+
+	for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) {
+		if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+			if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+			    && (icmp_codes[i].code_max
+				== icmp_codes[i-1].code_max))
+				printf(" (%s)", icmp_codes[i].name);
+			else
+				printf("\n   %s", icmp_codes[i].name);
+		}
+		else
+			printf("\n%s", icmp_codes[i].name);
+	}
+	printf("\n");
+}
+
+static void icmp_help(void)
+{
+	printf(
+"icmp match options:\n"
+"[!] --icmp-type typename	match icmp type\n"
+"[!] --icmp-type type[/code]	(or numeric type or type/code)\n");
+	print_icmptypes();
+}
+
+static const struct xt_option_entry icmp_opts[] = {
+	{.name = "icmp-type", .id = O_ICMP_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void 
+parse_icmp(const char *icmptype, uint8_t *type, uint8_t code[])
+{
+	static const unsigned int limit = ARRAY_SIZE(icmp_codes);
+	unsigned int match = limit;
+	unsigned int i;
+
+	for (i = 0; i < limit; i++) {
+		if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+		    == 0) {
+			if (match != limit)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Ambiguous ICMP type `%s':"
+					   " `%s' or `%s'?",
+					   icmptype,
+					   icmp_codes[match].name,
+					   icmp_codes[i].name);
+			match = i;
+		}
+	}
+
+	if (match != limit) {
+		*type = icmp_codes[match].type;
+		code[0] = icmp_codes[match].code_min;
+		code[1] = icmp_codes[match].code_max;
+	} else {
+		char *slash;
+		char buffer[strlen(icmptype) + 1];
+		unsigned int number;
+
+		strcpy(buffer, icmptype);
+		slash = strchr(buffer, '/');
+
+		if (slash)
+			*slash = '\0';
+
+		if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid ICMP type `%s'\n", buffer);
+		*type = number;
+		if (slash) {
+			if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX))
+				xtables_error(PARAMETER_PROBLEM,
+					   "Invalid ICMP code `%s'\n",
+					   slash+1);
+			code[0] = code[1] = number;
+		} else {
+			code[0] = 0;
+			code[1] = 0xFF;
+		}
+	}
+}
+
+static void icmp_init(struct xt_entry_match *m)
+{
+	struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
+
+	icmpinfo->type = 0xFF;
+	icmpinfo->code[1] = 0xFF;
+}
+
+static void icmp_parse(struct xt_option_call *cb)
+{
+	struct ipt_icmp *icmpinfo = cb->data;
+
+	xtables_option_parse(cb);
+	parse_icmp(cb->arg, &icmpinfo->type, icmpinfo->code);
+	if (cb->invert)
+		icmpinfo->invflags |= IPT_ICMP_INV;
+}
+
+static void print_icmptype(uint8_t type,
+			   uint8_t code_min, uint8_t code_max,
+			   int invert,
+			   int numeric)
+{
+	if (!numeric) {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i)
+			if (icmp_codes[i].type == type
+			    && icmp_codes[i].code_min == code_min
+			    && icmp_codes[i].code_max == code_max)
+				break;
+
+		if (i != ARRAY_SIZE(icmp_codes)) {
+			printf(" %s%s",
+			       invert ? "!" : "",
+			       icmp_codes[i].name);
+			return;
+		}
+	}
+
+	if (invert)
+		printf(" !");
+
+	printf("type %u", type);
+	if (code_min == code_max)
+		printf(" code %u", code_min);
+	else if (code_min != 0 || code_max != 0xFF)
+		printf(" codes %u-%u", code_min, code_max);
+}
+
+static void icmp_print(const void *ip, const struct xt_entry_match *match,
+                       int numeric)
+{
+	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+	printf(" icmp");
+	print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+		       icmp->invflags & IPT_ICMP_INV,
+		       numeric);
+
+	if (icmp->invflags & ~IPT_ICMP_INV)
+		printf(" Unknown invflags: 0x%X",
+		       icmp->invflags & ~IPT_ICMP_INV);
+}
+
+static void icmp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+	if (icmp->invflags & IPT_ICMP_INV)
+		printf(" !");
+
+	/* special hack for 'any' case */
+	if (icmp->type == 0xFF) {
+		printf(" --icmp-type any");
+	} else {
+		printf(" --icmp-type %u", icmp->type);
+		if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+			printf("/%u", icmp->code[0]);
+	}
+}
+
+static struct xtables_match icmp_mt_reg = {
+	.name		= "icmp",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_icmp)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ipt_icmp)),
+	.help		= icmp_help,
+	.init		= icmp_init,
+	.print		= icmp_print,
+	.save		= icmp_save,
+	.x6_parse	= icmp_parse,
+	.x6_options	= icmp_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&icmp_mt_reg);
+}
diff --git a/extensions/libipt_icmp.man b/extensions/libipt_icmp.man
new file mode 100644
index 0000000..1039704
--- /dev/null
+++ b/extensions/libipt_icmp.man
@@ -0,0 +1,9 @@
+This extension can be used if `\-\-protocol icmp' is specified. It
+provides the following option:
+.TP
+[\fB!\fP] \fB\-\-icmp\-type\fP {\fItype\fP[\fB/\fP\fIcode\fP]|\fItypename\fP}
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, type/code pair, or one of the ICMP type names shown by the command
+.nf
+ iptables \-p icmp \-h
+.fi
diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c
new file mode 100644
index 0000000..a8d9dda
--- /dev/null
+++ b/extensions/libipt_realm.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_realm.h>
+
+enum {
+	O_REALM = 0,
+};
+
+static void realm_help(void)
+{
+	printf(
+"realm match options:\n"
+"[!] --realm value[/mask]\n"
+"				Match realm\n");
+}
+
+static const struct xt_option_entry realm_opts[] = {
+	{.name = "realm", .id = O_REALM, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+/* array of realms from /etc/iproute2/rt_realms */
+static struct xtables_lmap *realms;
+
+static void realm_init(struct xt_entry_match *m)
+{
+	const char file[] = "/etc/iproute2/rt_realms";
+	realms = xtables_lmap_init(file);
+	if (realms == NULL && errno != ENOENT)
+		fprintf(stderr, "Warning: %s: %s\n", file, strerror(errno));
+}
+
+static void realm_parse(struct xt_option_call *cb)
+{
+	struct xt_realm_info *realminfo = cb->data;
+	int id;
+	char *end;
+
+	xtables_option_parse(cb);
+	realminfo->id = strtoul(cb->arg, &end, 0);
+	if (end != cb->arg && (*end == '/' || *end == '\0')) {
+		if (*end == '/')
+			realminfo->mask = strtoul(end+1, &end, 0);
+		else
+			realminfo->mask = 0xffffffff;
+		if (*end != '\0' || end == cb->arg)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Bad realm value \"%s\"", cb->arg);
+	} else {
+		id = xtables_lmap_name2id(realms, cb->arg);
+		if (id == -1)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Realm \"%s\" not found", cb->arg);
+		realminfo->id = id;
+		realminfo->mask = 0xffffffff;
+	}
+	if (cb->invert)
+		realminfo->invert = 1;
+}
+
+static void
+print_realm(unsigned long id, unsigned long mask, int numeric)
+{
+	const char* name = NULL;
+
+	if (mask != 0xffffffff)
+		printf(" 0x%lx/0x%lx", id, mask);
+	else {
+		if (numeric == 0)
+			name = xtables_lmap_id2name(realms, id);
+		if (name)
+			printf(" %s", name);
+		else
+			printf(" 0x%lx", id);
+	}
+}
+
+static void realm_print(const void *ip, const struct xt_entry_match *match,
+                        int numeric)
+{
+	const struct xt_realm_info *ri = (const void *)match->data;
+
+	if (ri->invert)
+		printf(" !");
+
+	printf(" realm");
+	print_realm(ri->id, ri->mask, numeric);
+}
+
+static void realm_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_realm_info *ri = (const void *)match->data;
+
+	if (ri->invert)
+		printf(" !");
+
+	printf(" --realm");
+	print_realm(ri->id, ri->mask, 0);
+}
+
+static struct xtables_match realm_mt_reg = {
+	.name		= "realm",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.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,
+	.save		= realm_save,
+	.x6_parse	= realm_parse,
+	.x6_options	= realm_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&realm_mt_reg);
+}
diff --git a/extensions/libipt_realm.man b/extensions/libipt_realm.man
new file mode 100644
index 0000000..a40b1ad
--- /dev/null
+++ b/extensions/libipt_realm.man
@@ -0,0 +1,7 @@
+This matches the routing realm.  Routing realms are used in complex routing
+setups involving dynamic routing protocols like BGP.
+.TP
+[\fB!\fP] \fB\-\-realm\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Matches a given realm number (and optionally mask). If not a number, value
+can be a named realm from /etc/iproute2/rt_realms (mask can not be used in
+that case).
diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c
new file mode 100644
index 0000000..5fe08cc
--- /dev/null
+++ b/extensions/libipt_ttl.c
@@ -0,0 +1,135 @@
+/* Shared library add-on to iptables to add TTL matching support 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is released under the terms of GNU GPL */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter_ipv4/ipt_ttl.h>
+
+enum {
+	O_TTL_EQ = 0,
+	O_TTL_LT,
+	O_TTL_GT,
+	F_TTL_EQ = 1 << O_TTL_EQ,
+	F_TTL_LT = 1 << O_TTL_LT,
+	F_TTL_GT = 1 << O_TTL_GT,
+	F_ANY    = F_TTL_EQ | F_TTL_LT | F_TTL_GT,
+};
+
+static void ttl_help(void)
+{
+	printf(
+"ttl match options:\n"
+"[!] --ttl-eq value	Match time to live value\n"
+"  --ttl-lt value	Match TTL < value\n"
+"  --ttl-gt value	Match TTL > value\n");
+}
+
+static void ttl_parse(struct xt_option_call *cb)
+{
+	struct ipt_ttl_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TTL_EQ:
+		info->mode = cb->invert ? IPT_TTL_NE : IPT_TTL_EQ;
+		break;
+	case O_TTL_LT:
+		info->mode = IPT_TTL_LT;
+		break;
+	case O_TTL_GT:
+		info->mode = IPT_TTL_GT;
+		break;
+	}
+}
+
+static void ttl_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+			"TTL match: You must specify one of "
+			"`--ttl-eq', `--ttl-lt', `--ttl-gt");
+}
+
+static void ttl_print(const void *ip, const struct xt_entry_match *match,
+                      int numeric)
+{
+	const struct ipt_ttl_info *info = 
+		(struct ipt_ttl_info *) match->data;
+
+	printf(" TTL match ");
+	switch (info->mode) {
+		case IPT_TTL_EQ:
+			printf("TTL ==");
+			break;
+		case IPT_TTL_NE:
+			printf("TTL !=");
+			break;
+		case IPT_TTL_LT:
+			printf("TTL <");
+			break;
+		case IPT_TTL_GT:
+			printf("TTL >");
+			break;
+	}
+	printf(" %u", info->ttl);
+}
+
+static void ttl_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_ttl_info *info =
+		(struct ipt_ttl_info *) match->data;
+
+	switch (info->mode) {
+		case IPT_TTL_EQ:
+			printf(" --ttl-eq");
+			break;
+		case IPT_TTL_NE:
+			printf(" ! --ttl-eq");
+			break;
+		case IPT_TTL_LT:
+			printf(" --ttl-lt");
+			break;
+		case IPT_TTL_GT:
+			printf(" --ttl-gt");
+			break;
+		default:
+			/* error */
+			break;
+	}
+	printf(" %u", info->ttl);
+}
+
+#define s struct ipt_ttl_info
+static const struct xt_option_entry ttl_opts[] = {
+	{.name = "ttl-lt", .id = O_TTL_LT, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl)},
+	{.name = "ttl-gt", .id = O_TTL_GT, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl)},
+	{.name = "ttl-eq", .id = O_TTL_EQ, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, ttl)},
+	{.name = "ttl", .id = O_TTL_EQ, .excl = F_ANY, .type = XTTYPE_UINT8,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static struct xtables_match ttl_mt_reg = {
+	.name		= "ttl",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(sizeof(struct ipt_ttl_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct ipt_ttl_info)),
+	.help		= ttl_help,
+	.print		= ttl_print,
+	.save		= ttl_save,
+	.x6_parse	= ttl_parse,
+	.x6_fcheck	= ttl_check,
+	.x6_options	= ttl_opts,
+};
+
+
+void _init(void) 
+{
+	xtables_register_match(&ttl_mt_reg);
+}
diff --git a/extensions/libipt_ttl.man b/extensions/libipt_ttl.man
new file mode 100644
index 0000000..1f32277
--- /dev/null
+++ b/extensions/libipt_ttl.man
@@ -0,0 +1,10 @@
+This module matches the time to live field in the IP header.
+.TP
+[\fB!\fP] \fB\-\-ttl\-eq\fP \fIttl\fP
+Matches the given TTL value.
+.TP
+\fB\-\-ttl\-gt\fP \fIttl\fP
+Matches if TTL is greater than the given TTL value.
+.TP
+\fB\-\-ttl\-lt\fP \fIttl\fP
+Matches if TTL is less than the given TTL value.
diff --git a/extensions/libipt_unclean.c b/extensions/libipt_unclean.c
new file mode 100644
index 0000000..bc4a4a0
--- /dev/null
+++ b/extensions/libipt_unclean.c
@@ -0,0 +1,15 @@
+/* Shared library add-on to iptables for unclean. */
+#include <xtables.h>
+
+static struct xtables_match unclean_mt_reg = {
+	.name		= "unclean",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_IPV4,
+	.size		= XT_ALIGN(0),
+	.userspacesize	= XT_ALIGN(0),
+};
+
+void _init(void)
+{
+	xtables_register_match(&unclean_mt_reg);
+}
diff --git a/extensions/libipt_unclean.man b/extensions/libipt_unclean.man
new file mode 100644
index 0000000..3fecd55
--- /dev/null
+++ b/extensions/libipt_unclean.man
@@ -0,0 +1,2 @@
+This module takes no options, but attempts to match packets which seem
+malformed or unusual.  This is regarded as experimental.
diff --git a/extensions/libxt_AUDIT.c b/extensions/libxt_AUDIT.c
new file mode 100644
index 0000000..86a61cb
--- /dev/null
+++ b/extensions/libxt_AUDIT.c
@@ -0,0 +1,101 @@
+/* Shared library add-on to xtables for AUDIT
+ *
+ * (C) 2010-2011, Thomas Graf <tgraf@redhat.com>
+ * (C) 2010-2011, Red Hat, Inc.
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_AUDIT.h>
+
+enum {
+	O_AUDIT_TYPE = 0,
+};
+
+static void audit_help(void)
+{
+	printf(
+"AUDIT target options\n"
+"  --type TYPE		Action type to be recorded.\n");
+}
+
+static const struct xt_option_entry audit_opts[] = {
+	{.name = "type", .id = O_AUDIT_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	XTOPT_TABLEEND,
+};
+
+static void audit_parse(struct xt_option_call *cb)
+{
+	struct xt_audit_info *einfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (strcasecmp(cb->arg, "accept") == 0)
+		einfo->type = XT_AUDIT_TYPE_ACCEPT;
+	else if (strcasecmp(cb->arg, "drop") == 0)
+		einfo->type = XT_AUDIT_TYPE_DROP;
+	else if (strcasecmp(cb->arg, "reject") == 0)
+		einfo->type = XT_AUDIT_TYPE_REJECT;
+	else
+		xtables_error(PARAMETER_PROBLEM,
+			   "Bad action type value \"%s\"", cb->arg);
+}
+
+static void audit_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct xt_audit_info *einfo =
+		(const struct xt_audit_info *)target->data;
+
+	printf(" AUDIT ");
+
+	switch(einfo->type) {
+	case XT_AUDIT_TYPE_ACCEPT:
+		printf("accept");
+		break;
+	case XT_AUDIT_TYPE_DROP:
+		printf("drop");
+		break;
+	case XT_AUDIT_TYPE_REJECT:
+		printf("reject");
+		break;
+	}
+}
+
+static void audit_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_audit_info *einfo =
+		(const struct xt_audit_info *)target->data;
+
+	switch(einfo->type) {
+	case XT_AUDIT_TYPE_ACCEPT:
+		printf(" --type accept");
+		break;
+	case XT_AUDIT_TYPE_DROP:
+		printf(" --type drop");
+		break;
+	case XT_AUDIT_TYPE_REJECT:
+		printf(" --type reject");
+		break;
+	}
+}
+
+static struct xtables_target audit_tg_reg = {
+	.name		= "AUDIT",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_UNSPEC,
+	.size		= XT_ALIGN(sizeof(struct xt_audit_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_audit_info)),
+	.help		= audit_help,
+	.print		= audit_print,
+	.save		= audit_save,
+	.x6_parse	= audit_parse,
+	.x6_options	= audit_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&audit_tg_reg);
+}
diff --git a/extensions/libxt_AUDIT.man b/extensions/libxt_AUDIT.man
new file mode 100644
index 0000000..cd79696
--- /dev/null
+++ b/extensions/libxt_AUDIT.man
@@ -0,0 +1,14 @@
+This target allows to create audit records for packets hitting the target.
+It can be used to record accepted, dropped, and rejected packets. See
+auditd(8) for additional details.
+.TP
+\fB\-\-type\fP {\fBaccept\fP|\fBdrop\fP|\fBreject\fP}
+Set type of audit record.
+.PP
+Example:
+.IP
+iptables \-N AUDIT_DROP
+.IP
+iptables \-A AUDIT_DROP \-j AUDIT \-\-type drop
+.IP
+iptables \-A AUDIT_DROP \-j DROP
diff --git a/extensions/libxt_CHECKSUM.c b/extensions/libxt_CHECKSUM.c
new file mode 100644
index 0000000..df9f9b3
--- /dev/null
+++ b/extensions/libxt_CHECKSUM.c
@@ -0,0 +1,77 @@
+/* Shared library add-on to xtables for CHECKSUM
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by Red Hat, Inc
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libxt_CHECKSUM.c borrowed some bits from libipt_ECN.c
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_CHECKSUM.h>
+
+enum {
+	O_CHECKSUM_FILL = 0,
+};
+
+static void CHECKSUM_help(void)
+{
+	printf(
+"CHECKSUM target options\n"
+"  --checksum-fill			Fill in packet checksum.\n");
+}
+
+static const struct xt_option_entry CHECKSUM_opts[] = {
+	{.name = "checksum-fill", .id = O_CHECKSUM_FILL,
+	 .flags = XTOPT_MAND, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static void CHECKSUM_parse(struct xt_option_call *cb)
+{
+	struct xt_CHECKSUM_info *einfo = cb->data;
+
+	xtables_option_parse(cb);
+	einfo->operation = XT_CHECKSUM_OP_FILL;
+}
+
+static void CHECKSUM_print(const void *ip, const struct xt_entry_target *target,
+                      int numeric)
+{
+	const struct xt_CHECKSUM_info *einfo =
+		(const struct xt_CHECKSUM_info *)target->data;
+
+	printf(" CHECKSUM");
+
+	if (einfo->operation & XT_CHECKSUM_OP_FILL)
+		printf(" fill");
+}
+
+static void CHECKSUM_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_CHECKSUM_info *einfo =
+		(const struct xt_CHECKSUM_info *)target->data;
+
+	if (einfo->operation & XT_CHECKSUM_OP_FILL)
+		printf(" --checksum-fill");
+}
+
+static struct xtables_target checksum_tg_reg = {
+	.name		= "CHECKSUM",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_UNSPEC,
+	.size		= XT_ALIGN(sizeof(struct xt_CHECKSUM_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_CHECKSUM_info)),
+	.help		= CHECKSUM_help,
+	.print		= CHECKSUM_print,
+	.save		= CHECKSUM_save,
+	.x6_parse	= CHECKSUM_parse,
+	.x6_options	= CHECKSUM_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&checksum_tg_reg);
+}
diff --git a/extensions/libxt_CHECKSUM.man b/extensions/libxt_CHECKSUM.man
new file mode 100644
index 0000000..92ae700
--- /dev/null
+++ b/extensions/libxt_CHECKSUM.man
@@ -0,0 +1,8 @@
+This target allows to selectively work around broken/old applications.
+It can only be used in the mangle table.
+.TP
+\fB\-\-checksum\-fill\fP
+Compute and fill in the checksum in a packet that lacks a checksum.
+This is particularly useful, if you need to work around old applications
+such as dhcp clients, that do not work well with checksum offloads,
+but don't want to disable checksum offload in your device.
diff --git a/extensions/libxt_CLASSIFY.c b/extensions/libxt_CLASSIFY.c
new file mode 100644
index 0000000..e04657a
--- /dev/null
+++ b/extensions/libxt_CLASSIFY.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2003-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_CLASSIFY.h>
+#include <linux/pkt_sched.h>
+
+enum {
+	O_SET_CLASS = 0,
+};
+
+static void
+CLASSIFY_help(void)
+{
+	printf(
+"CLASSIFY target options:\n"
+"--set-class MAJOR:MINOR    Set skb->priority value (always hexadecimal!)\n");
+}
+
+static const struct xt_option_entry CLASSIFY_opts[] = {
+	{.name = "set-class", .id = O_SET_CLASS, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	XTOPT_TABLEEND,
+};
+
+static int CLASSIFY_string_to_priority(const char *s, unsigned int *p)
+{
+	unsigned int i, j;
+
+	if (sscanf(s, "%x:%x", &i, &j) != 2)
+		return 1;
+	
+	*p = TC_H_MAKE(i<<16, j);
+	return 0;
+}
+
+static void CLASSIFY_parse(struct xt_option_call *cb)
+{
+	struct xt_classify_target_info *clinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (CLASSIFY_string_to_priority(cb->arg, &clinfo->priority))
+		xtables_error(PARAMETER_PROBLEM,
+			   "Bad class value \"%s\"", cb->arg);
+}
+
+static void
+CLASSIFY_print_class(unsigned int priority, int numeric)
+{
+	printf(" %x:%x", TC_H_MAJ(priority)>>16, TC_H_MIN(priority));
+}
+
+static void
+CLASSIFY_print(const void *ip,
+      const struct xt_entry_target *target,
+      int numeric)
+{
+	const struct xt_classify_target_info *clinfo =
+		(const struct xt_classify_target_info *)target->data;
+	printf(" CLASSIFY set");
+	CLASSIFY_print_class(clinfo->priority, numeric);
+}
+
+static void
+CLASSIFY_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_classify_target_info *clinfo =
+		(const struct xt_classify_target_info *)target->data;
+
+	printf(" --set-class %.4x:%.4x",
+	       TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
+}
+
+static struct xtables_target classify_target = { 
+	.family		= NFPROTO_UNSPEC,
+	.name		= "CLASSIFY",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_classify_target_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_classify_target_info)),
+	.help		= CLASSIFY_help,
+	.print		= CLASSIFY_print,
+	.save		= CLASSIFY_save,
+	.x6_parse	= CLASSIFY_parse,
+	.x6_options	= CLASSIFY_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&classify_target);
+}
diff --git a/extensions/libxt_CLASSIFY.man b/extensions/libxt_CLASSIFY.man
new file mode 100644
index 0000000..0270fd1
--- /dev/null
+++ b/extensions/libxt_CLASSIFY.man
@@ -0,0 +1,5 @@
+This module allows you to set the skb\->priority value (and thus classify the packet into a specific CBQ class).
+.TP
+\fB\-\-set\-class\fP \fImajor\fP\fB:\fP\fIminor\fP
+Set the major and minor class value. The values are always interpreted as
+hexadecimal even if no 0x prefix is given.
diff --git a/extensions/libxt_CONNMARK.c b/extensions/libxt_CONNMARK.c
new file mode 100644
index 0000000..5d5351e
--- /dev/null
+++ b/extensions/libxt_CONNMARK.c
@@ -0,0 +1,386 @@
+/* Shared library add-on to iptables to add CONNMARK target support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_CONNMARK.h>
+
+struct xt_connmark_target_info {
+	unsigned long mark;
+	unsigned long mask;
+	uint8_t mode;
+};
+
+enum {
+	O_SET_MARK = 0,
+	O_SAVE_MARK,
+	O_RESTORE_MARK,
+	O_AND_MARK,
+	O_OR_MARK,
+	O_XOR_MARK,
+	O_SET_XMARK,
+	O_CTMASK,
+	O_NFMASK,
+	O_MASK,
+	F_SET_MARK     = 1 << O_SET_MARK,
+	F_SAVE_MARK    = 1 << O_SAVE_MARK,
+	F_RESTORE_MARK = 1 << O_RESTORE_MARK,
+	F_AND_MARK     = 1 << O_AND_MARK,
+	F_OR_MARK      = 1 << O_OR_MARK,
+	F_XOR_MARK     = 1 << O_XOR_MARK,
+	F_SET_XMARK    = 1 << O_SET_XMARK,
+	F_CTMASK       = 1 << O_CTMASK,
+	F_NFMASK       = 1 << O_NFMASK,
+	F_MASK         = 1 << O_MASK,
+	F_OP_ANY       = F_SET_MARK | F_SAVE_MARK | F_RESTORE_MARK |
+	                 F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK,
+};
+
+static void CONNMARK_help(void)
+{
+	printf(
+"CONNMARK target options:\n"
+"  --set-mark value[/mask]       Set conntrack mark value\n"
+"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
+"  --restore-mark [--mask mask]  Restore saved nfmark value\n");
+}
+
+#define s struct xt_connmark_target_info
+static const struct xt_option_entry CONNMARK_opts[] = {
+	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
+	 .excl = F_OP_ANY},
+	{.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE,
+	 .excl = F_OP_ANY},
+	{.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE,
+	 .excl = F_OP_ANY},
+	{.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+#define s struct xt_connmark_tginfo1
+static const struct xt_option_entry connmark_tg_opts[] = {
+	{.name = "set-xmark", .id = O_SET_XMARK, .type = XTTYPE_MARKMASK32,
+	 .excl = F_OP_ANY},
+	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
+	 .excl = F_OP_ANY},
+	{.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_OP_ANY},
+	{.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_OP_ANY},
+	{.name = "xor-mark", .id = O_XOR_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_OP_ANY},
+	{.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE,
+	 .excl = F_OP_ANY},
+	{.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE,
+	 .excl = F_OP_ANY},
+	{.name = "ctmask", .id = O_CTMASK, .type = XTTYPE_UINT32,
+	 .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, ctmask)},
+	{.name = "nfmask", .id = O_NFMASK, .type = XTTYPE_UINT32,
+	 .excl = F_MASK, .flags = XTOPT_PUT, XTOPT_POINTER(s, nfmask)},
+	{.name = "mask", .id = O_MASK, .type = XTTYPE_UINT32,
+	 .excl = F_CTMASK | F_NFMASK},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void connmark_tg_help(void)
+{
+	printf(
+"CONNMARK target options:\n"
+"  --set-xmark value[/ctmask]    Zero mask bits and XOR ctmark with value\n"
+"  --save-mark [--ctmask mask] [--nfmask mask]\n"
+"                                Copy ctmark to nfmark using masks\n"
+"  --restore-mark [--ctmask mask] [--nfmask mask]\n"
+"                                Copy nfmark to ctmark using masks\n"
+"  --set-mark value[/mask]       Set conntrack mark value\n"
+"  --save-mark [--mask mask]     Save the packet nfmark in the connection\n"
+"  --restore-mark [--mask mask]  Restore saved nfmark value\n"
+"  --and-mark value              Binary AND the ctmark with bits\n"
+"  --or-mark value               Binary OR  the ctmark with bits\n"
+"  --xor-mark value              Binary XOR the ctmark with bits\n"
+);
+}
+
+static void connmark_tg_init(struct xt_entry_target *target)
+{
+	struct xt_connmark_tginfo1 *info = (void *)target->data;
+
+	/*
+	 * Need these defaults for --save-mark/--restore-mark if no
+	 * --ctmark or --nfmask is given.
+	 */
+	info->ctmask = UINT32_MAX;
+	info->nfmask = UINT32_MAX;
+}
+
+static void CONNMARK_parse(struct xt_option_call *cb)
+{
+	struct xt_connmark_target_info *markinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_MARK:
+		markinfo->mode = XT_CONNMARK_SET;
+		markinfo->mark = cb->val.mark;
+		markinfo->mask = cb->val.mask;
+		break;
+	case O_SAVE_MARK:
+		markinfo->mode = XT_CONNMARK_SAVE;
+		break;
+	case O_RESTORE_MARK:
+		markinfo->mode = XT_CONNMARK_RESTORE;
+		break;
+	case O_MASK:
+		markinfo->mask = cb->val.u32;
+		break;
+	}
+}
+
+static void connmark_tg_parse(struct xt_option_call *cb)
+{
+	struct xt_connmark_tginfo1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_XMARK:
+		info->mode   = XT_CONNMARK_SET;
+		info->ctmark = cb->val.mark;
+		info->ctmask = cb->val.mask;
+		break;
+	case O_SET_MARK:
+		info->mode   = XT_CONNMARK_SET;
+		info->ctmark = cb->val.mark;
+		info->ctmask = cb->val.mark | cb->val.mask;
+		break;
+	case O_AND_MARK:
+		info->mode   = XT_CONNMARK_SET;
+		info->ctmark = 0;
+		info->ctmask = ~cb->val.u32;
+		break;
+	case O_OR_MARK:
+		info->mode   = XT_CONNMARK_SET;
+		info->ctmark = cb->val.u32;
+		info->ctmask = cb->val.u32;
+		break;
+	case O_XOR_MARK:
+		info->mode   = XT_CONNMARK_SET;
+		info->ctmark = cb->val.u32;
+		info->ctmask = 0;
+		break;
+	case O_SAVE_MARK:
+		info->mode = XT_CONNMARK_SAVE;
+		break;
+	case O_RESTORE_MARK:
+		info->mode = XT_CONNMARK_RESTORE;
+		break;
+	case O_MASK:
+		info->nfmask = info->ctmask = cb->val.u32;
+		break;
+	}
+}
+
+static void connmark_tg_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_OP_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+		           "CONNMARK target: No operation specified");
+}
+
+static void
+print_mark(unsigned long mark)
+{
+	printf("0x%lx", mark);
+}
+
+static void
+print_mask(const char *text, unsigned long mask)
+{
+	if (mask != 0xffffffffUL)
+		printf("%s0x%lx", text, mask);
+}
+
+static void CONNMARK_print(const void *ip,
+                           const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_connmark_target_info *markinfo =
+		(const struct xt_connmark_target_info *)target->data;
+	switch (markinfo->mode) {
+	case XT_CONNMARK_SET:
+	    printf(" CONNMARK set ");
+	    print_mark(markinfo->mark);
+	    print_mask("/", markinfo->mask);
+	    break;
+	case XT_CONNMARK_SAVE:
+	    printf(" CONNMARK save ");
+	    print_mask("mask ", markinfo->mask);
+	    break;
+	case XT_CONNMARK_RESTORE:
+	    printf(" CONNMARK restore ");
+	    print_mask("mask ", markinfo->mask);
+	    break;
+	default:
+	    printf(" ERROR: UNKNOWN CONNMARK MODE");
+	    break;
+	}
+}
+
+static void
+connmark_tg_print(const void *ip, const struct xt_entry_target *target,
+                  int numeric)
+{
+	const struct xt_connmark_tginfo1 *info = (const void *)target->data;
+
+	switch (info->mode) {
+	case XT_CONNMARK_SET:
+		if (info->ctmark == 0)
+			printf(" CONNMARK and 0x%x",
+			       (unsigned int)(uint32_t)~info->ctmask);
+		else if (info->ctmark == info->ctmask)
+			printf(" CONNMARK or 0x%x", info->ctmark);
+		else if (info->ctmask == 0)
+			printf(" CONNMARK xor 0x%x", info->ctmark);
+		else if (info->ctmask == 0xFFFFFFFFU)
+			printf(" CONNMARK set 0x%x", info->ctmark);
+		else
+			printf(" CONNMARK xset 0x%x/0x%x",
+			       info->ctmark, info->ctmask);
+		break;
+	case XT_CONNMARK_SAVE:
+		if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX)
+			printf(" CONNMARK save");
+		else if (info->nfmask == info->ctmask)
+			printf(" CONNMARK save mask 0x%x", info->nfmask);
+		else
+			printf(" CONNMARK save nfmask 0x%x ctmask ~0x%x",
+			       info->nfmask, info->ctmask);
+		break;
+	case XT_CONNMARK_RESTORE:
+		if (info->ctmask == UINT32_MAX && info->nfmask == UINT32_MAX)
+			printf(" CONNMARK restore");
+		else if (info->ctmask == info->nfmask)
+			printf(" CONNMARK restore mask 0x%x", info->ctmask);
+		else
+			printf(" CONNMARK restore ctmask 0x%x nfmask ~0x%x",
+			       info->ctmask, info->nfmask);
+		break;
+
+	default:
+		printf(" ERROR: UNKNOWN CONNMARK MODE");
+		break;
+	}
+}
+
+static void CONNMARK_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_connmark_target_info *markinfo =
+		(const struct xt_connmark_target_info *)target->data;
+
+	switch (markinfo->mode) {
+	case XT_CONNMARK_SET:
+	    printf(" --set-mark ");
+	    print_mark(markinfo->mark);
+	    print_mask("/", markinfo->mask);
+	    break;
+	case XT_CONNMARK_SAVE:
+	    printf(" --save-mark ");
+	    print_mask("--mask ", markinfo->mask);
+	    break;
+	case XT_CONNMARK_RESTORE:
+	    printf(" --restore-mark ");
+	    print_mask("--mask ", markinfo->mask);
+	    break;
+	default:
+	    printf(" ERROR: UNKNOWN CONNMARK MODE");
+	    break;
+	}
+}
+
+static void CONNMARK_init(struct xt_entry_target *t)
+{
+	struct xt_connmark_target_info *markinfo
+		= (struct xt_connmark_target_info *)t->data;
+
+	markinfo->mask = 0xffffffffUL;
+}
+
+static void
+connmark_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_connmark_tginfo1 *info = (const void *)target->data;
+
+	switch (info->mode) {
+	case XT_CONNMARK_SET:
+		printf(" --set-xmark 0x%x/0x%x", info->ctmark, info->ctmask);
+		break;
+	case XT_CONNMARK_SAVE:
+		printf(" --save-mark --nfmask 0x%x --ctmask 0x%x",
+		       info->nfmask, info->ctmask);
+		break;
+	case XT_CONNMARK_RESTORE:
+		printf(" --restore-mark --nfmask 0x%x --ctmask 0x%x",
+		       info->nfmask, info->ctmask);
+		break;
+	default:
+		printf(" ERROR: UNKNOWN CONNMARK MODE");
+		break;
+	}
+}
+
+static struct xtables_target connmark_tg_reg[] = {
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "CONNMARK",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connmark_target_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_target_info)),
+		.help          = CONNMARK_help,
+		.init          = CONNMARK_init,
+		.print         = CONNMARK_print,
+		.save          = CONNMARK_save,
+		.x6_parse      = CONNMARK_parse,
+		.x6_fcheck     = connmark_tg_check,
+		.x6_options    = CONNMARK_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "CONNMARK",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)),
+		.help          = connmark_tg_help,
+		.init          = connmark_tg_init,
+		.print         = connmark_tg_print,
+		.save          = connmark_tg_save,
+		.x6_parse      = connmark_tg_parse,
+		.x6_fcheck     = connmark_tg_check,
+		.x6_options    = connmark_tg_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg));
+}
diff --git a/extensions/libxt_CONNMARK.man b/extensions/libxt_CONNMARK.man
new file mode 100644
index 0000000..9317923
--- /dev/null
+++ b/extensions/libxt_CONNMARK.man
@@ -0,0 +1,53 @@
+This module sets the netfilter mark value associated with a connection. The
+mark is 32 bits wide.
+.TP
+\fB\-\-set\-xmark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Zero out the bits given by \fImask\fP and XOR \fIvalue\fP into the ctmark.
+.TP
+\fB\-\-save\-mark\fP [\fB\-\-nfmask\fP \fInfmask\fP] [\fB\-\-ctmask\fP \fIctmask\fP]
+Copy the packet mark (nfmark) to the connection mark (ctmark) using the given
+masks. The new nfmark value is determined as follows:
+.IP
+ctmark = (ctmark & ~ctmask) ^ (nfmark & nfmask)
+.IP
+i.e. \fIctmask\fP defines what bits to clear and \fInfmask\fP what bits of the
+nfmark to XOR into the ctmark. \fIctmask\fP and \fInfmask\fP default to
+0xFFFFFFFF.
+.TP
+\fB\-\-restore\-mark\fP [\fB\-\-nfmask\fP \fInfmask\fP] [\fB\-\-ctmask\fP \fIctmask\fP]
+Copy the connection mark (ctmark) to the packet mark (nfmark) using the given
+masks. The new ctmark value is determined as follows:
+.IP
+nfmark = (nfmark & ~\fInfmask\fP) ^ (ctmark & \fIctmask\fP);
+.IP
+i.e. \fInfmask\fP defines what bits to clear and \fIctmask\fP what bits of the
+ctmark to XOR into the nfmark. \fIctmask\fP and \fInfmask\fP default to
+0xFFFFFFFF.
+.IP
+\fB\-\-restore\-mark\fP is only valid in the \fBmangle\fP table.
+.PP
+The following mnemonics are available for \fB\-\-set\-xmark\fP:
+.TP
+\fB\-\-and\-mark\fP \fIbits\fP
+Binary AND the ctmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark
+0/\fP\fIinvbits\fP, where \fIinvbits\fP is the binary negation of \fIbits\fP.)
+.TP
+\fB\-\-or\-mark\fP \fIbits\fP
+Binary OR the ctmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark\fP
+\fIbits\fP\fB/\fP\fIbits\fP.)
+.TP
+\fB\-\-xor\-mark\fP \fIbits\fP
+Binary XOR the ctmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark\fP
+\fIbits\fP\fB/0\fP.)
+.TP
+\fB\-\-set\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Set the connection mark. If a mask is specified then only those bits set in the
+mask are modified.
+.TP
+\fB\-\-save\-mark\fP [\fB\-\-mask\fP \fImask\fP]
+Copy the nfmark to the ctmark. If a mask is specified, only those bits are
+copied.
+.TP
+\fB\-\-restore\-mark\fP [\fB\-\-mask\fP \fImask\fP]
+Copy the ctmark to the nfmark. If a mask is specified, only those bits are
+copied. This is only valid in the \fBmangle\fP table.
diff --git a/extensions/libxt_CONNSECMARK.c b/extensions/libxt_CONNSECMARK.c
new file mode 100644
index 0000000..0b3cd79
--- /dev/null
+++ b/extensions/libxt_CONNSECMARK.c
@@ -0,0 +1,112 @@
+/*
+ * Shared library add-on to iptables to add CONNSECMARK target support.
+ *
+ * Based on the MARK and CONNMARK targets.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_CONNSECMARK.h>
+
+#define PFX "CONNSECMARK target: "
+
+enum {
+	O_SAVE = 0,
+	O_RESTORE,
+	F_SAVE    = 1 << O_SAVE,
+	F_RESTORE = 1 << O_RESTORE,
+};
+
+static void CONNSECMARK_help(void)
+{
+	printf(
+"CONNSECMARK target options:\n"
+"  --save                   Copy security mark from packet to conntrack\n"
+"  --restore                Copy security mark from connection to packet\n");
+}
+
+static const struct xt_option_entry CONNSECMARK_opts[] = {
+	{.name = "save", .id = O_SAVE, .excl = F_RESTORE, .type = XTTYPE_NONE},
+	{.name = "restore", .id = O_RESTORE, .excl = F_SAVE,
+	 .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static void CONNSECMARK_parse(struct xt_option_call *cb)
+{
+	struct xt_connsecmark_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SAVE:
+		info->mode = CONNSECMARK_SAVE;
+		break;
+	case O_RESTORE:
+		info->mode = CONNSECMARK_RESTORE;
+		break;
+	}
+}
+
+static void CONNSECMARK_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, PFX "parameter required");
+}
+
+static void print_connsecmark(const struct xt_connsecmark_target_info *info)
+{
+	switch (info->mode) {
+	case CONNSECMARK_SAVE:
+		printf("save");
+		break;
+		
+	case CONNSECMARK_RESTORE:
+		printf("restore");
+		break;
+		
+	default:
+		xtables_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+	}
+}
+
+static void
+CONNSECMARK_print(const void *ip, const struct xt_entry_target *target,
+                  int numeric)
+{
+	const struct xt_connsecmark_target_info *info =
+		(struct xt_connsecmark_target_info*)(target)->data;
+
+	printf(" CONNSECMARK ");
+	print_connsecmark(info);
+}
+
+static void
+CONNSECMARK_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_connsecmark_target_info *info =
+		(struct xt_connsecmark_target_info*)target->data;
+
+	printf(" --");
+	print_connsecmark(info);
+}
+
+static struct xtables_target connsecmark_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "CONNSECMARK",
+	.version	= XTABLES_VERSION,
+	.revision	= 0,
+	.size		= XT_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_connsecmark_target_info)),
+	.help		= CONNSECMARK_help,
+	.print		= CONNSECMARK_print,
+	.save		= CONNSECMARK_save,
+	.x6_parse	= CONNSECMARK_parse,
+	.x6_fcheck	= CONNSECMARK_check,
+	.x6_options	= CONNSECMARK_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&connsecmark_target);
+}
diff --git a/extensions/libxt_CONNSECMARK.man b/extensions/libxt_CONNSECMARK.man
new file mode 100644
index 0000000..2616ab9
--- /dev/null
+++ b/extensions/libxt_CONNSECMARK.man
@@ -0,0 +1,18 @@
+This module copies security markings from packets to connections
+(if unlabeled), and from connections back to packets (also only
+if unlabeled).  Typically used in conjunction with SECMARK, it is
+valid in the
+.B security
+table (for backwards compatibility with older kernels, it is also
+valid in the
+.B mangle
+table).
+.TP
+\fB\-\-save\fP
+If the packet has a security marking, copy it to the connection
+if the connection is not marked.
+.TP
+\fB\-\-restore\fP
+If the packet does not have a security marking, and the connection
+does, copy the security marking from the connection to the packet.
+
diff --git a/extensions/libxt_CT.c b/extensions/libxt_CT.c
new file mode 100644
index 0000000..6b28fe1
--- /dev/null
+++ b/extensions/libxt_CT.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2010-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/xt_CT.h>
+
+static void ct_help(void)
+{
+	printf(
+"CT target options:\n"
+" --notrack			Don't track connection\n"
+" --helper name			Use conntrack helper '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"
+	);
+}
+
+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,
+};
+
+#define s struct xt_ct_target_info
+static const struct xt_option_entry ct_opts[] = {
+	{.name = "notrack", .id = O_NOTRACK, .type = XTTYPE_NONE},
+	{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, helper)},
+	{.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
+
+#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;
+};
+
+static const struct event_tbl ct_event_tbl[] = {
+	{ "new",		IPCT_NEW },
+	{ "related",		IPCT_RELATED },
+	{ "destroy",		IPCT_DESTROY },
+	{ "reply",		IPCT_REPLY },
+	{ "assured",		IPCT_ASSURED },
+	{ "protoinfo",		IPCT_PROTOINFO },
+	{ "helper",		IPCT_HELPER },
+	{ "mark",		IPCT_MARK },
+	{ "natseqinfo",		IPCT_NATSEQADJ },
+	{ "secmark",		IPCT_SECMARK },
+};
+
+static const struct event_tbl exp_event_tbl[] = {
+	{ "new",		IPEXP_NEW },
+};
+
+static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size,
+				const char *events)
+{
+	char str[strlen(events) + 1], *e = str, *t;
+	unsigned int mask = 0, i;
+
+	strcpy(str, events);
+	while ((t = strsep(&e, ","))) {
+		for (i = 0; i < size; i++) {
+			if (strcmp(t, tbl[i].name))
+				continue;
+			mask |= 1 << tbl[i].event;
+			break;
+		}
+
+		if (i == size)
+			xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t);
+	}
+
+	return mask;
+}
+
+static void ct_print_events(const char *pfx, const struct event_tbl *tbl,
+			    unsigned int size, uint32_t mask)
+{
+	const char *sep = "";
+	unsigned int i;
+
+	printf(" %s ", pfx);
+	for (i = 0; i < size; i++) {
+		if (mask & (1 << tbl[i].event)) {
+			printf("%s%s", sep, tbl[i].name);
+			sep = ",";
+		}
+	}
+}
+
+static void ct_parse(struct xt_option_call *cb)
+{
+	struct xt_ct_target_info *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_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 =
+		(const struct xt_ct_target_info *)target->data;
+
+	printf(" CT");
+	if (info->flags & XT_CT_NOTRACK)
+		printf(" notrack");
+	if (info->helper[0])
+		printf(" helper %s", info->helper);
+	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_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])
+		printf(" --helper %s", info->helper);
+	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_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_targets(ct_target_reg, ARRAY_SIZE(ct_target_reg));
+}
diff --git a/extensions/libxt_CT.man b/extensions/libxt_CT.man
new file mode 100644
index 0000000..a93eb14
--- /dev/null
+++ b/extensions/libxt_CT.man
@@ -0,0 +1,30 @@
+The CT target allows to set parameters for a packet or its associated
+connection. The target attaches a "template" connection tracking entry to
+the packet, which is then used by the conntrack core when initializing
+a new ct entry. This target is thus only valid in the "raw" table.
+.TP
+\fB\-\-notrack\fP
+Disables connection tracking for this packet.
+.TP
+\fB\-\-helper\fP \fIname\fP
+Use the helper identified by \fIname\fP for the connection. This is more
+flexible than loading the conntrack helper modules with preset ports.
+.TP
+\fB\-\-ctevents\fP \fIevent\fP[\fB,\fP...]
+Only generate the specified conntrack events for this connection. Possible
+event types are: \fBnew\fP, \fBrelated\fP, \fBdestroy\fP, \fBreply\fP,
+\fBassured\fP, \fBprotoinfo\fP, \fBhelper\fP, \fBmark\fP (this refers to
+the ctmark, not nfmark), \fBnatseqinfo\fP, \fBsecmark\fP (ctsecmark).
+.TP
+\fB\-\-expevents\fP \fIevent\fP[\fB,\fP...]
+Only generate the specified expectation events for this connection.
+Possible event types are: \fBnew\fP.
+.TP
+\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/libxt_DNAT.man b/extensions/libxt_DNAT.man
new file mode 100644
index 0000000..225274f
--- /dev/null
+++ b/extensions/libxt_DNAT.man
@@ -0,0 +1,38 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+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 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. 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
+after another in cycle) load balancing takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+.TP
+\fB\-\-random\fP
+If option
+\fB\-\-random\fP
+is used then port mapping will be randomized (kernel >= 2.6.22).
+.TP
+\fB\-\-persistent\fP
+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_DSCP.c b/extensions/libxt_DSCP.c
new file mode 100644
index 0000000..e16e93c
--- /dev/null
+++ b/extensions/libxt_DSCP.c
@@ -0,0 +1,112 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2000- 2002 by Matthew G. Marsh <mgm@paktronix.com>,
+ * 		     Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_DSCP.c borrowed heavily from libipt_TOS.c
+ *
+ * --set-class added by Iain Barnes
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_DSCP.h>
+
+/* This is evil, but it's my code - HW*/
+#include "dscp_helper.c"
+
+enum {
+	O_SET_DSCP = 0,
+	O_SET_DSCP_CLASS,
+	F_SET_DSCP       = 1 << O_SET_DSCP,
+	F_SET_DSCP_CLASS = 1 << O_SET_DSCP_CLASS,
+};
+
+static void DSCP_help(void)
+{
+	printf(
+"DSCP target options\n"
+"  --set-dscp value		Set DSCP field in packet header to value\n"
+"  		                This value can be in decimal (ex: 32)\n"
+"               		or in hex (ex: 0x20)\n"
+"  --set-dscp-class class	Set the DSCP field in packet header to the\n"
+"				value represented by the DiffServ class value.\n"
+"				This class may be EF,BE or any of the CSxx\n"
+"				or AFxx classes.\n"
+"\n"
+"				These two options are mutually exclusive !\n"
+);
+}
+
+static const struct xt_option_entry DSCP_opts[] = {
+	{.name = "set-dscp", .id = O_SET_DSCP, .excl = F_SET_DSCP_CLASS,
+	 .type = XTTYPE_UINT8, .min = 0, .max = XT_DSCP_MAX,
+	 .flags = XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_DSCP_info, dscp)},
+	{.name = "set-dscp-class", .id = O_SET_DSCP_CLASS, .excl = F_SET_DSCP,
+	 .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static void DSCP_parse(struct xt_option_call *cb)
+{
+	struct xt_DSCP_info *dinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_DSCP_CLASS:
+		dinfo->dscp = class_to_dscp(cb->arg);
+		break;
+	}
+}
+
+static void DSCP_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "DSCP target: Parameter --set-dscp is required");
+}
+
+static void
+print_dscp(uint8_t dscp, int numeric)
+{
+	printf(" 0x%02x", dscp);
+}
+
+static void DSCP_print(const void *ip, const struct xt_entry_target *target,
+                       int numeric)
+{
+	const struct xt_DSCP_info *dinfo =
+		(const struct xt_DSCP_info *)target->data;
+	printf(" DSCP set");
+	print_dscp(dinfo->dscp, numeric);
+}
+
+static void DSCP_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_DSCP_info *dinfo =
+		(const struct xt_DSCP_info *)target->data;
+
+	printf(" --set-dscp 0x%02x", dinfo->dscp);
+}
+
+static struct xtables_target dscp_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "DSCP",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_DSCP_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_DSCP_info)),
+	.help		= DSCP_help,
+	.print		= DSCP_print,
+	.save		= DSCP_save,
+	.x6_parse	= DSCP_parse,
+	.x6_fcheck	= DSCP_check,
+	.x6_options	= DSCP_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&dscp_target);
+}
diff --git a/extensions/libxt_DSCP.man b/extensions/libxt_DSCP.man
new file mode 100644
index 0000000..551ba2e
--- /dev/null
+++ b/extensions/libxt_DSCP.man
@@ -0,0 +1,9 @@
+This target allows to alter the value of the DSCP bits within the TOS
+header of the IPv4 packet.  As this manipulates a packet, it can only
+be used in the mangle table.
+.TP
+\fB\-\-set\-dscp\fP \fIvalue\fP
+Set the DSCP field to a numerical value (can be decimal or hex)
+.TP
+\fB\-\-set\-dscp\-class\fP \fIclass\fP
+Set the DSCP field to a DiffServ class.
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_IDLETIMER.c b/extensions/libxt_IDLETIMER.c
new file mode 100644
index 0000000..21004a4
--- /dev/null
+++ b/extensions/libxt_IDLETIMER.c
@@ -0,0 +1,89 @@
+/*
+ * Shared library add-on for iptables to add IDLETIMER support.
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.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 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_IDLETIMER.h>
+
+enum {
+	O_TIMEOUT = 0,
+	O_LABEL,
+};
+
+#define s struct idletimer_tg_info
+static const struct xt_option_entry idletimer_tg_opts[] = {
+	{.name = "timeout", .id = O_TIMEOUT, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, timeout)},
+	{.name = "label", .id = O_LABEL, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, label)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void idletimer_tg_help(void)
+{
+	printf(
+"IDLETIMER target options:\n"
+" --timeout time	Timeout until the notification is sent (in seconds)\n"
+" --label string	Unique rule identifier\n"
+"\n");
+}
+
+static void idletimer_tg_print(const void *ip,
+			       const struct xt_entry_target *target,
+			       int numeric)
+{
+	struct idletimer_tg_info *info =
+		(struct idletimer_tg_info *) target->data;
+
+	printf(" timeout:%u", info->timeout);
+	printf(" label:%s", info->label);
+}
+
+static void idletimer_tg_save(const void *ip,
+			      const struct xt_entry_target *target)
+{
+	struct idletimer_tg_info *info =
+		(struct idletimer_tg_info *) target->data;
+
+	printf(" --timeout %u", info->timeout);
+	printf(" --label %s", info->label);
+}
+
+static struct xtables_target idletimer_tg_reg = {
+	.family	       = NFPROTO_UNSPEC,
+	.name	       = "IDLETIMER",
+	.version       = XTABLES_VERSION,
+	.revision      = 0,
+	.size	       = XT_ALIGN(sizeof(struct idletimer_tg_info)),
+	.userspacesize = offsetof(struct idletimer_tg_info, timer),
+	.help	       = idletimer_tg_help,
+	.x6_parse      = xtables_option_parse,
+	.print	       = idletimer_tg_print,
+	.save	       = idletimer_tg_save,
+	.x6_options    = idletimer_tg_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&idletimer_tg_reg);
+}
diff --git a/extensions/libxt_IDLETIMER.man b/extensions/libxt_IDLETIMER.man
new file mode 100644
index 0000000..e3c91ce
--- /dev/null
+++ b/extensions/libxt_IDLETIMER.man
@@ -0,0 +1,20 @@
+This target can be used to identify when interfaces have been idle for a
+certain period of time.  Timers are identified by labels and are created when
+a rule is set with a new label.  The rules also take a timeout value (in
+seconds) as an option.  If more than one rule uses the same timer label, the
+timer will be restarted whenever any of the rules get a hit.  One entry for
+each timer is created in sysfs.  This attribute contains the timer remaining
+for the timer to expire.  The attributes are located under the xt_idletimer
+class:
+.PP
+/sys/class/xt_idletimer/timers/<label>
+.PP
+When the timer expires, the target module sends a sysfs notification to the
+userspace, which can then decide what to do (eg. disconnect to save power).
+.TP
+\fB\-\-timeout\fP \fIamount\fP
+This is the time in seconds that will trigger the notification.
+.TP
+\fB\-\-label\fP \fIstring\fP
+This is a unique identifier for the timer.  The maximum length for the
+label string is 27 characters.
diff --git a/extensions/libxt_LED.c b/extensions/libxt_LED.c
new file mode 100644
index 0000000..8622c37
--- /dev/null
+++ b/extensions/libxt_LED.c
@@ -0,0 +1,139 @@
+/*
+ * libxt_LED.c - shared library add-on to iptables to add customized LED
+ *               trigger support.
+ *
+ * (C) 2008 Adam Nielsen <a.nielsen@shikadi.net>
+ *
+ * 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.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_LED.h>
+
+enum {
+	O_LED_TRIGGER_ID = 0,
+	O_LED_DELAY,
+	O_LED_ALWAYS_BLINK,
+};
+
+#define s struct xt_led_info
+static const struct xt_option_entry LED_opts[] = {
+	{.name = "led-trigger-id", .id = O_LED_TRIGGER_ID,
+	 .flags = XTOPT_MAND, .type = XTTYPE_STRING, .min = 0,
+	 .max = sizeof(((struct xt_led_info *)NULL)->id) -
+	        sizeof("netfilter-")},
+	{.name = "led-delay", .id = O_LED_DELAY, .type = XTTYPE_STRING},
+	{.name = "led-always-blink", .id = O_LED_ALWAYS_BLINK,
+	 .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void LED_help(void)
+{
+	printf(
+"LED target options:\n"
+"--led-trigger-id name           suffix for led trigger name\n"
+"--led-delay ms                  leave the LED on for this number of\n"
+"                                milliseconds after triggering.\n"
+"--led-always-blink              blink on arriving packets, even if\n"
+"                                the LED is already on.\n"
+	);
+}
+
+static void LED_parse(struct xt_option_call *cb)
+{
+	struct xt_led_info *led = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_LED_TRIGGER_ID:
+		strcpy(led->id, "netfilter-");
+		strcat(led->id, cb->arg);
+		break;
+	case O_LED_DELAY:
+		if (strncasecmp(cb->arg, "inf", 3) == 0)
+			led->delay = -1;
+		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;
+		break;
+	}
+}
+
+static void LED_print(const void *ip, const struct xt_entry_target *target,
+		      int numeric)
+{
+	const struct xt_led_info *led = (void *)target->data;
+	const char *id = led->id + strlen("netfilter-"); /* trim off prefix */
+
+	printf(" led-trigger-id:\"");
+	/* Escape double quotes and backslashes in the ID */
+	while (*id != '\0') {
+		if (*id == '"' || *id == '\\')
+			printf("\\");
+		printf("%c", *id++);
+	}
+	printf("\"");
+
+	if (led->delay == -1)
+		printf(" led-delay:inf");
+	else
+		printf(" led-delay:%dms", led->delay);
+
+	if (led->always_blink)
+		printf(" led-always-blink");
+}
+
+static void LED_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_led_info *led = (void *)target->data;
+	const char *id = led->id + strlen("netfilter-"); /* trim off prefix */
+
+	printf(" --led-trigger-id \"");
+	/* Escape double quotes and backslashes in the ID */
+	while (*id != '\0') {
+		if (*id == '"' || *id == '\\')
+			printf("\\");
+		printf("%c", *id++);
+	}
+	printf("\"");
+
+	/* Only print the delay if it's not zero (the default) */
+	if (led->delay > 0)
+		printf(" --led-delay %d", led->delay);
+	else if (led->delay == -1)
+		printf(" --led-delay inf");
+
+	/* Only print always_blink if it's not set to the default */
+	if (led->always_blink)
+		printf(" --led-always-blink");
+}
+
+static struct xtables_target led_tg_reg = {
+	.version       = XTABLES_VERSION,
+	.name          = "LED",
+	.family        = PF_UNSPEC,
+	.revision      = 0,
+	.size          = XT_ALIGN(sizeof(struct xt_led_info)),
+	.userspacesize = offsetof(struct xt_led_info, internal_data),
+	.help          = LED_help,
+	.print         = LED_print,
+	.save          = LED_save,
+	.x6_parse      = LED_parse,
+	.x6_options    = LED_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&led_tg_reg);
+}
diff --git a/extensions/libxt_LED.man b/extensions/libxt_LED.man
new file mode 100644
index 0000000..81c2f29
--- /dev/null
+++ b/extensions/libxt_LED.man
@@ -0,0 +1,30 @@
+This creates an LED-trigger that can then be attached to system indicator
+lights, to blink or illuminate them when certain packets pass through the
+system. One example might be to light up an LED for a few minutes every time
+an SSH connection is made to the local machine. The following options control
+the trigger behavior:
+.TP
+\fB\-\-led\-trigger\-id\fP \fIname\fP
+This is the name given to the LED trigger. The actual name of the trigger
+will be prefixed with "netfilter-".
+.TP
+\fB\-\-led-delay\fP \fIms\fP
+This indicates how long (in milliseconds) the LED should be left illuminated
+when a packet arrives before being switched off again. The default is 0
+(blink as fast as possible.) The special value \fIinf\fP can be given to
+leave the LED on permanently once activated. (In this case the trigger will
+need to be manually detached and reattached to the LED device to switch it
+off again.)
+.TP
+\fB\-\-led\-always\-blink\fP
+Always make the LED blink on packet arrival, even if the LED is already on.
+This allows notification of new packets even with long delay values (which
+otherwise would result in a silent prolonging of the delay time.)
+.TP
+Example:
+.TP
+Create an LED trigger for incoming SSH traffic:
+iptables \-A INPUT \-p tcp \-\-dport 22 \-j LED \-\-led\-trigger\-id ssh
+.TP
+Then attach the new trigger to an LED:
+echo netfilter\-ssh >/sys/class/leds/\fIledname\fP/trigger
diff --git a/extensions/libxt_LOG.man b/extensions/libxt_LOG.man
new file mode 100644
index 0000000..354edf4
--- /dev/null
+++ b/extensions/libxt_LOG.man
@@ -0,0 +1,32 @@
+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/IPv6 header fields) via the kernel log
+(where it can be read with \fIdmesg(1)\fP or read in the syslog).
+.PP
+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, 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,
+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/IPv6 packet header.
+.TP
+\fB\-\-log\-uid\fP
+Log the userid of the process which generated the packet.
diff --git a/extensions/libxt_MARK.c b/extensions/libxt_MARK.c
new file mode 100644
index 0000000..556dbde
--- /dev/null
+++ b/extensions/libxt_MARK.c
@@ -0,0 +1,296 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_MARK.h>
+
+/* Version 0 */
+struct xt_mark_target_info {
+	unsigned long mark;
+};
+
+/* Version 1 */
+enum {
+	XT_MARK_SET=0,
+	XT_MARK_AND,
+	XT_MARK_OR,
+};
+
+struct xt_mark_target_info_v1 {
+	unsigned long mark;
+	uint8_t mode;
+};
+
+enum {
+	O_SET_MARK = 0,
+	O_AND_MARK,
+	O_OR_MARK,
+	O_XOR_MARK,
+	O_SET_XMARK,
+	F_SET_MARK  = 1 << O_SET_MARK,
+	F_AND_MARK  = 1 << O_AND_MARK,
+	F_OR_MARK   = 1 << O_OR_MARK,
+	F_XOR_MARK  = 1 << O_XOR_MARK,
+	F_SET_XMARK = 1 << O_SET_XMARK,
+	F_ANY       = F_SET_MARK | F_AND_MARK | F_OR_MARK |
+	              F_XOR_MARK | F_SET_XMARK,
+};
+
+static void MARK_help(void)
+{
+	printf(
+"MARK target options:\n"
+"  --set-mark value                   Set nfmark value\n"
+"  --and-mark value                   Binary AND the nfmark with value\n"
+"  --or-mark  value                   Binary OR  the nfmark with value\n");
+}
+
+static const struct xt_option_entry MARK_opts[] = {
+	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	{.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	{.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry mark_tg_opts[] = {
+	{.name = "set-xmark", .id = O_SET_XMARK, .type = XTTYPE_MARKMASK32,
+	 .excl = F_ANY},
+	{.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
+	 .excl = F_ANY},
+	{.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	{.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	{.name = "xor-mark", .id = O_XOR_MARK, .type = XTTYPE_UINT32,
+	 .excl = F_ANY},
+	XTOPT_TABLEEND,
+};
+
+static void mark_tg_help(void)
+{
+	printf(
+"MARK target options:\n"
+"  --set-xmark value[/mask]  Clear bits in mask and XOR value into nfmark\n"
+"  --set-mark value[/mask]   Clear bits in mask and OR value into nfmark\n"
+"  --and-mark bits           Binary AND the nfmark with bits\n"
+"  --or-mark bits            Binary OR the nfmark with bits\n"
+"  --xor-mask bits           Binary XOR the nfmark with bits\n"
+"\n");
+}
+
+static void MARK_parse_v0(struct xt_option_call *cb)
+{
+	struct xt_mark_target_info *markinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_MARK:
+		markinfo->mark = cb->val.mark;
+		break;
+	default:
+		xtables_error(PARAMETER_PROBLEM,
+			   "MARK target: kernel too old for --%s",
+			   cb->entry->name);
+	}
+}
+
+static void MARK_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "MARK target: Parameter --set/and/or-mark"
+			   " is required");
+}
+
+static void MARK_parse_v1(struct xt_option_call *cb)
+{
+	struct xt_mark_target_info_v1 *markinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_MARK:
+	        markinfo->mode = XT_MARK_SET;
+		break;
+	case O_AND_MARK:
+	        markinfo->mode = XT_MARK_AND;
+		break;
+	case O_OR_MARK:
+	        markinfo->mode = XT_MARK_OR;
+		break;
+	}
+	markinfo->mark = cb->val.u32;
+}
+
+static void mark_tg_parse(struct xt_option_call *cb)
+{
+	struct xt_mark_tginfo2 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_XMARK:
+		info->mark = cb->val.mark;
+		info->mask = cb->val.mask;
+		break;
+	case O_SET_MARK:
+		info->mark = cb->val.mark;
+		info->mask = cb->val.mark | cb->val.mask;
+		break;
+	case O_AND_MARK:
+		info->mark = 0;
+		info->mask = ~cb->val.u32;
+		break;
+	case O_OR_MARK:
+		info->mark = info->mask = cb->val.u32;
+		break;
+	case O_XOR_MARK:
+		info->mark = cb->val.u32;
+		info->mask = 0;
+		break;
+	}
+}
+
+static void mark_tg_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, "MARK: One of the --set-xmark, "
+		           "--{and,or,xor,set}-mark options is required");
+}
+
+static void
+print_mark(unsigned long mark)
+{
+	printf(" 0x%lx", mark);
+}
+
+static void MARK_print_v0(const void *ip,
+                          const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_mark_target_info *markinfo =
+		(const struct xt_mark_target_info *)target->data;
+	printf(" MARK set");
+	print_mark(markinfo->mark);
+}
+
+static void MARK_save_v0(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_mark_target_info *markinfo =
+		(const struct xt_mark_target_info *)target->data;
+
+	printf(" --set-mark");
+	print_mark(markinfo->mark);
+}
+
+static void MARK_print_v1(const void *ip, const struct xt_entry_target *target,
+                          int numeric)
+{
+	const struct xt_mark_target_info_v1 *markinfo =
+		(const struct xt_mark_target_info_v1 *)target->data;
+
+	switch (markinfo->mode) {
+	case XT_MARK_SET:
+		printf(" MARK set");
+		break;
+	case XT_MARK_AND:
+		printf(" MARK and");
+		break;
+	case XT_MARK_OR: 
+		printf(" MARK or");
+		break;
+	}
+	print_mark(markinfo->mark);
+}
+
+static void mark_tg_print(const void *ip, const struct xt_entry_target *target,
+                          int numeric)
+{
+	const struct xt_mark_tginfo2 *info = (const void *)target->data;
+
+	if (info->mark == 0)
+		printf(" MARK and 0x%x", (unsigned int)(uint32_t)~info->mask);
+	else if (info->mark == info->mask)
+		printf(" MARK or 0x%x", info->mark);
+	else if (info->mask == 0)
+		printf(" MARK xor 0x%x", info->mark);
+	else if (info->mask == 0xffffffffU)
+		printf(" MARK set 0x%x", info->mark);
+	else
+		printf(" MARK xset 0x%x/0x%x", info->mark, info->mask);
+}
+
+static void MARK_save_v1(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_mark_target_info_v1 *markinfo =
+		(const struct xt_mark_target_info_v1 *)target->data;
+
+	switch (markinfo->mode) {
+	case XT_MARK_SET:
+		printf(" --set-mark");
+		break;
+	case XT_MARK_AND:
+		printf(" --and-mark");
+		break;
+	case XT_MARK_OR: 
+		printf(" --or-mark");
+		break;
+	}
+	print_mark(markinfo->mark);
+}
+
+static void mark_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_mark_tginfo2 *info = (const void *)target->data;
+
+	printf(" --set-xmark 0x%x/0x%x", info->mark, info->mask);
+}
+
+static struct xtables_target mark_tg_reg[] = {
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "MARK",
+		.version       = XTABLES_VERSION,
+		.revision      = 0,
+		.size          = XT_ALIGN(sizeof(struct xt_mark_target_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_target_info)),
+		.help          = MARK_help,
+		.print         = MARK_print_v0,
+		.save          = MARK_save_v0,
+		.x6_parse      = MARK_parse_v0,
+		.x6_fcheck     = MARK_check,
+		.x6_options    = MARK_opts,
+	},
+	{
+		.family        = NFPROTO_IPV4,
+		.name          = "MARK",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.size          = XT_ALIGN(sizeof(struct xt_mark_target_info_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_target_info_v1)),
+		.help          = MARK_help,
+		.print         = MARK_print_v1,
+		.save          = MARK_save_v1,
+		.x6_parse      = MARK_parse_v1,
+		.x6_fcheck     = MARK_check,
+		.x6_options    = MARK_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "MARK",
+		.revision      = 2,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_mark_tginfo2)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_tginfo2)),
+		.help          = mark_tg_help,
+		.print         = mark_tg_print,
+		.save          = mark_tg_save,
+		.x6_parse      = mark_tg_parse,
+		.x6_fcheck     = mark_tg_check,
+		.x6_options    = mark_tg_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg));
+}
diff --git a/extensions/libxt_MARK.man b/extensions/libxt_MARK.man
new file mode 100644
index 0000000..712fb76
--- /dev/null
+++ b/extensions/libxt_MARK.man
@@ -0,0 +1,27 @@
+This target is used to set the Netfilter mark value associated with the packet.
+It can, for example, be used in conjunction with routing based on fwmark (needs
+iproute2). If you plan on doing so, note that the mark needs to be set in the
+PREROUTING chain of the mangle table to affect routing.
+The mark field is 32 bits wide.
+.TP
+\fB\-\-set\-xmark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Zeroes out the bits given by \fImask\fP and XORs \fIvalue\fP into the packet
+mark ("nfmark"). If \fImask\fP is omitted, 0xFFFFFFFF is assumed.
+.TP
+\fB\-\-set\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Zeroes out the bits given by \fImask\fP and ORs \fIvalue\fP into the packet
+mark. If \fImask\fP is omitted, 0xFFFFFFFF is assumed.
+.PP
+The following mnemonics are available:
+.TP
+\fB\-\-and\-mark\fP \fIbits\fP
+Binary AND the nfmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark
+0/\fP\fIinvbits\fP, where \fIinvbits\fP is the binary negation of \fIbits\fP.)
+.TP
+\fB\-\-or\-mark\fP \fIbits\fP
+Binary OR the nfmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark\fP
+\fIbits\fP\fB/\fP\fIbits\fP.)
+.TP
+\fB\-\-xor\-mark\fP \fIbits\fP
+Binary XOR the nfmark with \fIbits\fP. (Mnemonic for \fB\-\-set\-xmark\fP
+\fIbits\fP\fB/0\fP.)
diff --git a/extensions/libxt_MASQUERADE.man b/extensions/libxt_MASQUERADE.man
new file mode 100644
index 0000000..c9e3950
--- /dev/null
+++ b/extensions/libxt_MASQUERADE.man
@@ -0,0 +1,28 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain.  It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target.  Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down.  This is the correct behavior when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway).
+.TP
+\fB\-\-to\-ports\fP \fIport\fP[\fB\-\fP\fIport\fP]
+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 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).
+.TP
+IPv6 support available since Linux kernels >= 3.7.
diff --git a/extensions/libxt_NETMAP.man b/extensions/libxt_NETMAP.man
new file mode 100644
index 0000000..06507db
--- /dev/null
+++ b/extensions/libxt_NETMAP.man
@@ -0,0 +1,11 @@
+This target allows you to statically map a whole network of addresses onto
+another network of addresses.  It can only be used from rules in the
+.B nat
+table.
+.TP
+\fB\-\-to\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+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.c b/extensions/libxt_NFLOG.c
new file mode 100644
index 0000000..448576a
--- /dev/null
+++ b/extensions/libxt_NFLOG.c
@@ -0,0 +1,106 @@
+#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_NFLOG.h>
+
+enum {
+	O_GROUP = 0,
+	O_PREFIX,
+	O_RANGE,
+	O_THRESHOLD,
+};
+
+#define s struct xt_nflog_info
+static const struct xt_option_entry NFLOG_opts[] = {
+	{.name = "nflog-group", .id = O_GROUP, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, group)},
+	{.name = "nflog-prefix", .id = O_PREFIX, .type = XTTYPE_STRING,
+	 .min = 1, .flags = XTOPT_PUT, XTOPT_POINTER(s, prefix)},
+	{.name = "nflog-range", .id = O_RANGE, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, len)},
+	{.name = "nflog-threshold", .id = O_THRESHOLD, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, threshold)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void NFLOG_help(void)
+{
+	printf("NFLOG target options:\n"
+	       " --nflog-group NUM		NETLINK group used for logging\n"
+	       " --nflog-range NUM		Number of byte to copy\n"
+	       " --nflog-threshold NUM		Message threshold of in-kernel queue\n"
+	       " --nflog-prefix STRING		Prefix string for log messages\n");
+}
+
+static void NFLOG_init(struct xt_entry_target *t)
+{
+	struct xt_nflog_info *info = (struct xt_nflog_info *)t->data;
+
+	info->threshold	= XT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static void NFLOG_parse(struct xt_option_call *cb)
+{
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_PREFIX:
+		if (strchr(cb->arg, '\n') != NULL)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Newlines not allowed in --log-prefix");
+		break;
+	}
+}
+
+static void nflog_print(const struct xt_nflog_info *info, char *prefix)
+{
+	if (info->prefix[0] != '\0') {
+		printf(" %snflog-prefix ", prefix);
+		xtables_save_string(info->prefix);
+	}
+	if (info->group)
+		printf(" %snflog-group %u", prefix, info->group);
+	if (info->len)
+		printf(" %snflog-range %u", prefix, info->len);
+	if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD)
+		printf(" %snflog-threshold %u", prefix, info->threshold);
+}
+
+static void NFLOG_print(const void *ip, const struct xt_entry_target *target,
+                        int numeric)
+{
+	const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+	nflog_print(info, "");
+}
+
+static void NFLOG_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data;
+
+	nflog_print(info, "--");
+}
+
+static struct xtables_target nflog_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "NFLOG",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_nflog_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_nflog_info)),
+	.help		= NFLOG_help,
+	.init		= NFLOG_init,
+	.x6_parse	= NFLOG_parse,
+	.print		= NFLOG_print,
+	.save		= NFLOG_save,
+	.x6_options	= NFLOG_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&nflog_target);
+}
diff --git a/extensions/libxt_NFLOG.man b/extensions/libxt_NFLOG.man
new file mode 100644
index 0000000..1b6dbf1
--- /dev/null
+++ b/extensions/libxt_NFLOG.man
@@ -0,0 +1,29 @@
+This target provides logging of matching packets. When this target is
+set for a rule, the Linux kernel will pass the packet to the loaded
+logging backend to log the packet. This is usually used in combination
+with nfnetlink_log as logging backend, which will multicast the packet
+through a
+.IR netlink
+socket to the specified multicast group. One or more userspace processes
+may subscribe to the group to receive the packets. Like LOG, this is a
+non-terminating target, i.e. rule traversal continues at the next rule.
+.TP
+\fB\-\-nflog\-group\fP \fInlgroup\fP
+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
+A prefix string to include in the log message, up to 64 characters
+long, useful for distinguishing messages in the logs.
+.TP
+\fB\-\-nflog\-range\fP \fIsize\fP
+The number of bytes to be copied to userspace (only applicable for
+nfnetlink_log). nfnetlink_log instances may specify their own
+range, this option overrides it.
+.TP
+\fB\-\-nflog\-threshold\fP \fIsize\fP
+Number of packets to queue inside the kernel before sending them
+to userspace (only applicable for nfnetlink_log). Higher values
+result in less overhead per packet, but increase delay until the
+packets reach userspace. The default value is 1.
+.BR
diff --git a/extensions/libxt_NFQUEUE.c b/extensions/libxt_NFQUEUE.c
new file mode 100644
index 0000000..0c86918
--- /dev/null
+++ b/extensions/libxt_NFQUEUE.c
@@ -0,0 +1,265 @@
+/* Shared library add-on to iptables for NFQ
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_NFQUEUE.h>
+
+enum {
+	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)
+{
+	printf(
+"NFQUEUE target options\n"
+"  --queue-num value		Send packet to QUEUE number <value>.\n"
+"  		                Valid queue numbers are 0-65535\n"
+);
+}
+
+static void NFQUEUE_help_v1(void)
+{
+	NFQUEUE_help();
+	printf(
+"  --queue-balance first:last	Balance flows between queues <value> to <value>.\n");
+}
+
+static void NFQUEUE_help_v2(void)
+{
+	NFQUEUE_help_v1();
+	printf(
+"  --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
+static const struct xt_option_entry NFQUEUE_opts[] = {
+	{.name = "queue-num", .id = O_QUEUE_NUM, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, queuenum),
+	 .excl = F_QUEUE_BALANCE},
+	{.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
+
+static void NFQUEUE_parse(struct xt_option_call *cb)
+{
+	xtables_option_parse(cb);
+	if (cb->entry->id == O_QUEUE_BALANCE)
+		xtables_error(PARAMETER_PROBLEM, "NFQUEUE target: "
+				   "--queue-balance not supported (kernel too old?)");
+}
+
+static void NFQUEUE_parse_v1(struct xt_option_call *cb)
+{
+	struct xt_NFQ_info_v1 *info = cb->data;
+	const uint16_t *r = cb->val.u16_range;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_QUEUE_BALANCE:
+		if (cb->nvals != 2)
+			xtables_error(PARAMETER_PROBLEM,
+				"Bad range \"%s\"", cb->arg);
+		if (r[0] >= r[1])
+			xtables_error(PARAMETER_PROBLEM, "%u should be less than %u",
+				r[0], r[1]);
+		info->queuenum = r[0];
+		info->queues_total = r[1] - r[0] + 1;
+		break;
+	}
+}
+
+static void NFQUEUE_parse_v2(struct xt_option_call *cb)
+{
+	struct xt_NFQ_info_v2 *info = cb->data;
+
+	NFQUEUE_parse_v1(cb);
+	switch (cb->entry->id) {
+	case O_QUEUE_BYPASS:
+		info->bypass = 1;
+		break;
+	}
+}
+
+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)
+{
+	const struct xt_NFQ_info *tinfo =
+		(const struct xt_NFQ_info *)target->data;
+	printf(" NFQUEUE num %u", tinfo->queuenum);
+}
+
+static void NFQUEUE_print_v1(const void *ip,
+                             const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_NFQ_info_v1 *tinfo = (const void *)target->data;
+	unsigned int last = tinfo->queues_total;
+
+	if (last > 1) {
+		last += tinfo->queuenum - 1;
+		printf(" NFQUEUE balance %u:%u", tinfo->queuenum, last);
+	} else {
+		printf(" NFQUEUE num %u", tinfo->queuenum);
+	}
+}
+
+static void NFQUEUE_print_v2(const void *ip,
+                             const struct xt_entry_target *target, int numeric)
+{
+	const struct xt_NFQ_info_v2 *info = (void *) target->data;
+
+	NFQUEUE_print_v1(ip, target, numeric);
+	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 =
+		(const struct xt_NFQ_info *)target->data;
+
+	printf(" --queue-num %u", tinfo->queuenum);
+}
+
+static void NFQUEUE_save_v1(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_NFQ_info_v1 *tinfo = (const void *)target->data;
+	unsigned int last = tinfo->queues_total;
+
+	if (last > 1) {
+		last += tinfo->queuenum - 1;
+		printf(" --queue-balance %u:%u", tinfo->queuenum, last);
+	} else {
+		printf(" --queue-num %u", tinfo->queuenum);
+	}
+}
+
+static void NFQUEUE_save_v2(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_NFQ_info_v2 *info = (void *) target->data;
+
+	NFQUEUE_save_v1(ip, target);
+
+	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)
+{
+	struct xt_NFQ_info_v1 *tinfo = (void *)t->data;
+	tinfo->queues_total = 1;
+}
+
+static struct xtables_target nfqueue_targets[] = {
+{
+	.family		= NFPROTO_UNSPEC,
+	.name		= "NFQUEUE",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_NFQ_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_NFQ_info)),
+	.help		= NFQUEUE_help,
+	.print		= NFQUEUE_print,
+	.save		= NFQUEUE_save,
+	.x6_parse	= NFQUEUE_parse,
+	.x6_options	= NFQUEUE_opts
+},{
+	.family		= NFPROTO_UNSPEC,
+	.revision	= 1,
+	.name		= "NFQUEUE",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_NFQ_info_v1)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_NFQ_info_v1)),
+	.help		= NFQUEUE_help_v1,
+	.init		= NFQUEUE_init_v1,
+	.print		= NFQUEUE_print_v1,
+	.save		= NFQUEUE_save_v1,
+	.x6_parse	= NFQUEUE_parse_v1,
+	.x6_options	= NFQUEUE_opts,
+},{
+	.family		= NFPROTO_UNSPEC,
+	.revision	= 2,
+	.name		= "NFQUEUE",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_NFQ_info_v2)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_NFQ_info_v2)),
+	.help		= NFQUEUE_help_v2,
+	.init		= NFQUEUE_init_v1,
+	.print		= NFQUEUE_print_v2,
+	.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,
+}
+};
+
+void _init(void)
+{
+	xtables_register_targets(nfqueue_targets, ARRAY_SIZE(nfqueue_targets));
+}
diff --git a/extensions/libxt_NFQUEUE.man b/extensions/libxt_NFQUEUE.man
new file mode 100644
index 0000000..1bfb7b8
--- /dev/null
+++ b/extensions/libxt_NFQUEUE.man
@@ -0,0 +1,33 @@
+This target passes the packet to userspace using the
+\fBnfnetlink_queue\fP handler.  The packet is put into the queue
+identified by its 16-bit queue number.  Userspace can inspect
+and modify the packet if desired. Userspace must then drop or
+reinject the packet into the kernel.  Please see libnetfilter_queue
+for details.
+.B
+nfnetlink_queue
+was added in Linux 2.6.14. The \fBqueue-balance\fP option was added in Linux 2.6.31,
+\fBqueue-bypass\fP in 2.6.39.
+.TP
+\fB\-\-queue\-num\fP \fIvalue\fP
+This specifies the QUEUE number to use. Valid queue numbers are 0 to 65535. The default value is 0.
+.PP
+.TP
+\fB\-\-queue\-balance\fP \fIvalue\fP\fB:\fP\fIvalue\fP
+This specifies a range of queues to use. Packets are then balanced across the given queues.
+This is useful for multicore systems: start multiple instances of the userspace program on
+queues x, x+1, .. x+n and use "\-\-queue\-balance \fIx\fP\fB:\fP\fIx+n\fP".
+Packets belonging to the same connection are put into the same nfqueue.
+.PP
+.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 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.man b/extensions/libxt_NOTRACK.man
new file mode 100644
index 0000000..4302b93
--- /dev/null
+++ b/extensions/libxt_NOTRACK.man
@@ -0,0 +1,3 @@
+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
new file mode 100644
index 0000000..449ceab
--- /dev/null
+++ b/extensions/libxt_RATEEST.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2008-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_RATEEST.h>
+
+struct rateest_tg_udata {
+	unsigned int interval;
+	unsigned int ewma_log;
+};
+
+static void
+RATEEST_help(void)
+{
+	printf(
+"RATEEST target options:\n"
+"  --rateest-name name		Rate estimator name\n"
+"  --rateest-interval sec	Rate measurement interval in seconds\n"
+"  --rateest-ewmalog value	Rate measurement averaging time constant\n");
+}
+
+enum {
+	O_NAME = 0,
+	O_INTERVAL,
+	O_EWMALOG,
+};
+
+#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
+
+static int
+RATEEST_get_time(unsigned int *time, const char *str)
+{
+	double t;
+	char *p;
+
+	t = strtod(str, &p);
+	if (p == str)
+		return -1;
+
+	if (*p) {
+		if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 ||
+		    strcasecmp(p, "secs")==0)
+			t *= TIME_UNITS_PER_SEC;
+		else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 ||
+			 strcasecmp(p, "msecs") == 0)
+			t *= TIME_UNITS_PER_SEC/1000;
+		else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 ||
+			 strcasecmp(p, "usecs") == 0)
+			t *= TIME_UNITS_PER_SEC/1000000;
+		else
+			return -1;
+	}
+
+	*time = t;
+	return 0;
+}
+
+static void
+RATEEST_print_time(unsigned int time)
+{
+	double tmp = time;
+
+	if (tmp >= TIME_UNITS_PER_SEC)
+		printf(" %.1fs", tmp / TIME_UNITS_PER_SEC);
+	else if (tmp >= TIME_UNITS_PER_SEC/1000)
+		printf(" %.1fms", tmp / (TIME_UNITS_PER_SEC / 1000));
+	else
+		printf(" %uus", time);
+}
+
+static void RATEEST_parse(struct xt_option_call *cb)
+{
+	struct rateest_tg_udata *udata = cb->udata;
+
+	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: bad interval value \"%s\"",
+				   cb->arg);
+		break;
+	case O_EWMALOG:
+		if (RATEEST_get_time(&udata->ewma_log, cb->arg) < 0)
+			xtables_error(PARAMETER_PROBLEM,
+				   "RATEEST: bad ewmalog value \"%s\"",
+				   cb->arg);
+		break;
+	}
+}
+
+static void RATEEST_final_check(struct xt_fcheck_call *cb)
+{
+	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 (udata->interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4))
+			break;
+	}
+
+	if (info->interval > 5)
+		xtables_error(PARAMETER_PROBLEM,
+			   "RATEEST: interval value is too large");
+	info->interval -= 2;
+
+	for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) {
+		double w = 1.0 - 1.0 / (1 << info->ewma_log);
+		if (udata->interval / (-log(w)) > udata->ewma_log)
+			break;
+	}
+	info->ewma_log--;
+
+	if (info->ewma_log == 0 || info->ewma_log >= 31)
+		xtables_error(PARAMETER_PROBLEM,
+			   "RATEEST: ewmalog value is out of range");
+}
+
+static void
+__RATEEST_print(const struct xt_entry_target *target, const char *prefix)
+{
+	const struct xt_rateest_target_info *info = (const void *)target->data;
+	unsigned int local_interval;
+	unsigned int local_ewma_log;
+
+	local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4;
+	local_ewma_log = local_interval * (1 << (info->ewma_log));
+
+	printf(" %sname %s", prefix, info->name);
+	printf(" %sinterval", prefix);
+	RATEEST_print_time(local_interval);
+	printf(" %sewmalog", prefix);
+	RATEEST_print_time(local_ewma_log);
+}
+
+static void
+RATEEST_print(const void *ip, const struct xt_entry_target *target,
+	      int numeric)
+{
+	__RATEEST_print(target, "");
+}
+
+static void
+RATEEST_save(const void *ip, const struct xt_entry_target *target)
+{
+	__RATEEST_print(target, "--rateest-");
+}
+
+static struct xtables_target rateest_tg_reg = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "RATEEST",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_rateest_target_info)),
+	.userspacesize	= offsetof(struct xt_rateest_target_info, est),
+	.help		= RATEEST_help,
+	.x6_parse	= RATEEST_parse,
+	.x6_fcheck	= RATEEST_final_check,
+	.print		= RATEEST_print,
+	.save		= RATEEST_save,
+	.x6_options	= RATEEST_opts,
+	.udata_size	= sizeof(struct rateest_tg_udata),
+};
+
+void _init(void)
+{
+	xtables_register_target(&rateest_tg_reg);
+}
diff --git a/extensions/libxt_RATEEST.man b/extensions/libxt_RATEEST.man
new file mode 100644
index 0000000..37de759
--- /dev/null
+++ b/extensions/libxt_RATEEST.man
@@ -0,0 +1,12 @@
+The RATEEST target collects statistics, performs rate estimation calculation
+and saves the results for later evaluation using the \fBrateest\fP match.
+.TP
+\fB\-\-rateest\-name\fP \fIname\fP
+Count matched packets into the pool referred to by \fIname\fP, which is freely
+choosable.
+.TP
+\fB\-\-rateest\-interval\fP \fIamount\fP{\fBs\fP|\fBms\fP|\fBus\fP}
+Rate measurement interval, in seconds, milliseconds or microseconds.
+.TP
+\fB\-\-rateest\-ewmalog\fP \fIvalue\fP
+Rate measurement averaging time constant.
diff --git a/extensions/libxt_REDIRECT.man b/extensions/libxt_REDIRECT.man
new file mode 100644
index 0000000..3400a6d
--- /dev/null
+++ b/extensions/libxt_REDIRECT.man
@@ -0,0 +1,24 @@
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+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 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 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).
+.TP
+IPv6 support available starting Linux kernels >= 3.7.
diff --git a/extensions/libxt_SECMARK.c b/extensions/libxt_SECMARK.c
new file mode 100644
index 0000000..6ba8606
--- /dev/null
+++ b/extensions/libxt_SECMARK.c
@@ -0,0 +1,88 @@
+/*
+ * Shared library add-on to iptables to add SECMARK target support.
+ *
+ * Based on the MARK target.
+ *
+ * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_SECMARK.h>
+
+#define PFX "SECMARK target: "
+
+enum {
+	O_SELCTX = 0,
+};
+
+static void SECMARK_help(void)
+{
+	printf(
+"SECMARK target options:\n"
+"  --selctx value                     Set the SELinux security context\n");
+}
+
+static const struct xt_option_entry SECMARK_opts[] = {
+	{.name = "selctx", .id = O_SELCTX, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_secmark_target_info, secctx)},
+	XTOPT_TABLEEND,
+};
+
+static void SECMARK_parse(struct xt_option_call *cb)
+{
+	struct xt_secmark_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	info->mode = SECMARK_MODE_SEL;
+}
+
+static void print_secmark(const struct xt_secmark_target_info *info)
+{
+	switch (info->mode) {
+	case SECMARK_MODE_SEL:
+		printf("selctx %s", info->secctx);
+		break;
+	
+	default:
+		xtables_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode);
+	}
+}
+
+static void SECMARK_print(const void *ip, const struct xt_entry_target *target,
+                          int numeric)
+{
+	const struct xt_secmark_target_info *info =
+		(struct xt_secmark_target_info*)(target)->data;
+
+	printf(" SECMARK ");
+	print_secmark(info);
+}
+
+static void SECMARK_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_secmark_target_info *info =
+		(struct xt_secmark_target_info*)target->data;
+
+	printf(" --");
+	print_secmark(info);
+}
+
+static struct xtables_target secmark_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "SECMARK",
+	.version	= XTABLES_VERSION,
+	.revision	= 0,
+	.size		= XT_ALIGN(sizeof(struct xt_secmark_target_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_secmark_target_info)),
+	.help		= SECMARK_help,
+	.print		= SECMARK_print,
+	.save		= SECMARK_save,
+	.x6_parse	= SECMARK_parse,
+	.x6_options	= SECMARK_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&secmark_target);
+}
diff --git a/extensions/libxt_SECMARK.man b/extensions/libxt_SECMARK.man
new file mode 100644
index 0000000..d0e6fd6
--- /dev/null
+++ b/extensions/libxt_SECMARK.man
@@ -0,0 +1,10 @@
+This is used to set the security mark value associated with the
+packet for use by security subsystems such as SELinux.  It is
+valid in the
+.B security
+table (for backwards compatibility with older kernels, it is also
+valid in the
+.B mangle
+table). The mark is 32 bits wide.
+.TP
+\fB\-\-selctx\fP \fIsecurity_context\fP
diff --git a/extensions/libxt_SET.c b/extensions/libxt_SET.c
new file mode 100644
index 0000000..a11db39
--- /dev/null
+++ b/extensions/libxt_SET.c
@@ -0,0 +1,400 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2010 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.  
+ */
+
+/* Shared library add-on to iptables to add IP set mangling target. */
+#include <stdbool.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <xtables.h>
+#include <linux/netfilter/xt_set.h>
+#include "libxt_set.h"
+
+/* Revision 0 */
+
+static void
+set_target_help_v0(void)
+{
+	printf("SET target options:\n"
+	       " --add-set name flags\n"
+	       " --del-set name flags\n"
+	       "		add/del src/dst IP/port from/to named sets,\n"
+	       "		where flags are the comma separated list of\n"
+	       "		'src' and 'dst' specifications.\n");
+}
+
+static const struct option set_target_opts_v0[] = {
+	{.name = "add-set", .has_arg = true, .val = '1'},
+	{.name = "del-set", .has_arg = true, .val = '2'},
+	XT_GETOPT_TABLEEND,
+};
+
+static void
+set_target_check_v0(unsigned int flags)
+{
+	if (!flags)
+		xtables_error(PARAMETER_PROBLEM,
+			   "You must specify either `--add-set' or `--del-set'");
+}
+
+static void
+set_target_init_v0(struct xt_entry_target *target)
+{
+	struct xt_set_info_target_v0 *info =
+		(struct xt_set_info_target_v0 *) target->data;
+
+	info->add_set.index =
+	info->del_set.index = IPSET_INVALID_ID;
+
+}
+
+static void
+parse_target_v0(char **argv, int invert, unsigned int *flags,
+		struct xt_set_info_v0 *info, const char *what)
+{
+	if (info->u.flags[0])
+		xtables_error(PARAMETER_PROBLEM,
+			      "--%s can be specified only once", what);
+
+	if (!argv[optind]
+	    || argv[optind][0] == '-' || argv[optind][0] == '!')
+		xtables_error(PARAMETER_PROBLEM,
+			      "--%s requires two args.", what);
+
+	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, (struct xt_set_info *)info);
+	parse_dirs_v0(argv[optind], info);
+	optind++;
+	
+	*flags = 1;
+}
+
+static int
+set_target_parse_v0(int c, char **argv, int invert, unsigned int *flags,
+		    const void *entry, struct xt_entry_target **target)
+{
+	struct xt_set_info_target_v0 *myinfo =
+		(struct xt_set_info_target_v0 *) (*target)->data;
+
+	switch (c) {
+	case '1':		/* --add-set <set> <flags> */
+		parse_target_v0(argv, invert, flags,
+				&myinfo->add_set, "add-set");
+		break;
+	case '2':		/* --del-set <set>[:<flags>] <flags> */
+		parse_target_v0(argv, invert, flags,
+				&myinfo->del_set, "del-set");
+		break;
+	}
+	return 1;
+}
+
+static void
+print_target_v0(const char *prefix, const struct xt_set_info_v0 *info)
+{
+	int i;
+	char setname[IPSET_MAXNAMELEN];
+
+	if (info->index == IPSET_INVALID_ID)
+		return;
+	get_set_byid(setname, info->index);
+	printf(" %s %s", prefix, setname);
+	for (i = 0; i < IPSET_DIM_MAX; i++) {
+		if (!info->u.flags[i])
+			break;		
+		printf("%s%s",
+		       i == 0 ? " " : ",",
+		       info->u.flags[i] & IPSET_SRC ? "src" : "dst");
+	}
+}
+
+static void
+set_target_print_v0(const void *ip, const struct xt_entry_target *target,
+                    int numeric)
+{
+	const struct xt_set_info_target_v0 *info = (const void *)target->data;
+
+	print_target_v0("add-set", &info->add_set);
+	print_target_v0("del-set", &info->del_set);
+}
+
+static void
+set_target_save_v0(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_set_info_target_v0 *info = (const void *)target->data;
+
+	print_target_v0("--add-set", &info->add_set);
+	print_target_v0("--del-set", &info->del_set);
+}
+
+/* Revision 1 */
+static void
+set_target_init_v1(struct xt_entry_target *target)
+{
+	struct xt_set_info_target_v1 *info =
+		(struct xt_set_info_target_v1 *) target->data;
+
+	info->add_set.index =
+	info->del_set.index = IPSET_INVALID_ID;
+
+}
+
+#define SET_TARGET_ADD		0x1
+#define SET_TARGET_DEL		0x2
+#define SET_TARGET_EXIST	0x4
+#define SET_TARGET_TIMEOUT	0x8
+
+static void
+parse_target(char **argv, int invert, struct xt_set_info *info,
+	     const char *what)
+{
+	if (info->dim)
+		xtables_error(PARAMETER_PROBLEM,
+			      "--%s can be specified only once", what);
+	if (!argv[optind]
+	    || argv[optind][0] == '-' || argv[optind][0] == '!')
+		xtables_error(PARAMETER_PROBLEM,
+			      "--%s requires two args.", what);
+
+	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);
+	optind++;
+}
+
+static int
+set_target_parse_v1(int c, char **argv, int invert, unsigned int *flags,
+		    const void *entry, struct xt_entry_target **target)
+{
+	struct xt_set_info_target_v1 *myinfo =
+		(struct xt_set_info_target_v1 *) (*target)->data;
+
+	switch (c) {
+	case '1':		/* --add-set <set> <flags> */
+		parse_target(argv, invert, &myinfo->add_set, "add-set");
+		*flags |= SET_TARGET_ADD;
+		break;
+	case '2':		/* --del-set <set>[:<flags>] <flags> */
+		parse_target(argv, invert, &myinfo->del_set, "del-set");
+		*flags |= SET_TARGET_DEL;
+		break;
+	}
+	return 1;
+}
+
+static void
+print_target(const char *prefix, const struct xt_set_info *info)
+{
+	int i;
+	char setname[IPSET_MAXNAMELEN];
+
+	if (info->index == IPSET_INVALID_ID)
+		return;
+	get_set_byid(setname, info->index);
+	printf(" %s %s", prefix, setname);
+	for (i = 1; i <= info->dim; i++) {
+		printf("%s%s",
+		       i == 1 ? " " : ",",
+		       info->flags & (1 << i) ? "src" : "dst");
+	}
+}
+
+static void
+set_target_print_v1(const void *ip, const struct xt_entry_target *target,
+		    int numeric)
+{
+	const struct xt_set_info_target_v1 *info = (const void *)target->data;
+
+	print_target("add-set", &info->add_set);
+	print_target("del-set", &info->del_set);
+}
+
+static void
+set_target_save_v1(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_set_info_target_v1 *info = (const void *)target->data;
+
+	print_target("--add-set", &info->add_set);
+	print_target("--del-set", &info->del_set);
+}
+
+/* Revision 2 */
+
+static void
+set_target_help_v2(void)
+{
+	printf("SET target options:\n"
+	       " --add-set name flags [--exist] [--timeout n]\n"
+	       " --del-set name flags\n"
+	       "		add/del src/dst IP/port from/to named sets,\n"
+	       "		where flags are the comma separated list of\n"
+	       "		'src' and 'dst' specifications.\n");
+}
+
+static const struct option set_target_opts_v2[] = {
+	{.name = "add-set", .has_arg = true,  .val = '1'},
+	{.name = "del-set", .has_arg = true,  .val = '2'},
+	{.name = "exist",   .has_arg = false, .val = '3'},
+	{.name = "timeout", .has_arg = true,  .val = '4'},
+	XT_GETOPT_TABLEEND,
+};
+
+static void
+set_target_check_v2(unsigned int flags)
+{
+	if (!(flags & (SET_TARGET_ADD|SET_TARGET_DEL)))
+		xtables_error(PARAMETER_PROBLEM,
+			   "You must specify either `--add-set' or `--del-set'");
+	if (!(flags & SET_TARGET_ADD)) {
+		if (flags & SET_TARGET_EXIST)
+			xtables_error(PARAMETER_PROBLEM,
+				"Flag `--exist' can be used with `--add-set' only");
+		if (flags & SET_TARGET_TIMEOUT)
+			xtables_error(PARAMETER_PROBLEM,
+				"Option `--timeout' can be used with `--add-set' only");
+	}
+}
+
+
+static void
+set_target_init_v2(struct xt_entry_target *target)
+{
+	struct xt_set_info_target_v2 *info =
+		(struct xt_set_info_target_v2 *) target->data;
+
+	info->add_set.index =
+	info->del_set.index = IPSET_INVALID_ID;
+	info->timeout = UINT32_MAX;
+}
+
+static int
+set_target_parse_v2(int c, char **argv, int invert, unsigned int *flags,
+		    const void *entry, struct xt_entry_target **target)
+{
+	struct xt_set_info_target_v2 *myinfo =
+		(struct xt_set_info_target_v2 *) (*target)->data;
+	unsigned int timeout;
+
+	switch (c) {
+	case '1':		/* --add-set <set> <flags> */
+		parse_target(argv, invert, &myinfo->add_set, "add-set");
+		*flags |= SET_TARGET_ADD;
+		break;
+	case '2':		/* --del-set <set>[:<flags>] <flags> */
+		parse_target(argv, invert, &myinfo->del_set, "del-set");
+		*flags |= SET_TARGET_DEL;
+		break;
+	case '3':
+		myinfo->flags |= IPSET_FLAG_EXIST;
+		*flags |= SET_TARGET_EXIST;
+		break;
+	case '4':
+		if (!xtables_strtoui(optarg, NULL, &timeout, 0, UINT32_MAX - 1))
+			xtables_error(PARAMETER_PROBLEM,
+				      "Invalid value for option --timeout "
+				      "or out of range 0-%u", UINT32_MAX - 1);
+		myinfo->timeout = timeout;
+		*flags |= SET_TARGET_TIMEOUT;
+		break;	
+	}
+	return 1;
+}
+
+static void
+set_target_print_v2(const void *ip, const struct xt_entry_target *target,
+		    int numeric)
+{
+	const struct xt_set_info_target_v2 *info = (const void *)target->data;
+
+	print_target("add-set", &info->add_set);
+	if (info->flags & IPSET_FLAG_EXIST)
+		printf(" exist");
+	if (info->timeout != UINT32_MAX)
+		printf(" timeout %u", info->timeout);
+	print_target("del-set", &info->del_set);
+}
+
+static void
+set_target_save_v2(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_set_info_target_v2 *info = (const void *)target->data;
+
+	print_target("--add-set", &info->add_set);
+	if (info->flags & IPSET_FLAG_EXIST)
+		printf(" --exist");
+	if (info->timeout != UINT32_MAX)
+		printf(" --timeout %u", info->timeout);
+	print_target("--del-set", &info->del_set);
+}
+
+static struct xtables_target set_tg_reg[] = {
+	{
+		.name		= "SET",
+		.revision	= 0,
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV4,
+		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v0)),
+		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v0)),
+		.help		= set_target_help_v0,
+		.init		= set_target_init_v0,
+		.parse		= set_target_parse_v0,
+		.final_check	= set_target_check_v0,
+		.print		= set_target_print_v0,
+		.save		= set_target_save_v0,
+		.extra_opts	= set_target_opts_v0,
+	},
+	{
+		.name		= "SET",
+		.revision	= 1,
+		.version	= XTABLES_VERSION,
+		.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_v0,
+		.init		= set_target_init_v1,
+		.parse		= set_target_parse_v1,
+		.final_check	= set_target_check_v0,
+		.print		= set_target_print_v1,
+		.save		= set_target_save_v1,
+		.extra_opts	= set_target_opts_v0,
+	},
+	{
+		.name		= "SET",
+		.revision	= 2,
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_UNSPEC,
+		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v2)),
+		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v2)),
+		.help		= set_target_help_v2,
+		.init		= set_target_init_v2,
+		.parse		= set_target_parse_v2,
+		.final_check	= set_target_check_v2,
+		.print		= set_target_print_v2,
+		.save		= set_target_save_v2,
+		.extra_opts	= set_target_opts_v2,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg));
+}
diff --git a/extensions/libxt_SET.man b/extensions/libxt_SET.man
new file mode 100644
index 0000000..c35ba93
--- /dev/null
+++ b/extensions/libxt_SET.man
@@ -0,0 +1,25 @@
+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 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 set
+.IP
+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 an entry, the timeout value to use instead of the default
+one from the set definition
+.TP
+\fB\-\-exist\fP
+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, which, for
+standard kernels, is the case since Linux 2.6.39.
diff --git a/extensions/libxt_SNAT.man b/extensions/libxt_SNAT.man
new file mode 100644
index 0000000..f0620a2
--- /dev/null
+++ b/extensions/libxt_SNAT.man
@@ -0,0 +1,45 @@
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+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 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. 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
+in cycle) takes place between these addresses.
+Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges
+anymore.
+.TP
+\fB\-\-random\fP
+If option
+\fB\-\-random\fP
+is used then port mapping will be randomized (kernel >= 2.6.21).
+.TP
+\fB\-\-persistent\fP
+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_SYNPROXY.c b/extensions/libxt_SYNPROXY.c
new file mode 100644
index 0000000..475590e
--- /dev/null
+++ b/extensions/libxt_SYNPROXY.c
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * 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.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_SYNPROXY.h>
+
+enum {
+	O_SACK_PERM = 0,
+	O_TIMESTAMP,
+	O_WSCALE,
+	O_MSS,
+	O_ECN,
+};
+
+static void SYNPROXY_help(void)
+{
+	printf(
+"SYNPROXY target options:\n"
+"  --sack-perm                        Set SACK_PERM\n"
+"  --timestamp                        Set TIMESTAMP\n"
+"  --wscale value                     Set window scaling factor\n"
+"  --mss value                        Set MSS value\n"
+"  --ecn                              Set ECN\n");
+}
+
+static const struct xt_option_entry SYNPROXY_opts[] = {
+	{.name = "sack-perm", .id = O_SACK_PERM, .type = XTTYPE_NONE, },
+	{.name = "timestamp", .id = O_TIMESTAMP, .type = XTTYPE_NONE, },
+	{.name = "wscale",    .id = O_WSCALE,    .type = XTTYPE_UINT32, },
+	{.name = "mss",       .id = O_MSS,       .type = XTTYPE_UINT32, },
+	{.name = "ecn",       .id = O_ECN,	 .type = XTTYPE_NONE, },
+	XTOPT_TABLEEND,
+};
+
+static void SYNPROXY_parse(struct xt_option_call *cb)
+{
+	struct xt_synproxy_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SACK_PERM:
+		info->options |= XT_SYNPROXY_OPT_SACK_PERM;
+		break;
+	case O_TIMESTAMP:
+		info->options |= XT_SYNPROXY_OPT_TIMESTAMP;
+		break;
+	case O_WSCALE:
+		info->options |= XT_SYNPROXY_OPT_WSCALE;
+		info->wscale = cb->val.u32;
+		break;
+	case O_MSS:
+		info->options |= XT_SYNPROXY_OPT_MSS;
+		info->mss = cb->val.u32;
+		break;
+	case O_ECN:
+		info->options |= XT_SYNPROXY_OPT_ECN;
+		break;
+	}
+}
+
+static void SYNPROXY_check(struct xt_fcheck_call *cb)
+{
+}
+
+static void SYNPROXY_print(const void *ip, const struct xt_entry_target *target,
+                           int numeric)
+{
+	const struct xt_synproxy_info *info =
+		(const struct xt_synproxy_info *)target->data;
+
+	printf(" SYNPROXY ");
+	if (info->options & XT_SYNPROXY_OPT_SACK_PERM)
+		printf("sack-perm ");
+	if (info->options & XT_SYNPROXY_OPT_TIMESTAMP)
+		printf("timestamp ");
+	if (info->options & XT_SYNPROXY_OPT_WSCALE)
+		printf("wscale %u ", info->wscale);
+	if (info->options & XT_SYNPROXY_OPT_MSS)
+		printf("mss %u ", info->mss);
+	if (info->options & XT_SYNPROXY_OPT_ECN)
+		printf("ecn ");
+}
+
+static void SYNPROXY_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_synproxy_info *info =
+		(const struct xt_synproxy_info *)target->data;
+
+	if (info->options & XT_SYNPROXY_OPT_SACK_PERM)
+		printf(" --sack-perm");
+	if (info->options & XT_SYNPROXY_OPT_TIMESTAMP)
+		printf(" --timestamp");
+	if (info->options & XT_SYNPROXY_OPT_WSCALE)
+		printf(" --wscale %u", info->wscale);
+	if (info->options & XT_SYNPROXY_OPT_MSS)
+		printf(" --mss %u", info->mss);
+	if (info->options & XT_SYNPROXY_OPT_ECN)
+		printf(" --ecn");
+}
+
+static struct xtables_target synproxy_tg_reg = {
+	.family        = NFPROTO_UNSPEC,
+	.name          = "SYNPROXY",
+	.version       = XTABLES_VERSION,
+	.revision      = 0,
+	.size          = XT_ALIGN(sizeof(struct xt_synproxy_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_synproxy_info)),
+	.help          = SYNPROXY_help,
+	.print         = SYNPROXY_print,
+	.save          = SYNPROXY_save,
+	.x6_parse      = SYNPROXY_parse,
+	.x6_fcheck     = SYNPROXY_check,
+	.x6_options    = SYNPROXY_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&synproxy_tg_reg);
+}
diff --git a/extensions/libxt_TCPMSS.c b/extensions/libxt_TCPMSS.c
new file mode 100644
index 0000000..4b71e44
--- /dev/null
+++ b/extensions/libxt_TCPMSS.c
@@ -0,0 +1,126 @@
+/* Shared library add-on to iptables to add TCPMSS target support.
+ *
+ * Copyright (c) 2000 Marc Boucher
+*/
+#include "config.h"
+#include <stdio.h>
+#include <xtables.h>
+#include <netinet/ip.h>
+#include <linux/netfilter/xt_TCPMSS.h>
+
+enum {
+	O_SET_MSS = 0,
+	O_CLAMP_MSS,
+};
+
+struct mssinfo {
+	struct xt_entry_target t;
+	struct xt_tcpmss_info mss;
+};
+
+static void __TCPMSS_help(int hdrsize)
+{
+	printf(
+"TCPMSS target mutually-exclusive options:\n"
+"  --set-mss value               explicitly set MSS option to specified value\n"
+"  --clamp-mss-to-pmtu           automatically clamp MSS value to (path_MTU - %d)\n",
+hdrsize);
+}
+
+static void TCPMSS_help(void)
+{
+	__TCPMSS_help(sizeof(struct iphdr));
+}
+
+static void TCPMSS_help6(void)
+{
+	__TCPMSS_help(SIZEOF_STRUCT_IP6_HDR);
+}
+
+static const struct xt_option_entry TCPMSS4_opts[] = {
+	{.name = "set-mss", .id = O_SET_MSS, .type = XTTYPE_UINT16,
+	 .min = 0, .max = UINT16_MAX - sizeof(struct iphdr),
+	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_tcpmss_info, mss)},
+	{.name = "clamp-mss-to-pmtu", .id = O_CLAMP_MSS, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+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,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_tcpmss_info, mss)},
+	{.name = "clamp-mss-to-pmtu", .id = O_CLAMP_MSS, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static void TCPMSS_parse(struct xt_option_call *cb)
+{
+	struct xt_tcpmss_info *mssinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->entry->id == O_CLAMP_MSS)
+		mssinfo->mss = XT_TCPMSS_CLAMP_PMTU;
+}
+
+static void TCPMSS_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "TCPMSS target: At least one parameter is required");
+}
+
+static void TCPMSS_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct xt_tcpmss_info *mssinfo =
+		(const struct xt_tcpmss_info *)target->data;
+	if(mssinfo->mss == XT_TCPMSS_CLAMP_PMTU)
+		printf(" TCPMSS clamp to PMTU");
+	else
+		printf(" TCPMSS set %u", mssinfo->mss);
+}
+
+static void TCPMSS_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tcpmss_info *mssinfo =
+		(const struct xt_tcpmss_info *)target->data;
+
+	if(mssinfo->mss == XT_TCPMSS_CLAMP_PMTU)
+		printf(" --clamp-mss-to-pmtu");
+	else
+		printf(" --set-mss %u", mssinfo->mss);
+}
+
+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_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
+}
diff --git a/extensions/libxt_TCPMSS.man b/extensions/libxt_TCPMSS.man
new file mode 100644
index 0000000..8da8e76
--- /dev/null
+++ b/extensions/libxt_TCPMSS.man
@@ -0,0 +1,41 @@
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 40 for IPv4 or 60 for IPv6, respectively).
+Of course, it can only be used
+in conjunction with
+\fB\-p tcp\fP.
+.PP
+This target is used to overcome criminally braindead ISPs or servers
+which block "ICMP Fragmentation Needed" or "ICMPv6 Packet Too Big"
+packets.  The symptoms of this
+problem are that everything works fine from your Linux
+firewall/router, but machines behind it can never exchange large
+packets:
+.IP 1. 4
+Web browsers connect, then hang with no data received.
+.IP 2. 4
+Small mail works fine, but large emails hang.
+.IP 3. 4
+ssh works fine, but scp hangs after initial handshaking.
+.PP
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.IP
+ iptables \-t mangle \-A FORWARD \-p tcp \-\-tcp\-flags SYN,RST SYN
+             \-j TCPMSS \-\-clamp\-mss\-to\-pmtu
+.TP
+\fB\-\-set\-mss\fP \fIvalue\fP
+Explicitly sets MSS option to specified value. If the MSS of the packet is
+already lower than \fIvalue\fP, it will \fBnot\fP be increased (from Linux
+2.6.25 onwards) to avoid more problems with hosts relying on a proper MSS.
+.TP
+\fB\-\-clamp\-mss\-to\-pmtu\fP
+Automatically clamp MSS value to (path_MTU \- 40 for IPv4; \-60 for IPv6).
+This may not function as desired where asymmetric routes with differing
+path MTU exist \(em the kernel uses the path MTU which it would use to send
+packets from itself to the source and destination IP addresses. Prior to
+Linux 2.6.25, only the path MTU to the destination IP address was
+considered by this option; subsequent kernels also consider the path MTU
+to the source IP address.
+.PP
+These options are mutually exclusive.
diff --git a/extensions/libxt_TCPOPTSTRIP.c b/extensions/libxt_TCPOPTSTRIP.c
new file mode 100644
index 0000000..6897857
--- /dev/null
+++ b/extensions/libxt_TCPOPTSTRIP.c
@@ -0,0 +1,167 @@
+/*
+ * Shared library add-on to iptables to add TCPOPTSTRIP target support.
+ * Copyright (c) 2007 Sven Schnelle <svens@bitebene.org>
+ * Copyright © CC Computer Consultants GmbH, 2007
+ * Jan Engelhardt <jengelh@computergmbh.de>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <netinet/tcp.h>
+#include <linux/netfilter/xt_TCPOPTSTRIP.h>
+#ifndef TCPOPT_MD5SIG
+#	define TCPOPT_MD5SIG 19
+#endif
+
+enum {
+	O_STRIP_OPTION = 0,
+};
+
+struct tcp_optionmap {
+	const char *name, *desc;
+	const unsigned int option;
+};
+
+static const struct xt_option_entry tcpoptstrip_tg_opts[] = {
+	{.name = "strip-options", .id = O_STRIP_OPTION, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+
+static const struct tcp_optionmap tcp_optionmap[] = {
+	{"wscale",         "Window scale",         TCPOPT_WINDOW},
+	{"mss",            "Maximum Segment Size", TCPOPT_MAXSEG},
+	{"sack-permitted", "SACK permitted",       TCPOPT_SACK_PERMITTED},
+	{"sack",           "Selective ACK",        TCPOPT_SACK},
+	{"timestamp",      "Timestamp",            TCPOPT_TIMESTAMP},
+	{"md5",            "MD5 signature",        TCPOPT_MD5SIG},
+	{NULL},
+};
+
+static void tcpoptstrip_tg_help(void)
+{
+	const struct tcp_optionmap *w;
+
+	printf(
+"TCPOPTSTRIP target options:\n"
+"  --strip-options value     strip specified TCP options denoted by value\n"
+"                            (separated by comma) from TCP header\n"
+"  Instead of the numeric value, you can also use the following names:\n"
+	);
+
+	for (w = tcp_optionmap; w->name != NULL; ++w)
+		printf("    %-14s    strip \"%s\" option\n", w->name, w->desc);
+}
+
+static void
+parse_list(struct xt_tcpoptstrip_target_info *info, const char *arg)
+{
+	unsigned int option;
+	char *p;
+	int i;
+
+	while (true) {
+		p = strchr(arg, ',');
+		if (p != NULL)
+			*p = '\0';
+
+		option = 0;
+		for (i = 0; tcp_optionmap[i].name != NULL; ++i)
+			if (strcmp(tcp_optionmap[i].name, arg) == 0) {
+				option = tcp_optionmap[i].option;
+				break;
+			}
+
+		if (option == 0 &&
+		    !xtables_strtoui(arg, NULL, &option, 0, UINT8_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+			           "Bad TCP option value \"%s\"", arg);
+
+		if (option < 2)
+			xtables_error(PARAMETER_PROBLEM,
+			           "Option value may not be 0 or 1");
+
+		if (tcpoptstrip_test_bit(info->strip_bmap, option))
+			xtables_error(PARAMETER_PROBLEM,
+			           "Option \"%s\" already specified", arg);
+
+		tcpoptstrip_set_bit(info->strip_bmap, option);
+		if (p == NULL)
+			break;
+		arg = p + 1;
+	}
+}
+
+static void tcpoptstrip_tg_parse(struct xt_option_call *cb)
+{
+	struct xt_tcpoptstrip_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	parse_list(info, cb->arg);
+}
+
+static void
+tcpoptstrip_print_list(const struct xt_tcpoptstrip_target_info *info,
+                       bool numeric)
+{
+	unsigned int i, j;
+	const char *name;
+	bool first = true;
+
+	for (i = 0; i < 256; ++i) {
+		if (!tcpoptstrip_test_bit(info->strip_bmap, i))
+			continue;
+		if (!first)
+			printf(",");
+
+		first = false;
+		name  = NULL;
+		if (!numeric)
+			for (j = 0; tcp_optionmap[j].name != NULL; ++j)
+				if (tcp_optionmap[j].option == i)
+					name = tcp_optionmap[j].name;
+
+		if (name != NULL)
+			printf("%s", name);
+		else
+			printf("%u", i);
+	}
+}
+
+static void
+tcpoptstrip_tg_print(const void *ip, const struct xt_entry_target *target,
+                     int numeric)
+{
+	const struct xt_tcpoptstrip_target_info *info =
+		(const void *)target->data;
+
+	printf(" TCPOPTSTRIP options ");
+	tcpoptstrip_print_list(info, numeric);
+}
+
+static void
+tcpoptstrip_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tcpoptstrip_target_info *info =
+		(const void *)target->data;
+
+	printf(" --strip-options ");
+	tcpoptstrip_print_list(info, true);
+}
+
+static struct xtables_target tcpoptstrip_tg_reg = {
+	.version       = XTABLES_VERSION,
+	.name          = "TCPOPTSTRIP",
+	.family        = NFPROTO_UNSPEC,
+	.size          = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)),
+	.help          = tcpoptstrip_tg_help,
+	.print         = tcpoptstrip_tg_print,
+	.save          = tcpoptstrip_tg_save,
+	.x6_parse      = tcpoptstrip_tg_parse,
+	.x6_options    = tcpoptstrip_tg_opts,
+};
+
+void _init(void)
+{
+	xtables_register_target(&tcpoptstrip_tg_reg);
+}
diff --git a/extensions/libxt_TCPOPTSTRIP.man b/extensions/libxt_TCPOPTSTRIP.man
new file mode 100644
index 0000000..2a07709
--- /dev/null
+++ b/extensions/libxt_TCPOPTSTRIP.man
@@ -0,0 +1,7 @@
+This target will strip TCP options off a TCP packet. (It will actually replace
+them by NO-OPs.) As such, you will need to add the \fB\-p tcp\fP parameters.
+.TP
+\fB\-\-strip\-options\fP \fIoption\fP[\fB,\fP\fIoption\fP...]
+Strip the given option(s). The options may be specified by TCP option number or
+by symbolic name. The list of recognized options can be obtained by calling
+iptables with \fB\-j TCPOPTSTRIP \-h\fP.
diff --git a/extensions/libxt_TEE.c b/extensions/libxt_TEE.c
new file mode 100644
index 0000000..92c7601
--- /dev/null
+++ b/extensions/libxt_TEE.c
@@ -0,0 +1,127 @@
+/*
+ *	"TEE" target extension for iptables
+ *	Copyright © Sebastian Claßen <sebastian.classen [at] freenet.ag>, 2007
+ *	Jan Engelhardt <jengelh [at] medozas de>, 2007 - 2010
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License; either
+ *	version 2 of the License, or any later version, as published by the
+ *	Free Software Foundation.
+ */
+#include <sys/socket.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <xtables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_TEE.h>
+
+enum {
+	O_GATEWAY = 0,
+	O_OIF,
+};
+
+#define s struct xt_tee_tginfo
+static const struct xt_option_entry tee_tg_opts[] = {
+	{.name = "gateway", .id = O_GATEWAY, .type = XTTYPE_HOST,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, gw)},
+	{.name = "oif", .id = O_OIF, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, oif)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void tee_tg_help(void)
+{
+	printf(
+"TEE target options:\n"
+"  --gateway IPADDR    Route packet via the gateway given by address\n"
+"  --oif NAME          Include oif in route calculation\n"
+"\n");
+}
+
+static void tee_tg_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct xt_tee_tginfo *info = (const void *)target->data;
+
+	if (numeric)
+		printf(" TEE gw:%s", xtables_ipaddr_to_numeric(&info->gw.in));
+	else
+		printf(" TEE gw:%s", xtables_ipaddr_to_anyname(&info->gw.in));
+	if (*info->oif != '\0')
+		printf(" oif=%s", info->oif);
+}
+
+static void tee_tg6_print(const void *ip, const struct xt_entry_target *target,
+                          int numeric)
+{
+	const struct xt_tee_tginfo *info = (const void *)target->data;
+
+	if (numeric)
+		printf(" TEE gw:%s", xtables_ip6addr_to_numeric(&info->gw.in6));
+	else
+		printf(" TEE gw:%s", xtables_ip6addr_to_anyname(&info->gw.in6));
+	if (*info->oif != '\0')
+		printf(" oif=%s", info->oif);
+}
+
+static void tee_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tee_tginfo *info = (const void *)target->data;
+
+	printf(" --gateway %s", xtables_ipaddr_to_numeric(&info->gw.in));
+	if (*info->oif != '\0')
+		printf(" --oif %s", info->oif);
+}
+
+static void tee_tg6_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tee_tginfo *info = (const void *)target->data;
+
+	printf(" --gateway %s", xtables_ip6addr_to_numeric(&info->gw.in6));
+	if (*info->oif != '\0')
+		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,
+	},
+	{
+		.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_targets(tee_tg_reg, ARRAY_SIZE(tee_tg_reg));
+}
diff --git a/extensions/libxt_TEE.man b/extensions/libxt_TEE.man
new file mode 100644
index 0000000..456d150
--- /dev/null
+++ b/extensions/libxt_TEE.man
@@ -0,0 +1,12 @@
+The \fBTEE\fP target will clone a packet and redirect this clone to another
+machine on the \fBlocal\fP network segment. In other words, the nexthop
+must be the target, or you will have to configure the nexthop to forward it
+further if so desired.
+.TP
+\fB\-\-gateway\fP \fIipaddr\fP
+Send the cloned packet to the host reachable at the given IP address.
+Use of 0.0.0.0 (for IPv4 packets) or :: (IPv6) is invalid.
+.PP
+To forward all incoming traffic on eth0 to an Network Layer logging box:
+.PP
+\-t mangle \-A PREROUTING \-i eth0 \-j TEE \-\-gateway 2001:db8::1
diff --git a/extensions/libxt_TOS.c b/extensions/libxt_TOS.c
new file mode 100644
index 0000000..cef5876
--- /dev/null
+++ b/extensions/libxt_TOS.c
@@ -0,0 +1,220 @@
+/*
+ * Shared library add-on to iptables to add TOS target support
+ *
+ * Copyright © CC Computer Consultants GmbH, 2007
+ * Contact: Jan Engelhardt <jengelh@medozas.de>
+ */
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include <xtables.h>
+#include <linux/netfilter/xt_DSCP.h>
+#include "tos_values.c"
+
+struct ipt_tos_target_info {
+	uint8_t tos;
+};
+
+enum {
+	O_SET_TOS = 0,
+	O_AND_TOS,
+	O_OR_TOS,
+	O_XOR_TOS,
+	F_SET_TOS = 1 << O_SET_TOS,
+	F_AND_TOS = 1 << O_AND_TOS,
+	F_OR_TOS  = 1 << O_OR_TOS,
+	F_XOR_TOS = 1 << O_XOR_TOS,
+	F_ANY     = F_SET_TOS | F_AND_TOS | F_OR_TOS | F_XOR_TOS,
+};
+
+static const struct xt_option_entry tos_tg_opts_v0[] = {
+	{.name = "set-tos", .id = O_SET_TOS, .type = XTTYPE_TOSMASK,
+	 .excl = F_ANY, .max = 0xFF},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry tos_tg_opts[] = {
+	{.name = "set-tos", .id = O_SET_TOS, .type = XTTYPE_TOSMASK,
+	 .excl = F_ANY, .max = 0x3F},
+	{.name = "and-tos", .id = O_AND_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	{.name = "or-tos", .id = O_OR_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	{.name = "xor-tos", .id = O_XOR_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	XTOPT_TABLEEND,
+};
+
+static void tos_tg_help_v0(void)
+{
+	const struct tos_symbol_info *symbol;
+
+	printf(
+"TOS target options:\n"
+"  --set-tos value     Set Type of Service/Priority field to value\n"
+"  --set-tos symbol    Set TOS field (IPv4 only) by symbol\n"
+"                      Accepted symbolic names for value are:\n");
+
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		printf("                        (0x%02x) %2u %s\n",
+		       symbol->value, symbol->value, symbol->name);
+
+	printf("\n");
+}
+
+static void tos_tg_help(void)
+{
+	const struct tos_symbol_info *symbol;
+
+	printf(
+"TOS target v%s options:\n"
+"  --set-tos value[/mask]  Set Type of Service/Priority field to value\n"
+"                          (Zero out bits in mask and XOR value into TOS)\n"
+"  --set-tos symbol        Set TOS field (IPv4 only) by symbol\n"
+"                          (this zeroes the 4-bit Precedence part!)\n"
+"                          Accepted symbolic names for value are:\n",
+XTABLES_VERSION);
+
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		printf("                            (0x%02x) %2u %s\n",
+		       symbol->value, symbol->value, symbol->name);
+
+	printf(
+"\n"
+"  --and-tos bits          Binary AND the TOS value with bits\n"
+"  --or-tos  bits          Binary OR the TOS value with bits\n"
+"  --xor-tos bits          Binary XOR the TOS value with bits\n"
+);
+}
+
+static void tos_tg_parse_v0(struct xt_option_call *cb)
+{
+	struct ipt_tos_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->val.tos_mask != 0xFF)
+		xtables_error(PARAMETER_PROBLEM, "tos match: Your kernel "
+		           "is too old to support anything besides "
+			   "/0xFF as a mask.");
+	info->tos = cb->val.tos_value;
+}
+
+static void tos_tg_parse(struct xt_option_call *cb)
+{
+	struct xt_tos_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_TOS:
+		info->tos_value = cb->val.tos_value;
+		info->tos_mask  = cb->val.tos_mask;
+		break;
+	case O_AND_TOS:
+		info->tos_value = 0;
+		info->tos_mask  = ~cb->val.u8;
+		break;
+	case O_OR_TOS:
+		info->tos_value = cb->val.u8;
+		info->tos_mask  = cb->val.u8;
+		break;
+	case O_XOR_TOS:
+		info->tos_value = cb->val.u8;
+		info->tos_mask  = 0;
+		break;
+	}
+}
+
+static void tos_tg_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+		           "TOS: An action is required");
+}
+
+static void tos_tg_print_v0(const void *ip,
+                            const struct xt_entry_target *target, int numeric)
+{
+	const struct ipt_tos_target_info *info = (const void *)target->data;
+
+	printf(" TOS set ");
+	if (numeric || !tos_try_print_symbolic("", info->tos, 0xFF))
+		printf("0x%02x", info->tos);
+}
+
+static void tos_tg_print(const void *ip, const struct xt_entry_target *target,
+                         int numeric)
+{
+	const struct xt_tos_target_info *info = (const void *)target->data;
+
+	if (numeric)
+		printf(" TOS set 0x%02x/0x%02x",
+		       info->tos_value, info->tos_mask);
+	else if (tos_try_print_symbolic(" TOS set",
+	    info->tos_value, info->tos_mask))
+		/* already printed by call */
+		return;
+	else if (info->tos_value == 0)
+		printf(" TOS and 0x%02x",
+		       (unsigned int)(uint8_t)~info->tos_mask);
+	else if (info->tos_value == info->tos_mask)
+		printf(" TOS or 0x%02x", info->tos_value);
+	else if (info->tos_mask == 0)
+		printf(" TOS xor 0x%02x", info->tos_value);
+	else
+		printf(" TOS set 0x%02x/0x%02x",
+		       info->tos_value, info->tos_mask);
+}
+
+static void tos_tg_save_v0(const void *ip, const struct xt_entry_target *target)
+{
+	const struct ipt_tos_target_info *info = (const void *)target->data;
+
+	printf(" --set-tos 0x%02x", info->tos);
+}
+
+static void tos_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tos_target_info *info = (const void *)target->data;
+
+	printf(" --set-tos 0x%02x/0x%02x", info->tos_value, info->tos_mask);
+}
+
+static struct xtables_target tos_tg_reg[] = {
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "TOS",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_tos_target_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)),
+		.help          = tos_tg_help_v0,
+		.print         = tos_tg_print_v0,
+		.save          = tos_tg_save_v0,
+		.x6_parse      = tos_tg_parse_v0,
+		.x6_fcheck     = tos_tg_check,
+		.x6_options    = tos_tg_opts_v0,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "TOS",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_tos_target_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)),
+		.help          = tos_tg_help,
+		.print         = tos_tg_print,
+		.save          = tos_tg_save,
+		.x6_parse      = tos_tg_parse,
+		.x6_fcheck     = tos_tg_check,
+		.x6_options    = tos_tg_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(tos_tg_reg, ARRAY_SIZE(tos_tg_reg));
+}
diff --git a/extensions/libxt_TOS.man b/extensions/libxt_TOS.man
new file mode 100644
index 0000000..58118ec
--- /dev/null
+++ b/extensions/libxt_TOS.man
@@ -0,0 +1,36 @@
+This module sets the Type of Service field in the IPv4 header (including the
+"precedence" bits) or the Priority field in the IPv6 header. Note that TOS
+shares the same bits as DSCP and ECN. The TOS target is only valid in the
+\fBmangle\fP table.
+.TP
+\fB\-\-set\-tos\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+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 (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.
+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. 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. 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_TPROXY.c b/extensions/libxt_TPROXY.c
new file mode 100644
index 0000000..d13ec85
--- /dev/null
+++ b/extensions/libxt_TPROXY.c
@@ -0,0 +1,195 @@
+/*
+ * shared library add-on to iptables to add TPROXY target support.
+ *
+ * Copyright (C) 2002-2008 BalaBit IT Ltd.
+ */
+#include <stdio.h>
+#include <limits.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_TPROXY.h>
+#include <arpa/inet.h>
+
+enum {
+	P_PORT = 0,
+	P_ADDR,
+	P_MARK,
+	F_PORT = 1 << P_PORT,
+	F_ADDR = 1 << P_ADDR,
+	F_MARK = 1 << P_MARK,
+};
+
+#define s struct xt_tproxy_target_info
+static const struct xt_option_entry tproxy_tg0_opts[] = {
+	{.name = "on-port", .id = P_PORT, .type = XTTYPE_PORT,
+	 .flags = XTOPT_MAND | XTOPT_NBO | XTOPT_PUT, XTOPT_POINTER(s, lport)},
+	{.name = "on-ip", .id = P_ADDR, .type = XTTYPE_HOST},
+	{.name = "tproxy-mark", .id = P_MARK, .type = XTTYPE_MARKMASK32},
+	XTOPT_TABLEEND,
+};
+#undef s
+#define s struct xt_tproxy_target_info_v1
+static const struct xt_option_entry tproxy_tg1_opts[] = {
+	{.name = "on-port", .id = P_PORT, .type = XTTYPE_PORT,
+	 .flags = XTOPT_MAND | XTOPT_NBO | XTOPT_PUT, XTOPT_POINTER(s, lport)},
+	{.name = "on-ip", .id = P_ADDR, .type = XTTYPE_HOST,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, laddr)},
+	{.name = "tproxy-mark", .id = P_MARK, .type = XTTYPE_MARKMASK32},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void tproxy_tg_help(void)
+{
+	printf(
+"TPROXY target options:\n"
+"  --on-port port		    Redirect connection to port, or the original port if 0\n"
+"  --on-ip ip			    Optionally redirect to the given IP\n"
+"  --tproxy-mark value[/mask]	    Mark packets with the given value/mask\n\n");
+}
+
+static void tproxy_tg_print(const void *ip, const struct xt_entry_target *target,
+			 int numeric)
+{
+	const struct xt_tproxy_target_info *info = (const void *)target->data;
+	printf(" TPROXY redirect %s:%u mark 0x%x/0x%x",
+	       xtables_ipaddr_to_numeric((const struct in_addr *)&info->laddr),
+	       ntohs(info->lport), (unsigned int)info->mark_value,
+	       (unsigned int)info->mark_mask);
+}
+
+static void
+tproxy_tg_print4(const void *ip, const struct xt_entry_target *target,
+		 int numeric)
+{
+	const struct xt_tproxy_target_info_v1 *info =
+		(const void *)target->data;
+
+	printf(" TPROXY redirect %s:%u mark 0x%x/0x%x",
+	       xtables_ipaddr_to_numeric(&info->laddr.in),
+	       ntohs(info->lport), (unsigned int)info->mark_value,
+	       (unsigned int)info->mark_mask);
+}
+
+static void
+tproxy_tg_print6(const void *ip, const struct xt_entry_target *target,
+		 int numeric)
+{
+	const struct xt_tproxy_target_info_v1 *info =
+		(const void *)target->data;
+
+	printf(" TPROXY redirect %s:%u mark 0x%x/0x%x",
+	       xtables_ip6addr_to_numeric(&info->laddr.in6),
+	       ntohs(info->lport), (unsigned int)info->mark_value,
+	       (unsigned int)info->mark_mask);
+}
+
+static void tproxy_tg_save(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tproxy_target_info *info = (const void *)target->data;
+
+	printf(" --on-port %u", ntohs(info->lport));
+	printf(" --on-ip %s",
+	       xtables_ipaddr_to_numeric((const struct in_addr *)&info->laddr));
+	printf(" --tproxy-mark 0x%x/0x%x",
+	       (unsigned int)info->mark_value, (unsigned int)info->mark_mask);
+}
+
+static void
+tproxy_tg_save4(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tproxy_target_info_v1 *info;
+
+	info = (const void *)target->data;
+	printf(" --on-port %u", ntohs(info->lport));
+	printf(" --on-ip %s", xtables_ipaddr_to_numeric(&info->laddr.in));
+	printf(" --tproxy-mark 0x%x/0x%x",
+	       (unsigned int)info->mark_value, (unsigned int)info->mark_mask);
+}
+
+static void
+tproxy_tg_save6(const void *ip, const struct xt_entry_target *target)
+{
+	const struct xt_tproxy_target_info_v1 *info;
+
+	info = (const void *)target->data;
+	printf(" --on-port %u", ntohs(info->lport));
+	printf(" --on-ip %s", xtables_ip6addr_to_numeric(&info->laddr.in6));
+	printf(" --tproxy-mark 0x%x/0x%x",
+	       (unsigned int)info->mark_value, (unsigned int)info->mark_mask);
+}
+
+static void tproxy_tg0_parse(struct xt_option_call *cb)
+{
+	struct xt_tproxy_target_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case P_MARK:
+		info->mark_value = cb->val.mark;
+		info->mark_mask  = cb->val.mask;
+		break;
+	case P_ADDR:
+		info->laddr = cb->val.haddr.ip;
+		break;
+	}
+}
+
+static void tproxy_tg1_parse(struct xt_option_call *cb)
+{
+	struct xt_tproxy_target_info_v1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case P_MARK:
+		info->mark_value = cb->val.mark;
+		info->mark_mask  = cb->val.mask;
+		break;
+	}
+}
+
+static struct xtables_target tproxy_tg_reg[] = {
+	{
+		.name          = "TPROXY",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
+		.help          = tproxy_tg_help,
+		.print         = tproxy_tg_print,
+		.save          = tproxy_tg_save,
+		.x6_options    = tproxy_tg0_opts,
+		.x6_parse      = tproxy_tg0_parse,
+	},
+	{
+		.name          = "TPROXY",
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+		.help          = tproxy_tg_help,
+		.print         = tproxy_tg_print4,
+		.save          = tproxy_tg_save4,
+		.x6_options    = tproxy_tg1_opts,
+		.x6_parse      = tproxy_tg1_parse,
+	},
+	{
+		.name          = "TPROXY",
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+		.help          = tproxy_tg_help,
+		.print         = tproxy_tg_print6,
+		.save          = tproxy_tg_save6,
+		.x6_options    = tproxy_tg1_opts,
+		.x6_parse      = tproxy_tg1_parse,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
+}
diff --git a/extensions/libxt_TPROXY.man b/extensions/libxt_TPROXY.man
new file mode 100644
index 0000000..2f7d82d
--- /dev/null
+++ b/extensions/libxt_TPROXY.man
@@ -0,0 +1,21 @@
+This target is only valid in the \fBmangle\fP table, in the \fBPREROUTING\fP
+chain and user-defined chains which are only called from this chain. It
+redirects the packet to a local socket without changing the packet header in
+any way. It can also change the mark value which can then be used in advanced
+routing rules.
+It takes three options:
+.TP
+\fB\-\-on\-port\fP \fIport\fP
+This specifies a destination port to use. It is a required option, 0 means the
+new destination port is the same as the original. This is only valid if the
+rule also specifies \fB\-p tcp\fP or \fB\-p udp\fP.
+.TP
+\fB\-\-on\-ip\fP \fIaddress\fP
+This specifies a destination address to use. By default the address is the IP
+address of the incoming interface. This is only valid if the rule also
+specifies \fB\-p tcp\fP or \fB\-p udp\fP.
+.TP
+\fB\-\-tproxy\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Marks packets with the given value/mask. The fwmark value set here can be used
+by advanced routing. (Required for transparent proxying to work: otherwise
+these packets will get forwarded, which is probably not what you want.)
diff --git a/extensions/libxt_TRACE.c b/extensions/libxt_TRACE.c
new file mode 100644
index 0000000..0282e6f
--- /dev/null
+++ b/extensions/libxt_TRACE.c
@@ -0,0 +1,21 @@
+/* Shared library add-on to iptables to add TRACE target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+
+static struct xtables_target trace_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "TRACE",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(0),
+	.userspacesize	= XT_ALIGN(0),
+};
+
+void _init(void)
+{
+	xtables_register_target(&trace_target);
+}
diff --git a/extensions/libxt_TRACE.man b/extensions/libxt_TRACE.man
new file mode 100644
index 0000000..8d590a5
--- /dev/null
+++ b/extensions/libxt_TRACE.man
@@ -0,0 +1,13 @@
+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
+to be visible.
+The packets are logged with the string prefix:
+"TRACE: tablename:chainname:type:rulenum " where type can be "rule" for 
+plain rule, "return" for implicit rule at the end of a user defined chain 
+and "policy" for the policy of the built in chains. 
+.br
+It can only be used in the
+.BR raw
+table.
diff --git a/extensions/libxt_addrtype.c b/extensions/libxt_addrtype.c
new file mode 100644
index 0000000..e5d3033
--- /dev/null
+++ b/extensions/libxt_addrtype.c
@@ -0,0 +1,302 @@
+/* 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/xt_addrtype.h>
+
+enum {
+	O_SRC_TYPE = 0,
+	O_DST_TYPE,
+	O_LIMIT_IFACE_IN,
+	O_LIMIT_IFACE_OUT,
+	F_SRC_TYPE        = 1 << O_SRC_TYPE,
+	F_DST_TYPE        = 1 << O_DST_TYPE,
+	F_LIMIT_IFACE_IN  = 1 << O_LIMIT_IFACE_IN,
+	F_LIMIT_IFACE_OUT = 1 << O_LIMIT_IFACE_OUT,
+};
+
+/* from linux/rtnetlink.h, must match order of enumeration */
+static const char *const rtn_names[] = {
+	"UNSPEC",
+	"UNICAST",
+	"LOCAL",
+	"BROADCAST",
+	"ANYCAST",
+	"MULTICAST",
+	"BLACKHOLE",
+	"UNREACHABLE",
+	"PROHIBIT",
+	"THROW",
+	"NAT",
+	"XRESOLVE",
+	NULL
+};
+
+static void addrtype_help_types(void)
+{
+	int i;
+
+	for (i = 0; rtn_names[i]; i++)
+		printf("                                %s\n", rtn_names[i]);
+}
+
+static void addrtype_help_v0(void)
+{
+	printf(
+"Address type match options:\n"
+" [!] --src-type type[,...]      Match source address type\n"
+" [!] --dst-type type[,...]      Match destination address type\n"
+"\n"
+"Valid types:           \n");
+	addrtype_help_types();
+}
+
+static void addrtype_help_v1(void)
+{
+	printf(
+"Address type match options:\n"
+" [!] --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 outgoing device\n"
+"\n"
+"Valid types:           \n");
+	addrtype_help_types();
+}
+
+static int
+parse_type(const char *name, size_t len, uint16_t *mask)
+{
+	int i;
+
+	for (i = 0; rtn_names[i]; i++)
+		if (strncasecmp(name, rtn_names[i], len) == 0) {
+			/* build up bitmask for kernel module */
+			*mask |= (1 << i);
+			return 1;
+		}
+
+	return 0;
+}
+
+static void parse_types(const char *arg, uint16_t *mask)
+{
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg || !parse_type(arg, comma-arg, mask))
+			xtables_error(PARAMETER_PROBLEM,
+			           "addrtype: bad type `%s'", arg);
+		arg = comma + 1;
+	}
+
+	if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask))
+		xtables_error(PARAMETER_PROBLEM, "addrtype: bad type \"%s\"", arg);
+}
+	
+static void addrtype_parse_v0(struct xt_option_call *cb)
+{
+	struct xt_addrtype_info *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->invert_source = 1;
+		break;
+	case O_DST_TYPE:
+		parse_types(cb->arg, &info->dest);
+		if (cb->invert)
+			info->invert_dest = 1;
+		break;
+	}
+}
+
+static void addrtype_parse_v1(struct xt_option_call *cb)
+{
+	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 |= XT_ADDRTYPE_INVERT_SOURCE;
+		break;
+	case O_DST_TYPE:
+		parse_types(cb->arg, &info->dest);
+		if (cb->invert)
+			info->flags |= XT_ADDRTYPE_INVERT_DEST;
+		break;
+	case O_LIMIT_IFACE_IN:
+		info->flags |= XT_ADDRTYPE_LIMIT_IFACE_IN;
+		break;
+	case O_LIMIT_IFACE_OUT:
+		info->flags |= XT_ADDRTYPE_LIMIT_IFACE_OUT;
+		break;
+	}
+}
+
+static void addrtype_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & (F_SRC_TYPE | F_DST_TYPE)))
+		xtables_error(PARAMETER_PROBLEM,
+			   "addrtype: you must specify --src-type or --dst-type");
+}
+
+static void print_types(uint16_t mask)
+{
+	const char *sep = "";
+	int i;
+
+	for (i = 0; rtn_names[i]; i++)
+		if (mask & (1 << i)) {
+			printf("%s%s", sep, rtn_names[i]);
+			sep = ",";
+		}
+}
+
+static void addrtype_print_v0(const void *ip, const struct xt_entry_match *match,
+                              int numeric)
+{
+	const struct xt_addrtype_info *info = (const void *)match->data;
+
+	printf(" ADDRTYPE match");
+	if (info->source) {
+		printf(" src-type ");
+		if (info->invert_source)
+			printf("!");
+		print_types(info->source);
+	}
+	if (info->dest) {
+		printf(" dst-type");
+		if (info->invert_dest)
+			printf("!");
+		print_types(info->dest);
+	}
+}
+
+static void addrtype_print_v1(const void *ip, const struct xt_entry_match *match,
+                              int numeric)
+{
+	const struct xt_addrtype_info_v1 *info = (const void *)match->data;
+
+	printf(" ADDRTYPE match");
+	if (info->source) {
+		printf(" src-type ");
+		if (info->flags & XT_ADDRTYPE_INVERT_SOURCE)
+			printf("!");
+		print_types(info->source);
+	}
+	if (info->dest) {
+		printf(" dst-type ");
+		if (info->flags & XT_ADDRTYPE_INVERT_DEST)
+			printf("!");
+		print_types(info->dest);
+	}
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN)
+		printf(" limit-in");
+	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 xt_addrtype_info *info = (const void *)match->data;
+
+	if (info->source) {
+		if (info->invert_source)
+			printf(" !");
+		printf(" --src-type ");
+		print_types(info->source);
+	}
+	if (info->dest) {
+		if (info->invert_dest)
+			printf(" !");
+		printf(" --dst-type ");
+		print_types(info->dest);
+	}
+}
+
+static void addrtype_save_v1(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_addrtype_info_v1 *info = (const void *)match->data;
+
+	if (info->source) {
+		if (info->flags & XT_ADDRTYPE_INVERT_SOURCE)
+			printf(" !");
+		printf(" --src-type ");
+		print_types(info->source);
+	}
+	if (info->dest) {
+		if (info->flags & XT_ADDRTYPE_INVERT_DEST)
+			printf(" !");
+		printf(" --dst-type ");
+		print_types(info->dest);
+	}
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN)
+		printf(" --limit-iface-in");
+	if (info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
+		printf(" --limit-iface-out");
+}
+
+static const struct xt_option_entry addrtype_opts_v0[] = {
+	{.name = "src-type", .id = O_SRC_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "dst-type", .id = O_DST_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry addrtype_opts_v1[] = {
+	{.name = "src-type", .id = O_SRC_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "dst-type", .id = O_DST_TYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "limit-iface-in", .id = O_LIMIT_IFACE_IN,
+	 .type = XTTYPE_NONE, .excl = F_LIMIT_IFACE_OUT},
+	{.name = "limit-iface-out", .id = O_LIMIT_IFACE_OUT,
+	 .type = XTTYPE_NONE, .excl = F_LIMIT_IFACE_IN},
+	XTOPT_TABLEEND,
+};
+
+static struct xtables_match addrtype_mt_reg[] = {
+	{
+		.name          = "addrtype",
+		.version       = XTABLES_VERSION,
+		.family        = NFPROTO_IPV4,
+		.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,
+		.x6_parse      = addrtype_parse_v0,
+		.x6_fcheck     = addrtype_check,
+		.x6_options    = addrtype_opts_v0,
+	},
+	{
+		.name          = "addrtype",
+		.revision      = 1,
+		.version       = XTABLES_VERSION,
+		.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,
+		.x6_parse      = addrtype_parse_v1,
+		.x6_fcheck     = addrtype_check,
+		.x6_options    = addrtype_opts_v1,
+	},
+};
+
+
+void _init(void) 
+{
+	xtables_register_matches(addrtype_mt_reg, ARRAY_SIZE(addrtype_mt_reg));
+}
diff --git a/extensions/libxt_addrtype.man b/extensions/libxt_addrtype.man
new file mode 100644
index 0000000..16fd9df
--- /dev/null
+++ b/extensions/libxt_addrtype.man
@@ -0,0 +1,69 @@
+This module matches packets based on their 
+.B address type.
+Address types are used within the kernel networking stack and categorize
+addresses into various groups.  The exact definition of that group depends on the specific layer three protocol.
+.PP
+The following address types are possible:
+.TP
+.BI "UNSPEC"
+an unspecified address (i.e. 0.0.0.0)
+.TP
+.BI "UNICAST"
+an unicast address
+.TP
+.BI "LOCAL"
+a local address
+.TP
+.BI "BROADCAST"
+a broadcast address
+.TP
+.BI "ANYCAST"
+an anycast packet
+.TP
+.BI "MULTICAST"
+a multicast address
+.TP
+.BI "BLACKHOLE"
+a blackhole address
+.TP
+.BI "UNREACHABLE"
+an unreachable address
+.TP
+.BI "PROHIBIT"
+a prohibited address
+.TP
+.BI "THROW"
+FIXME
+.TP
+.BI "NAT"
+FIXME
+.TP
+.BI "XRESOLVE"
+.TP
+[\fB!\fP] \fB\-\-src\-type\fP \fItype\fP
+Matches if the source address is of given type
+.TP
+[\fB!\fP] \fB\-\-dst\-type\fP \fItype\fP
+Matches if the destination address is of given type
+.TP
+.BI "\-\-limit\-iface\-in"
+The address type checking can be limited to the interface the packet is coming
+in. This option is only valid in the
+.BR PREROUTING ,
+.B INPUT
+and
+.B FORWARD
+chains. It cannot be specified with the
+\fB\-\-limit\-iface\-out\fP
+option.
+.TP
+\fB\-\-limit\-iface\-out\fP
+The address type checking can be limited to the interface the packet is going
+out. This option is only valid in the
+.BR POSTROUTING ,
+.B OUTPUT
+and
+.B FORWARD
+chains. It cannot be specified with the
+\fB\-\-limit\-iface\-in\fP
+option.
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_cluster.c b/extensions/libxt_cluster.c
new file mode 100644
index 0000000..3adff12
--- /dev/null
+++ b/extensions/libxt_cluster.c
@@ -0,0 +1,146 @@
+/*
+ * (C) 2009 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.
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_cluster.h>
+
+static void
+cluster_help(void)
+{
+	printf(
+"cluster match options:\n"
+"  --cluster-total-nodes <num>		Set number of total nodes in cluster\n"
+"  [!] --cluster-local-node <num>	Set the local node number\n"
+"  [!] --cluster-local-nodemask <num>	Set the local node mask\n"
+"  --cluster-hash-seed <num>		Set seed value of the Jenkins hash\n");
+}
+
+enum {
+	O_CL_TOTAL_NODES = 0,
+	O_CL_LOCAL_NODE,
+	O_CL_LOCAL_NODEMASK,
+	O_CL_HASH_SEED,
+	F_CL_TOTAL_NODES    = 1 << O_CL_TOTAL_NODES,
+	F_CL_LOCAL_NODE     = 1 << O_CL_LOCAL_NODE,
+	F_CL_LOCAL_NODEMASK = 1 << O_CL_LOCAL_NODEMASK,
+	F_CL_HASH_SEED      = 1 << O_CL_HASH_SEED,
+};
+
+#define s struct xt_cluster_match_info
+static const struct xt_option_entry cluster_opts[] = {
+	{.name = "cluster-total-nodes", .id = O_CL_TOTAL_NODES,
+	 .type = XTTYPE_UINT32, .min = 1, .max = XT_CLUSTER_NODES_MAX,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, total_nodes)},
+	{.name = "cluster-local-node", .id = O_CL_LOCAL_NODE,
+	 .excl = F_CL_LOCAL_NODEMASK, .flags = XTOPT_INVERT,
+	 .type = XTTYPE_UINT32, .min = 1, .max = XT_CLUSTER_NODES_MAX},
+	{.name = "cluster-local-nodemask", .id = O_CL_LOCAL_NODEMASK,
+	 .excl = F_CL_LOCAL_NODE, .type = XTTYPE_UINT32,
+	 .min = 1, .max = XT_CLUSTER_NODES_MAX,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, node_mask)},
+	{.name = "cluster-hash-seed", .id = O_CL_HASH_SEED,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_MAND | XTOPT_PUT,
+	 XTOPT_POINTER(s, hash_seed)},
+	XTOPT_TABLEEND,
+};
+
+static void cluster_parse(struct xt_option_call *cb)
+{
+	struct xt_cluster_match_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_CL_LOCAL_NODE:
+		if (cb->invert)
+			info->flags |= XT_CLUSTER_F_INV;
+		info->node_mask = 1 << (cb->val.u32 - 1);
+		break;
+	case O_CL_LOCAL_NODEMASK:
+		if (cb->invert)
+			info->flags |= XT_CLUSTER_F_INV;
+		break;
+	}
+}
+
+static void cluster_check(struct xt_fcheck_call *cb)
+{
+	const struct xt_cluster_match_info *info = cb->data;
+	unsigned int test;
+
+	test = F_CL_TOTAL_NODES | F_CL_LOCAL_NODE | F_CL_HASH_SEED;
+	if ((cb->xflags & test) == test) {
+		if (info->node_mask >= (1ULL << info->total_nodes))
+			xtables_error(PARAMETER_PROBLEM,
+				      "cluster match: "
+				      "`--cluster-local-node' "
+				      "must be <= `--cluster-total-nodes'");
+		return;
+	}
+
+	test = F_CL_TOTAL_NODES | F_CL_LOCAL_NODEMASK | F_CL_HASH_SEED;
+	if ((cb->xflags & test) == test) {
+		if (info->node_mask >= (1ULL << info->total_nodes))
+			xtables_error(PARAMETER_PROBLEM,
+				      "cluster match: "
+				      "`--cluster-local-nodemask' too big "
+				      "for `--cluster-total-nodes'");
+		return;
+	}
+	if (!(cb->xflags & (F_CL_LOCAL_NODE | F_CL_LOCAL_NODEMASK)))
+		xtables_error(PARAMETER_PROBLEM,
+			      "cluster match: `--cluster-local-node' or"
+			      "`--cluster-local-nodemask' is missing");
+}
+
+static void
+cluster_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_cluster_match_info *info = (void *)match->data;
+
+	printf(" cluster ");
+	if (info->flags & XT_CLUSTER_F_INV)
+		printf("!node_mask=0x%08x", info->node_mask);
+	else
+		printf("node_mask=0x%08x", info->node_mask);
+
+	printf(" total_nodes=%u hash_seed=0x%08x",
+		info->total_nodes, info->hash_seed);
+}
+
+static void
+cluster_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_cluster_match_info *info = (void *)match->data;
+
+	if (info->flags & XT_CLUSTER_F_INV)
+		printf(" ! --cluster-local-nodemask 0x%08x", info->node_mask);
+	else
+		printf(" --cluster-local-nodemask 0x%08x", info->node_mask);
+
+	printf(" --cluster-total-nodes %u --cluster-hash-seed 0x%08x",
+		info->total_nodes, info->hash_seed);
+}
+
+static struct xtables_match cluster_mt_reg = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "cluster",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_cluster_match_info)),
+	.userspacesize  = XT_ALIGN(sizeof(struct xt_cluster_match_info)),
+ 	.help		= cluster_help,
+	.print		= cluster_print,
+	.save		= cluster_save,
+	.x6_parse	= cluster_parse,
+	.x6_fcheck	= cluster_check,
+	.x6_options	= cluster_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&cluster_mt_reg);
+}
diff --git a/extensions/libxt_cluster.man b/extensions/libxt_cluster.man
new file mode 100644
index 0000000..94b4b20
--- /dev/null
+++ b/extensions/libxt_cluster.man
@@ -0,0 +1,67 @@
+Allows you to deploy gateway and back-end load-sharing clusters without the
+need of load-balancers.
+.PP
+This match requires that all the nodes see the same packets. Thus, the cluster
+match decides if this node has to handle a packet given the following options:
+.TP
+\fB\-\-cluster\-total\-nodes\fP \fInum\fP
+Set number of total nodes in cluster.
+.TP
+[\fB!\fP] \fB\-\-cluster\-local\-node\fP \fInum\fP
+Set the local node number ID.
+.TP
+[\fB!\fP] \fB\-\-cluster\-local\-nodemask\fP \fImask\fP
+Set the local node number ID mask. You can use this option instead
+of \fB\-\-cluster\-local\-node\fP.
+.TP
+\fB\-\-cluster\-hash\-seed\fP \fIvalue\fP
+Set seed value of the Jenkins hash.
+.PP
+Example:
+.IP
+iptables \-A PREROUTING \-t mangle \-i eth1 \-m cluster
+\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1
+\-\-cluster\-hash\-seed 0xdeadbeef
+\-j MARK \-\-set-mark 0xffff
+.IP
+iptables \-A PREROUTING \-t mangle \-i eth2 \-m cluster
+\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1
+\-\-cluster\-hash\-seed 0xdeadbeef
+\-j MARK -\-set\-mark 0xffff
+.IP
+iptables \-A PREROUTING \-t mangle \-i eth1
+\-m mark ! \-\-mark 0xffff \-j DROP
+.IP
+iptables \-A PREROUTING \-t mangle \-i eth2
+\-m mark ! \-\-mark 0xffff \-j DROP
+.PP
+And the following commands to make all nodes see the same packets:
+.IP
+ip maddr add 01:00:5e:00:01:01 dev eth1
+.IP
+ip maddr add 01:00:5e:00:01:02 dev eth2
+.IP
+arptables \-A OUTPUT \-o eth1 \-\-h\-length 6
+\-j mangle \-\-mangle-mac-s 01:00:5e:00:01:01
+.IP
+arptables \-A INPUT \-i eth1 \-\-h-length 6
+\-\-destination-mac 01:00:5e:00:01:01
+\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27
+.IP
+arptables \-A OUTPUT \-o eth2 \-\-h\-length 6
+\-j mangle \-\-mangle\-mac\-s 01:00:5e:00:01:02
+.IP
+arptables \-A INPUT \-i eth2 \-\-h\-length 6
+\-\-destination\-mac 01:00:5e:00:01:02
+\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27
+.PP
+\fBNOTE\fP: the arptables commands above use mainstream syntax. If you
+are using arptables-jf included in some RedHat, CentOS and Fedora
+versions, you will hit syntax errors. Therefore, you'll have to adapt
+these to the arptables-jf syntax to get them working.
+.PP
+In the case of TCP connections, pickup facility has to be disabled
+to avoid marking TCP ACK packets coming in the reply direction as
+valid.
+.IP
+echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose
diff --git a/extensions/libxt_comment.c b/extensions/libxt_comment.c
new file mode 100644
index 0000000..6ed2ff9
--- /dev/null
+++ b/extensions/libxt_comment.c
@@ -0,0 +1,67 @@
+/* Shared library add-on to iptables to add comment match support.
+ *
+ * ChangeLog
+ *     2003-05-13: Brad Fisher <brad@info-link.net>
+ *         Initial comment match
+ *     2004-05-12: Brad Fisher <brad@info-link.net>
+ *         Port to patch-o-matic-ng
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_comment.h>
+
+enum {
+	O_COMMENT = 0,
+};
+
+static void comment_help(void)
+{
+	printf(
+		"comment match options:\n"
+		"--comment COMMENT             Attach a comment to a rule\n");
+}
+
+static const struct xt_option_entry comment_opts[] = {
+	{.name = "comment", .id = O_COMMENT, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_comment_info, comment)},
+	XTOPT_TABLEEND,
+};
+
+static void
+comment_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	struct xt_comment_info *commentinfo = (void *)match->data;
+
+	commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0';
+	printf(" /* %s */", commentinfo->comment);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+comment_save(const void *ip, const struct xt_entry_match *match)
+{
+	struct xt_comment_info *commentinfo = (void *)match->data;
+
+	commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0';
+	printf(" --comment");
+	xtables_save_string(commentinfo->comment);
+}
+
+static struct xtables_match comment_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "comment",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_comment_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_comment_info)),
+	.help		= comment_help,
+	.print 		= comment_print,
+	.save 		= comment_save,
+	.x6_parse	= xtables_option_parse,
+	.x6_options	= comment_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&comment_match);
+}
diff --git a/extensions/libxt_comment.man b/extensions/libxt_comment.man
new file mode 100644
index 0000000..faaee2a
--- /dev/null
+++ b/extensions/libxt_comment.man
@@ -0,0 +1,6 @@
+Allows you to add comments (up to 256 characters) to any rule.
+.TP
+\fB\-\-comment\fP \fIcomment\fP
+.TP
+Example:
+iptables \-A INPUT \-i eth1 \-m comment \-\-comment "my local LAN"
diff --git a/extensions/libxt_connbytes.c b/extensions/libxt_connbytes.c
new file mode 100644
index 0000000..ed2ad25
--- /dev/null
+++ b/extensions/libxt_connbytes.c
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_connbytes.h>
+
+enum {
+	O_CONNBYTES = 0,
+	O_CONNBYTES_DIR,
+	O_CONNBYTES_MODE,
+};
+
+static void connbytes_help(void)
+{
+	printf(
+"connbytes match options:\n"
+" [!] --connbytes from:[to]\n"
+"     --connbytes-dir [original, reply, both]\n"
+"     --connbytes-mode [packets, bytes, avgpkt]\n");
+}
+
+static const struct xt_option_entry connbytes_opts[] = {
+	{.name = "connbytes", .id = O_CONNBYTES, .type = XTTYPE_UINT64RC,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	{.name = "connbytes-dir", .id = O_CONNBYTES_DIR, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	{.name = "connbytes-mode", .id = O_CONNBYTES_MODE,
+	 .type = XTTYPE_STRING, .flags = XTOPT_MAND},
+	XTOPT_TABLEEND,
+};
+
+static void connbytes_parse(struct xt_option_call *cb)
+{
+	struct xt_connbytes_info *sinfo = cb->data;
+	unsigned long long i;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_CONNBYTES:
+		sinfo->count.from = 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;
+			sinfo->count.to = i;
+		}
+		break;
+	case O_CONNBYTES_DIR:
+		if (strcmp(cb->arg, "original") == 0)
+			sinfo->direction = XT_CONNBYTES_DIR_ORIGINAL;
+		else if (strcmp(cb->arg, "reply") == 0)
+			sinfo->direction = XT_CONNBYTES_DIR_REPLY;
+		else if (strcmp(cb->arg, "both") == 0)
+			sinfo->direction = XT_CONNBYTES_DIR_BOTH;
+		else
+			xtables_error(PARAMETER_PROBLEM,
+				   "Unknown --connbytes-dir `%s'", cb->arg);
+		break;
+	case O_CONNBYTES_MODE:
+		if (strcmp(cb->arg, "packets") == 0)
+			sinfo->what = XT_CONNBYTES_PKTS;
+		else if (strcmp(cb->arg, "bytes") == 0)
+			sinfo->what = XT_CONNBYTES_BYTES;
+		else if (strcmp(cb->arg, "avgpkt") == 0)
+			sinfo->what = XT_CONNBYTES_AVGPKT;
+		else
+			xtables_error(PARAMETER_PROBLEM,
+				   "Unknown --connbytes-mode `%s'", cb->arg);
+		break;
+	}
+}
+
+static void print_mode(const struct xt_connbytes_info *sinfo)
+{
+	switch (sinfo->what) {
+		case XT_CONNBYTES_PKTS:
+			fputs(" packets", stdout);
+			break;
+		case XT_CONNBYTES_BYTES:
+			fputs(" bytes", stdout);
+			break;
+		case XT_CONNBYTES_AVGPKT:
+			fputs(" avgpkt", stdout);
+			break;
+		default:
+			fputs(" unknown", stdout);
+			break;
+	}
+}
+
+static void print_direction(const struct xt_connbytes_info *sinfo)
+{
+	switch (sinfo->direction) {
+		case XT_CONNBYTES_DIR_ORIGINAL:
+			fputs(" original", stdout);
+			break;
+		case XT_CONNBYTES_DIR_REPLY:
+			fputs(" reply", stdout);
+			break;
+		case XT_CONNBYTES_DIR_BOTH:
+			fputs(" both", stdout);
+			break;
+		default:
+			fputs(" unknown", stdout);
+			break;
+	}
+}
+
+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;
+
+	print_from_to(sinfo, "");
+
+	fputs(" connbytes mode", stdout);
+	print_mode(sinfo);
+
+	fputs(" connbytes direction", stdout);
+	print_direction(sinfo);
+}
+
+static void connbytes_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connbytes_info *sinfo = (const void *)match->data;
+
+	print_from_to(sinfo, "--");
+
+	fputs(" --connbytes-mode", stdout);
+	print_mode(sinfo);
+
+	fputs(" --connbytes-dir", stdout);
+	print_direction(sinfo);
+}
+
+static struct xtables_match connbytes_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name 		= "connbytes",
+	.version 	= XTABLES_VERSION,
+	.size 		= XT_ALIGN(sizeof(struct xt_connbytes_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_connbytes_info)),
+	.help		= connbytes_help,
+	.print		= connbytes_print,
+	.save 		= connbytes_save,
+	.x6_parse	= connbytes_parse,
+	.x6_options	= connbytes_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&connbytes_match);
+}
diff --git a/extensions/libxt_connbytes.man b/extensions/libxt_connbytes.man
new file mode 100644
index 0000000..0504a55
--- /dev/null
+++ b/extensions/libxt_connbytes.man
@@ -0,0 +1,36 @@
+Match by how many bytes or packets a connection (or one of the two
+flows constituting the connection) has transferred so far, or by
+average bytes per packet.
+.PP
+The counters are 64-bit and are thus not expected to overflow ;)
+.PP
+The primary use is to detect long-lived downloads and mark them to be
+scheduled using a lower priority band in traffic control.
+.PP
+The transferred bytes per connection can also be viewed through
+`conntrack \-L` and accessed via ctnetlink.
+.PP
+NOTE that for connections which have no accounting information, the match will
+always return false. The "net.netfilter.nf_conntrack_acct" sysctl flag controls
+whether \fBnew\fP connections will be byte/packet counted. Existing connection
+flows will not be gaining/losing a/the accounting structure when be sysctl flag
+is flipped.
+.TP
+[\fB!\fP] \fB\-\-connbytes\fP \fIfrom\fP[\fB:\fP\fIto\fP]
+match packets from a connection whose packets/bytes/average packet
+size is more than FROM and less than TO bytes/packets. if TO is
+omitted only FROM check is done. "!" is used to match packets not
+falling in the range.
+.TP
+\fB\-\-connbytes\-dir\fP {\fBoriginal\fP|\fBreply\fP|\fBboth\fP}
+which packets to consider
+.TP
+\fB\-\-connbytes\-mode\fP {\fBpackets\fP|\fBbytes\fP|\fBavgpkt\fP}
+whether to check the amount of packets, number of bytes transferred or
+the average size (in bytes) of all packets received so far. Note that
+when "both" is used together with "avgpkt", and data is going (mainly)
+only in one direction (for example HTTP), the average packet size will
+be about half of the actual data packets.
+.TP
+Example:
+iptables .. \-m connbytes \-\-connbytes 10000:100000 \-\-connbytes\-dir both \-\-connbytes\-mode bytes ...
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.c b/extensions/libxt_connlimit.c
new file mode 100644
index 0000000..a569f86
--- /dev/null
+++ b/extensions/libxt_connlimit.c
@@ -0,0 +1,252 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_connlimit.h>
+
+enum {
+	O_UPTO = 0,
+	O_ABOVE,
+	O_MASK,
+	O_SADDR,
+	O_DADDR,
+	F_UPTO  = 1 << O_UPTO,
+	F_ABOVE = 1 << O_ABOVE,
+	F_MASK  = 1 << O_MASK,
+	F_SADDR = 1 << O_SADDR,
+	F_DADDR = 1 << O_DADDR,
+};
+
+static void connlimit_help(void)
+{
+	printf(
+"connlimit match options:\n"
+"  --connlimit-upto n     match if the number of existing connections is 0..n\n"
+"  --connlimit-above n    match if the number of existing connections is >n\n"
+"  --connlimit-mask n     group hosts using prefix length (default: max len)\n"
+"  --connlimit-saddr      select source address for grouping\n"
+"  --connlimit-daddr      select destination addresses for grouping\n");
+}
+
+#define s struct xt_connlimit_info
+static const struct xt_option_entry connlimit_opts[] = {
+	{.name = "connlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, limit)},
+	{.name = "connlimit-above", .id = O_ABOVE, .excl = F_UPTO,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, limit)},
+	{.name = "connlimit-mask", .id = O_MASK, .type = XTTYPE_PLENMASK,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, mask)},
+	{.name = "connlimit-saddr", .id = O_SADDR, .excl = F_DADDR,
+	 .type = XTTYPE_NONE},
+	{.name = "connlimit-daddr", .id = O_DADDR, .excl = F_SADDR,
+	 .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void connlimit_init(struct xt_entry_match *match)
+{
+	struct xt_connlimit_info *info = (void *)match->data;
+
+	/* This will also initialize the v4 mask correctly */
+	memset(info->v6_mask, 0xFF, sizeof(info->v6_mask));
+}
+
+static void connlimit_parse(struct xt_option_call *cb, uint8_t family)
+{
+	struct xt_connlimit_info *info = cb->data;
+	const unsigned int revision = (*cb->match)->u.user.revision;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_ABOVE:
+		if (cb->invert)
+			info->flags |= XT_CONNLIMIT_INVERT;
+		break;
+	case O_UPTO:
+		if (!cb->invert)
+			info->flags |= XT_CONNLIMIT_INVERT;
+		break;
+	case O_SADDR:
+		if (revision < 1)
+			xtables_error(PARAMETER_PROBLEM,
+				"xt_connlimit.0 does not support "
+				"--connlimit-daddr");
+		info->flags &= ~XT_CONNLIMIT_DADDR;
+		break;
+	case O_DADDR:
+		if (revision < 1)
+			xtables_error(PARAMETER_PROBLEM,
+				"xt_connlimit.0 does not support "
+				"--connlimit-daddr");
+		info->flags |= XT_CONNLIMIT_DADDR;
+		break;
+	}
+}
+
+static void connlimit_parse4(struct xt_option_call *cb)
+{
+	return connlimit_parse(cb, NFPROTO_IPV4);
+}
+
+static void connlimit_parse6(struct xt_option_call *cb)
+{
+	return connlimit_parse(cb, NFPROTO_IPV6);
+}
+
+static void connlimit_check(struct xt_fcheck_call *cb)
+{
+	if ((cb->xflags & (F_UPTO | F_ABOVE)) == 0)
+		xtables_error(PARAMETER_PROBLEM,
+			"You must specify \"--connlimit-above\" or "
+			"\"--connlimit-upto\".");
+}
+
+static unsigned int count_bits4(uint32_t mask)
+{
+	unsigned int bits = 0;
+
+	for (mask = ~ntohl(mask); mask != 0; mask >>= 1)
+		++bits;
+
+	return 32 - bits;
+}
+
+static unsigned int count_bits6(const uint32_t *mask)
+{
+	unsigned int bits = 0, i;
+	uint32_t tmp[4];
+
+	for (i = 0; i < 4; ++i)
+		for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1)
+			++bits;
+	return 128 - bits;
+}
+
+static void connlimit_print4(const void *ip,
+                             const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf(" #conn %s/%u %s %u",
+	       (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
+	       count_bits4(info->v4_mask),
+	       (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
+}
+
+static void connlimit_print6(const void *ip,
+                             const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+
+	printf(" #conn %s/%u %s %u",
+	       (info->flags & XT_CONNLIMIT_DADDR) ? "dst" : "src",
+	       count_bits6(info->v6_mask),
+	       (info->flags & XT_CONNLIMIT_INVERT) ? "<=" : ">", info->limit);
+}
+
+static void connlimit_save4(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+	const int revision = match->u.user.revision;
+
+	if (info->flags & XT_CONNLIMIT_INVERT)
+		printf(" --connlimit-upto %u", info->limit);
+	else
+		printf(" --connlimit-above %u", info->limit);
+	printf(" --connlimit-mask %u", count_bits4(info->v4_mask));
+	if (revision >= 1) {
+		if (info->flags & XT_CONNLIMIT_DADDR)
+			printf(" --connlimit-daddr");
+		else
+			printf(" --connlimit-saddr");
+	}
+}
+
+static void connlimit_save6(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connlimit_info *info = (const void *)match->data;
+	const int revision = match->u.user.revision;
+
+	if (info->flags & XT_CONNLIMIT_INVERT)
+		printf(" --connlimit-upto %u", info->limit);
+	else
+		printf(" --connlimit-above %u", info->limit);
+	printf(" --connlimit-mask %u", count_bits6(info->v6_mask));
+	if (revision >= 1) {
+		if (info->flags & XT_CONNLIMIT_DADDR)
+			printf(" --connlimit-daddr");
+		else
+			printf(" --connlimit-saddr");
+	}
+}
+
+static struct xtables_match connlimit_mt_reg[] = {
+	{
+		.name          = "connlimit",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
+		.userspacesize = offsetof(struct xt_connlimit_info, data),
+		.help          = connlimit_help,
+		.init          = connlimit_init,
+		.x6_parse      = connlimit_parse4,
+		.x6_fcheck     = connlimit_check,
+		.print         = connlimit_print4,
+		.save          = connlimit_save4,
+		.x6_options    = connlimit_opts,
+	},
+	{
+		.name          = "connlimit",
+		.revision      = 0,
+		.family        = NFPROTO_IPV6,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
+		.userspacesize = offsetof(struct xt_connlimit_info, data),
+		.help          = connlimit_help,
+		.init          = connlimit_init,
+		.x6_parse      = connlimit_parse6,
+		.x6_fcheck     = connlimit_check,
+		.print         = connlimit_print6,
+		.save          = connlimit_save6,
+		.x6_options    = connlimit_opts,
+	},
+	{
+		.name          = "connlimit",
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
+		.userspacesize = offsetof(struct xt_connlimit_info, data),
+		.help          = connlimit_help,
+		.init          = connlimit_init,
+		.x6_parse      = connlimit_parse4,
+		.x6_fcheck     = connlimit_check,
+		.print         = connlimit_print4,
+		.save          = connlimit_save4,
+		.x6_options    = connlimit_opts,
+	},
+	{
+		.name          = "connlimit",
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connlimit_info)),
+		.userspacesize = offsetof(struct xt_connlimit_info, data),
+		.help          = connlimit_help,
+		.init          = connlimit_init,
+		.x6_parse      = connlimit_parse6,
+		.x6_fcheck     = connlimit_check,
+		.print         = connlimit_print6,
+		.save          = connlimit_save6,
+		.x6_options    = connlimit_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg));
+}
diff --git a/extensions/libxt_connlimit.man b/extensions/libxt_connlimit.man
new file mode 100644
index 0000000..ad9f40f
--- /dev/null
+++ b/extensions/libxt_connlimit.man
@@ -0,0 +1,42 @@
+Allows you to restrict the number of parallel connections to a server per
+client IP address (or client address block).
+.TP
+\fB\-\-connlimit\-upto\fP \fIn\fP
+Match if the number of existing connections is below or equal \fIn\fP.
+.TP
+\fB\-\-connlimit\-above\fP \fIn\fP
+Match if the number of existing connections is above \fIn\fP.
+.TP
+\fB\-\-connlimit\-mask\fP \fIprefix_length\fP
+Group hosts using the prefix length. For IPv4, this must be a number between
+(including) 0 and 32. For IPv6, between 0 and 128. If not specified, the
+maximum prefix length for the applicable protocol is used.
+.TP
+\fB\-\-connlimit\-saddr\fP
+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.
+.PP
+Examples:
+.TP
+# allow 2 telnet connections per client host
+iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit \-\-connlimit\-above 2 \-j REJECT
+.TP
+# you can also match the other way around:
+iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit \-\-connlimit\-upto 2 \-j ACCEPT
+.TP
+# limit the number of parallel HTTP requests to 16 per class C sized \
+source network (24 bit netmask)
+iptables \-p tcp \-\-syn \-\-dport 80 \-m connlimit \-\-connlimit\-above 16
+\-\-connlimit\-mask 24 \-j REJECT
+.TP
+# limit the number of parallel HTTP requests to 16 for the link local network
+(ipv6)
+ip6tables \-p tcp \-\-syn \-\-dport 80 \-s fe80::/64 \-m connlimit \-\-connlimit\-above
+16 \-\-connlimit\-mask 64 \-j REJECT
+.TP
+# Limit the number of connections to a particular host:
+ip6tables \-p tcp \-\-syn \-\-dport 49152:65535 \-d 2001:db8::1 \-m connlimit
+\-\-connlimit-above 100 \-j REJECT
diff --git a/extensions/libxt_connmark.c b/extensions/libxt_connmark.c
new file mode 100644
index 0000000..6f1d532
--- /dev/null
+++ b/extensions/libxt_connmark.c
@@ -0,0 +1,157 @@
+/* Shared library add-on to iptables to add connmark matching support.
+ *
+ * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * Version 1.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_connmark.h>
+
+struct xt_connmark_info {
+	unsigned long mark, mask;
+	uint8_t invert;
+};
+
+enum {
+	O_MARK = 0,
+};
+
+static void connmark_mt_help(void)
+{
+	printf(
+"connmark match options:\n"
+"[!] --mark value[/mask]    Match ctmark value with optional mask\n");
+}
+
+static const struct xt_option_entry connmark_mt_opts[] = {
+	{.name = "mark", .id = O_MARK, .type = XTTYPE_MARKMASK32,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void connmark_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_connmark_mtinfo1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		info->invert = true;
+	info->mark = cb->val.mark;
+	info->mask = cb->val.mask;
+}
+
+static void connmark_parse(struct xt_option_call *cb)
+{
+	struct xt_connmark_info *markinfo = cb->data;
+
+	xtables_option_parse(cb);
+	markinfo->mark = cb->val.mark;
+	markinfo->mask = cb->val.mask;
+	if (cb->invert)
+		markinfo->invert = 1;
+}
+
+static void print_mark(unsigned int mark, unsigned int mask)
+{
+	if (mask != 0xffffffffU)
+		printf(" 0x%x/0x%x", mark, mask);
+	else
+		printf(" 0x%x", mark);
+}
+
+static void
+connmark_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_connmark_info *info = (const void *)match->data;
+
+	printf(" CONNMARK match ");
+	if (info->invert)
+		printf("!");
+	print_mark(info->mark, info->mask);
+}
+
+static void
+connmark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_connmark_mtinfo1 *info = (const void *)match->data;
+
+	printf(" connmark match ");
+	if (info->invert)
+		printf("!");
+	print_mark(info->mark, info->mask);
+}
+
+static void connmark_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connmark_info *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	printf(" --mark");
+	print_mark(info->mark, info->mask);
+}
+
+static void
+connmark_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_connmark_mtinfo1 *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	printf(" --mark");
+	print_mark(info->mark, info->mask);
+}
+
+static struct xtables_match connmark_mt_reg[] = {
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "connmark",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_connmark_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_info)),
+		.help          = connmark_mt_help,
+		.print         = connmark_print,
+		.save          = connmark_save,
+		.x6_parse      = connmark_parse,
+		.x6_options    = connmark_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "connmark",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)),
+		.help          = connmark_mt_help,
+		.print         = connmark_mt_print,
+		.save          = connmark_mt_save,
+		.x6_parse      = connmark_mt_parse,
+		.x6_options    = connmark_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(connmark_mt_reg, ARRAY_SIZE(connmark_mt_reg));
+}
diff --git a/extensions/libxt_connmark.man b/extensions/libxt_connmark.man
new file mode 100644
index 0000000..4e83801
--- /dev/null
+++ b/extensions/libxt_connmark.man
@@ -0,0 +1,6 @@
+This module matches the netfilter mark field associated with a connection
+(which can be set using the \fBCONNMARK\fP target below).
+.TP
+[\fB!\fP] \fB\-\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Matches packets in connections with the given mark value (if a mask is
+specified, this is logically ANDed with the mark before the comparison).
diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c
new file mode 100644
index 0000000..128bbd2
--- /dev/null
+++ b/extensions/libxt_conntrack.c
@@ -0,0 +1,1328 @@
+/*
+ *	libxt_conntrack
+ *	Shared library add-on to iptables for conntrack matching support.
+ *
+ *	GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
+ *	Jan Engelhardt <jengelh@computergmbh.de>
+ */
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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 {
+		__be32 ip;
+		union {
+			__u16 all;
+		} u;
+	} src;
+
+	struct {
+		__be32 ip;
+		union {
+			__u16 all;
+		} u;
+
+		/* The protocol. */
+		__u16 protonum;
+	} dst;
+};
+
+struct xt_conntrack_info {
+	unsigned int statemask, statusmask;
+
+	struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX];
+	struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
+
+	unsigned long expires_min, expires_max;
+
+	/* Flags word */
+	uint8_t flags;
+	/* Inverse flags */
+	uint8_t invflags;
+};
+
+enum {
+	O_CTSTATE = 0,
+	O_CTPROTO,
+	O_CTORIGSRC,
+	O_CTORIGDST,
+	O_CTREPLSRC,
+	O_CTREPLDST,
+	O_CTORIGSRCPORT,
+	O_CTORIGDSTPORT,
+	O_CTREPLSRCPORT,
+	O_CTREPLDSTPORT,
+	O_CTSTATUS,
+	O_CTEXPIRE,
+	O_CTDIR,
+};
+
+static void conntrack_mt_help(void)
+{
+	printf(
+"conntrack match options:\n"
+"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n"
+"                               State(s) to match\n"
+"[!] --ctproto proto            Protocol to match; by number or name, e.g. \"tcp\"\n"
+"[!] --ctorigsrc address[/mask]\n"
+"[!] --ctorigdst address[/mask]\n"
+"[!] --ctreplsrc address[/mask]\n"
+"[!] --ctrepldst address[/mask]\n"
+"                               Original/Reply source/destination address\n"
+"[!] --ctorigsrcport port\n"
+"[!] --ctorigdstport port\n"
+"[!] --ctreplsrcport port\n"
+"[!] --ctrepldstport port\n"
+"                               TCP/UDP/SCTP orig./reply source/destination port\n"
+"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n"
+"                               Status(es) to match\n"
+"[!] --ctexpire time[:time]     Match remaining lifetime in seconds against\n"
+"                               value or range of values (inclusive)\n"
+"    --ctdir {ORIGINAL|REPLY}   Flow direction of packet\n");
+}
+
+#define s struct xt_conntrack_info /* for v0 */
+static const struct xt_option_entry conntrack_mt_opts_v0[] = {
+	{.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_HOST,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctorigdst", .id = O_CTORIGDST, .type = XTTYPE_HOST,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctreplsrc", .id = O_CTREPLSRC, .type = XTTYPE_HOST,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctrepldst", .id = O_CTREPLDST, .type = XTTYPE_HOST,
+	 .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},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+#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,
+	 .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},
+	{.name = "ctorigsrcport", .id = O_CTORIGSRCPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctorigdstport", .id = O_CTORIGDSTPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctreplsrcport", .id = O_CTREPLSRCPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctrepldstport", .id = O_CTREPLDSTPORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT},
+	{.name = "ctdir", .id = O_CTDIR, .type = XTTYPE_STRING},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static int
+parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo)
+{
+	if (strncasecmp(state, "INVALID", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_INVALID;
+	else if (strncasecmp(state, "NEW", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
+	else if (strncasecmp(state, "ESTABLISHED", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
+	else if (strncasecmp(state, "RELATED", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
+	else if (strncasecmp(state, "UNTRACKED", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED;
+	else if (strncasecmp(state, "SNAT", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_SNAT;
+	else if (strncasecmp(state, "DNAT", len) == 0)
+		sinfo->statemask |= XT_CONNTRACK_STATE_DNAT;
+	else
+		return 0;
+	return 1;
+}
+
+static void
+parse_states(const char *arg, struct xt_conntrack_info *sinfo)
+{
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+			xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
+		arg = comma+1;
+	}
+	if (!*arg)
+		xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of "
+					      "states with no spaces, e.g. "
+					      "ESTABLISHED,RELATED");
+	if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
+}
+
+static bool
+conntrack_ps_state(struct xt_conntrack_mtinfo3 *info, const char *state,
+                   size_t z)
+{
+	if (strncasecmp(state, "INVALID", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_INVALID;
+	else if (strncasecmp(state, "NEW", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW);
+	else if (strncasecmp(state, "ESTABLISHED", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
+	else if (strncasecmp(state, "RELATED", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
+	else if (strncasecmp(state, "UNTRACKED", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED;
+	else if (strncasecmp(state, "SNAT", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_SNAT;
+	else if (strncasecmp(state, "DNAT", z) == 0)
+		info->state_mask |= XT_CONNTRACK_STATE_DNAT;
+	else
+		return false;
+	return true;
+}
+
+static void
+conntrack_ps_states(struct xt_conntrack_mtinfo3 *info, const char *arg)
+{
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg || !conntrack_ps_state(info, arg, comma - arg))
+			xtables_error(PARAMETER_PROBLEM,
+			           "Bad ctstate \"%s\"", arg);
+		arg = comma + 1;
+	}
+
+	if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg)))
+		xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg);
+}
+
+static int
+parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo)
+{
+	if (strncasecmp(status, "NONE", len) == 0)
+		sinfo->statusmask |= 0;
+	else if (strncasecmp(status, "EXPECTED", len) == 0)
+		sinfo->statusmask |= IPS_EXPECTED;
+	else if (strncasecmp(status, "SEEN_REPLY", len) == 0)
+		sinfo->statusmask |= IPS_SEEN_REPLY;
+	else if (strncasecmp(status, "ASSURED", len) == 0)
+		sinfo->statusmask |= IPS_ASSURED;
+#ifdef IPS_CONFIRMED
+	else if (strncasecmp(status, "CONFIRMED", len) == 0)
+		sinfo->statusmask |= IPS_CONFIRMED;
+#endif
+	else
+		return 0;
+	return 1;
+}
+
+static void
+parse_statuses(const char *arg, struct xt_conntrack_info *sinfo)
+{
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg || !parse_status(arg, comma-arg, sinfo))
+			xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
+		arg = comma+1;
+	}
+
+	if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
+		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
+}
+
+static bool
+conntrack_ps_status(struct xt_conntrack_mtinfo3 *info, const char *status,
+                    size_t z)
+{
+	if (strncasecmp(status, "NONE", z) == 0)
+		info->status_mask |= 0;
+	else if (strncasecmp(status, "EXPECTED", z) == 0)
+		info->status_mask |= IPS_EXPECTED;
+	else if (strncasecmp(status, "SEEN_REPLY", z) == 0)
+		info->status_mask |= IPS_SEEN_REPLY;
+	else if (strncasecmp(status, "ASSURED", z) == 0)
+		info->status_mask |= IPS_ASSURED;
+	else if (strncasecmp(status, "CONFIRMED", z) == 0)
+		info->status_mask |= IPS_CONFIRMED;
+	else
+		return false;
+	return true;
+}
+
+static void
+conntrack_ps_statuses(struct xt_conntrack_mtinfo3 *info, const char *arg)
+{
+	const char *comma;
+
+	while ((comma = strchr(arg, ',')) != NULL) {
+		if (comma == arg || !conntrack_ps_status(info, arg, comma - arg))
+			xtables_error(PARAMETER_PROBLEM,
+			           "Bad ctstatus \"%s\"", arg);
+		arg = comma + 1;
+	}
+
+	if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg)))
+		xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg);
+}
+
+static void conntrack_parse(struct xt_option_call *cb)
+{
+	struct xt_conntrack_info *sinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_CTSTATE:
+		parse_states(cb->arg, sinfo);
+		if (cb->invert)
+			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;
+		if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
+		    && (sinfo->invflags & XT_INV_PROTO))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rule would never match protocol");
+
+		sinfo->flags |= XT_CONNTRACK_PROTO;
+		break;
+	case O_CTORIGSRC:
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_ORIGSRC;
+		sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = cb->val.haddr.ip;
+		sinfo->flags |= XT_CONNTRACK_ORIGSRC;
+		break;
+	case O_CTORIGDST:
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_ORIGDST;
+		sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = cb->val.haddr.ip;
+		sinfo->flags |= XT_CONNTRACK_ORIGDST;
+		break;
+	case O_CTREPLSRC:
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_REPLSRC;
+		sinfo->tuple[IP_CT_DIR_REPLY].src.ip = cb->val.haddr.ip;
+		sinfo->flags |= XT_CONNTRACK_REPLSRC;
+		break;
+	case O_CTREPLDST:
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_REPLDST;
+		sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = cb->val.haddr.ip;
+		sinfo->flags |= XT_CONNTRACK_REPLDST;
+		break;
+	case O_CTSTATUS:
+		parse_statuses(cb->arg, sinfo);
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_STATUS;
+		sinfo->flags |= XT_CONNTRACK_STATUS;
+		break;
+	case O_CTEXPIRE:
+		sinfo->expires_min = cb->val.u32_range[0];
+		sinfo->expires_max = cb->val.u32_range[0];
+		if (cb->nvals >= 2)
+			sinfo->expires_max = cb->val.u32_range[1];
+		if (cb->invert)
+			sinfo->invflags |= XT_CONNTRACK_EXPIRES;
+		sinfo->flags |= XT_CONNTRACK_EXPIRES;
+		break;
+	}
+}
+
+static void conntrack_mt_parse(struct xt_option_call *cb, uint8_t rev)
+{
+	struct xt_conntrack_mtinfo3 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_CTSTATE:
+		conntrack_ps_states(info, cb->arg);
+		info->match_flags |= XT_CONNTRACK_STATE;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_STATE;
+		break;
+	case O_CTPROTO:
+		info->l4proto = cb->val.protocol;
+		if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO))
+			xtables_error(PARAMETER_PROBLEM, "conntrack: rule would "
+			           "never match protocol");
+
+		info->match_flags |= XT_CONNTRACK_PROTO;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_PROTO;
+		break;
+	case O_CTORIGSRC:
+		info->origsrc_addr = cb->val.haddr;
+		info->origsrc_mask = cb->val.hmask;
+		info->match_flags |= XT_CONNTRACK_ORIGSRC;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_ORIGSRC;
+		break;
+	case O_CTORIGDST:
+		info->origdst_addr = cb->val.haddr;
+		info->origdst_mask = cb->val.hmask;
+		info->match_flags |= XT_CONNTRACK_ORIGDST;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_ORIGDST;
+		break;
+	case O_CTREPLSRC:
+		info->replsrc_addr = cb->val.haddr;
+		info->replsrc_mask = cb->val.hmask;
+		info->match_flags |= XT_CONNTRACK_REPLSRC;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_REPLSRC;
+		break;
+	case O_CTREPLDST:
+		info->repldst_addr = cb->val.haddr;
+		info->repldst_mask = cb->val.hmask;
+		info->match_flags |= XT_CONNTRACK_REPLDST;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_REPLDST;
+		break;
+	case O_CTSTATUS:
+		conntrack_ps_statuses(info, cb->arg);
+		info->match_flags |= XT_CONNTRACK_STATUS;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_STATUS;
+		break;
+	case O_CTEXPIRE:
+		info->expires_min = cb->val.u32_range[0];
+		info->expires_max = cb->val.u32_range[0];
+		if (cb->nvals >= 2)
+			info->expires_max = cb->val.u32_range[1];
+		info->match_flags |= XT_CONNTRACK_EXPIRES;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_EXPIRES;
+		break;
+	case O_CTORIGSRCPORT:
+		info->origsrc_port = cb->val.port_range[0];
+		info->origsrc_port_high = cb->val.port_range[cb->nvals >= 2];
+		info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT;
+		break;
+	case O_CTORIGDSTPORT:
+		info->origdst_port = cb->val.port_range[0];
+		info->origdst_port_high = cb->val.port_range[cb->nvals >= 2];
+		info->match_flags |= XT_CONNTRACK_ORIGDST_PORT;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT;
+		break;
+	case O_CTREPLSRCPORT:
+		info->replsrc_port = cb->val.port_range[0];
+		info->replsrc_port_high = cb->val.port_range[cb->nvals >= 2];
+		info->match_flags |= XT_CONNTRACK_REPLSRC_PORT;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT;
+		break;
+	case O_CTREPLDSTPORT:
+		info->repldst_port = cb->val.port_range[0];
+		info->repldst_port_high = cb->val.port_range[cb->nvals >= 2];
+		info->match_flags |= XT_CONNTRACK_REPLDST_PORT;
+		if (cb->invert)
+			info->invert_flags |= XT_CONNTRACK_REPLDST_PORT;
+		break;
+	case O_CTDIR:
+		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
+			info->match_flags  |= XT_CONNTRACK_DIRECTION;
+			info->invert_flags &= ~XT_CONNTRACK_DIRECTION;
+		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
+			info->match_flags  |= XT_CONNTRACK_DIRECTION;
+			info->invert_flags |= XT_CONNTRACK_DIRECTION;
+		} else {
+			xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", cb->arg);
+		}
+		break;
+	}
+}
+
+#define cinfo_transform(r, l) \
+	do { \
+		memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \
+		(r)->state_mask  = (l)->state_mask; \
+		(r)->status_mask = (l)->status_mask; \
+	} while (false);
+
+static void conntrack1_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_conntrack_mtinfo1 *info = cb->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	memset(&up, 0, sizeof(up));
+	cinfo_transform(&up, info);
+	up.origsrc_port_high = up.origsrc_port;
+	up.origdst_port_high = up.origdst_port;
+	up.replsrc_port_high = up.replsrc_port;
+	up.repldst_port_high = up.repldst_port;
+	cb->data = &up;
+	conntrack_mt_parse(cb, 3);
+	if (up.origsrc_port != up.origsrc_port_high ||
+	    up.origdst_port != up.origdst_port_high ||
+	    up.replsrc_port != up.replsrc_port_high ||
+	    up.repldst_port != up.repldst_port_high)
+		xtables_error(PARAMETER_PROBLEM,
+			"conntrack rev 1 does not support port ranges");
+	cinfo_transform(info, &up);
+	cb->data = info;
+}
+
+static void conntrack2_mt_parse(struct xt_option_call *cb)
+{
+#define cinfo2_transform(r, l) \
+		memcpy((r), (l), offsetof(typeof(*(l)), sizeof(*info));
+
+	struct xt_conntrack_mtinfo2 *info = cb->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	memset(&up, 0, sizeof(up));
+	memcpy(&up, info, sizeof(*info));
+	up.origsrc_port_high = up.origsrc_port;
+	up.origdst_port_high = up.origdst_port;
+	up.replsrc_port_high = up.replsrc_port;
+	up.repldst_port_high = up.repldst_port;
+	cb->data = &up;
+	conntrack_mt_parse(cb, 3);
+	if (up.origsrc_port != up.origsrc_port_high ||
+	    up.origdst_port != up.origdst_port_high ||
+	    up.replsrc_port != up.replsrc_port_high ||
+	    up.repldst_port != up.repldst_port_high)
+		xtables_error(PARAMETER_PROBLEM,
+			"conntrack rev 2 does not support port ranges");
+	memcpy(info, &up, sizeof(*info));
+	cb->data = info;
+#undef cinfo2_transform
+}
+
+static void conntrack3_mt_parse(struct xt_option_call *cb)
+{
+	conntrack_mt_parse(cb, 3);
+}
+
+static void conntrack_mt_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option "
+		           "is required");
+}
+
+static void
+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 = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_SNAT) {
+		printf("%sSNAT", sep);
+		sep = ",";
+	}
+	if (statemask & XT_CONNTRACK_STATE_DNAT) {
+		printf("%sDNAT", sep);
+		sep = ",";
+	}
+}
+
+static void
+print_status(unsigned int statusmask)
+{
+	const char *sep = " ";
+
+	if (statusmask & IPS_EXPECTED) {
+		printf("%sEXPECTED", sep);
+		sep = ",";
+	}
+	if (statusmask & IPS_SEEN_REPLY) {
+		printf("%sSEEN_REPLY", sep);
+		sep = ",";
+	}
+	if (statusmask & IPS_ASSURED) {
+		printf("%sASSURED", sep);
+		sep = ",";
+	}
+	if (statusmask & IPS_CONFIRMED) {
+		printf("%sCONFIRMED", sep);
+		sep = ",";
+	}
+	if (statusmask == 0)
+		printf("%sNONE", sep);
+}
+
+static void
+conntrack_dump_addr(const union nf_inet_addr *addr,
+                    const union nf_inet_addr *mask,
+                    unsigned int family, bool numeric)
+{
+	if (family == NFPROTO_IPV4) {
+		if (!numeric && addr->ip == 0) {
+			printf(" anywhere");
+			return;
+		}
+		if (numeric)
+			printf(" %s%s",
+			       xtables_ipaddr_to_numeric(&addr->in),
+			       xtables_ipmask_to_numeric(&mask->in));
+		else
+			printf(" %s%s",
+			       xtables_ipaddr_to_anyname(&addr->in),
+			       xtables_ipmask_to_numeric(&mask->in));
+	} else if (family == NFPROTO_IPV6) {
+		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
+		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
+			printf(" anywhere");
+			return;
+		}
+		if (numeric)
+			printf(" %s%s",
+			       xtables_ip6addr_to_numeric(&addr->in6),
+			       xtables_ip6mask_to_numeric(&mask->in6));
+		else
+			printf(" %s%s",
+			       xtables_ip6addr_to_anyname(&addr->in6),
+			       xtables_ip6mask_to_numeric(&mask->in6));
+	}
+}
+
+static void
+print_addr(const struct in_addr *addr, const struct in_addr *mask,
+           int inv, int numeric)
+{
+	char buf[BUFSIZ];
+
+	if (inv)
+		printf(" !");
+
+	if (mask->s_addr == 0L && !numeric)
+		printf(" %s", "anywhere");
+	else {
+		if (numeric)
+			strcpy(buf, xtables_ipaddr_to_numeric(addr));
+		else
+			strcpy(buf, xtables_ipaddr_to_anyname(addr));
+		strcat(buf, xtables_ipmask_to_numeric(mask));
+		printf(" %s", buf);
+	}
+}
+
+static void
+matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx)
+{
+	const struct xt_conntrack_info *sinfo = (const void *)match->data;
+
+	if(sinfo->flags & XT_CONNTRACK_STATE) {
+        	if (sinfo->invflags & XT_CONNTRACK_STATE)
+			printf(" !");
+		printf(" %sctstate", optpfx);
+		print_state(sinfo->statemask);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_PROTO) {
+        	if (sinfo->invflags & XT_CONNTRACK_PROTO)
+			printf(" !");
+		printf(" %sctproto", optpfx);
+		printf(" %u", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_ORIGSRC) {
+		if (sinfo->invflags & XT_CONNTRACK_ORIGSRC)
+			printf(" !");
+		printf(" %sctorigsrc", optpfx);
+
+		print_addr(
+		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
+		    &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
+		    false,
+		    numeric);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_ORIGDST) {
+		if (sinfo->invflags & XT_CONNTRACK_ORIGDST)
+			printf(" !");
+		printf(" %sctorigdst", optpfx);
+
+		print_addr(
+		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
+		    &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
+		    false,
+		    numeric);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_REPLSRC) {
+		if (sinfo->invflags & XT_CONNTRACK_REPLSRC)
+			printf(" !");
+		printf(" %sctreplsrc", optpfx);
+
+		print_addr(
+		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
+		    &sinfo->sipmsk[IP_CT_DIR_REPLY],
+		    false,
+		    numeric);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_REPLDST) {
+		if (sinfo->invflags & XT_CONNTRACK_REPLDST)
+			printf(" !");
+		printf(" %sctrepldst", optpfx);
+
+		print_addr(
+		    (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
+		    &sinfo->dipmsk[IP_CT_DIR_REPLY],
+		    false,
+		    numeric);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_STATUS) {
+        	if (sinfo->invflags & XT_CONNTRACK_STATUS)
+			printf(" !");
+		printf(" %sctstatus", optpfx);
+		print_status(sinfo->statusmask);
+	}
+
+	if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
+        	if (sinfo->invflags & XT_CONNTRACK_EXPIRES)
+			printf(" !");
+		printf(" %sctexpire ", optpfx);
+
+        	if (sinfo->expires_max == sinfo->expires_min)
+			printf("%lu", sinfo->expires_min);
+        	else
+			printf("%lu:%lu", sinfo->expires_min, sinfo->expires_max);
+	}
+
+	if (sinfo->flags & XT_CONNTRACK_DIRECTION) {
+		if (sinfo->invflags & XT_CONNTRACK_DIRECTION)
+			printf(" %sctdir REPLY", optpfx);
+		else
+			printf(" %sctdir ORIGINAL", optpfx);
+	}
+
+}
+
+static void
+conntrack_dump_ports(const char *prefix, const char *opt,
+		     u_int16_t port_low, u_int16_t port_high)
+{
+	if (port_high == 0 || port_low == port_high)
+		printf(" %s%s %u", prefix, opt, port_low);
+	else
+		printf(" %s%s %u:%u", prefix, opt, port_low, port_high);
+}
+
+static void
+conntrack_dump(const struct xt_conntrack_mtinfo3 *info, const char *prefix,
+               unsigned int family, bool numeric, bool v3)
+{
+	if (info->match_flags & XT_CONNTRACK_STATE) {
+		if (info->invert_flags & XT_CONNTRACK_STATE)
+			printf(" !");
+		printf(" %s%s", prefix,
+			info->match_flags & XT_CONNTRACK_STATE_ALIAS
+				? "state" : "ctstate");
+		print_state(info->state_mask);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_PROTO) {
+		if (info->invert_flags & XT_CONNTRACK_PROTO)
+			printf(" !");
+		printf(" %sctproto %u", prefix, info->l4proto);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_ORIGSRC) {
+		if (info->invert_flags & XT_CONNTRACK_ORIGSRC)
+			printf(" !");
+		printf(" %sctorigsrc", prefix);
+		conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask,
+		                    family, numeric);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_ORIGDST) {
+		if (info->invert_flags & XT_CONNTRACK_ORIGDST)
+			printf(" !");
+		printf(" %sctorigdst", prefix);
+		conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask,
+		                    family, numeric);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_REPLSRC) {
+		if (info->invert_flags & XT_CONNTRACK_REPLSRC)
+			printf(" !");
+		printf(" %sctreplsrc", prefix);
+		conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask,
+		                    family, numeric);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_REPLDST) {
+		if (info->invert_flags & XT_CONNTRACK_REPLDST)
+			printf(" !");
+		printf(" %sctrepldst", prefix);
+		conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask,
+		                    family, numeric);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) {
+		if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)
+			printf(" !");
+		conntrack_dump_ports(prefix, "ctorigsrcport",
+				     v3 ? info->origsrc_port : ntohs(info->origsrc_port),
+				     v3 ? info->origsrc_port_high : 0);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) {
+		if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)
+			printf(" !");
+		conntrack_dump_ports(prefix, "ctorigdstport",
+				     v3 ? info->origdst_port : ntohs(info->origdst_port),
+				     v3 ? info->origdst_port_high : 0);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) {
+		if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)
+			printf(" !");
+		conntrack_dump_ports(prefix, "ctreplsrcport",
+				     v3 ? info->replsrc_port : ntohs(info->replsrc_port),
+				     v3 ? info->replsrc_port_high : 0);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) {
+		if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT)
+			printf(" !");
+		conntrack_dump_ports(prefix, "ctrepldstport",
+				     v3 ? info->repldst_port : ntohs(info->repldst_port),
+				     v3 ? info->repldst_port_high : 0);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_STATUS) {
+		if (info->invert_flags & XT_CONNTRACK_STATUS)
+			printf(" !");
+		printf(" %sctstatus", prefix);
+		print_status(info->status_mask);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_EXPIRES) {
+		if (info->invert_flags & XT_CONNTRACK_EXPIRES)
+			printf(" !");
+		printf(" %sctexpire ", prefix);
+
+		if (info->expires_max == info->expires_min)
+			printf("%u", (unsigned int)info->expires_min);
+		else
+			printf("%u:%u", (unsigned int)info->expires_min,
+			       (unsigned int)info->expires_max);
+	}
+
+	if (info->match_flags & XT_CONNTRACK_DIRECTION) {
+		if (info->invert_flags & XT_CONNTRACK_DIRECTION)
+			printf(" %sctdir REPLY", prefix);
+		else
+			printf(" %sctdir ORIGINAL", prefix);
+	}
+}
+
+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)
+{
+	matchinfo_print(ip, match, numeric, "");
+}
+
+static void
+conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	cinfo_transform(&up, info);
+	conntrack_dump(&up, "", NFPROTO_IPV4, numeric, false);
+}
+
+static void
+conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	cinfo_transform(&up, info);
+	conntrack_dump(&up, "", NFPROTO_IPV6, numeric, false);
+}
+
+static void
+conntrack2_mt_print(const void *ip, const struct xt_entry_match *match,
+                    int numeric)
+{
+	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, false);
+}
+
+static void
+conntrack2_mt6_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, false);
+}
+
+static void
+conntrack3_mt_print(const void *ip, const struct xt_entry_match *match,
+                    int numeric)
+{
+	conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric, true);
+}
+
+static void
+conntrack3_mt6_print(const void *ip, const struct xt_entry_match *match,
+                     int numeric)
+{
+	conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric, true);
+}
+
+static void conntrack_save(const void *ip, const struct xt_entry_match *match)
+{
+	matchinfo_print(ip, match, 1, "--");
+}
+
+static void conntrack3_mt_save(const void *ip,
+                               const struct xt_entry_match *match)
+{
+	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, true);
+}
+
+static void conntrack3_mt6_save(const void *ip,
+                                const struct xt_entry_match *match)
+{
+	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, true);
+}
+
+static void conntrack2_mt_save(const void *ip,
+                               const struct xt_entry_match *match)
+{
+	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true, false);
+}
+
+static void conntrack2_mt6_save(const void *ip,
+                                const struct xt_entry_match *match)
+{
+	conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true, false);
+}
+
+static void
+conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	cinfo_transform(&up, info);
+	conntrack_dump(&up, "--", NFPROTO_IPV4, true, false);
+}
+
+static void
+conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_conntrack_mtinfo1 *info = (void *)match->data;
+	struct xt_conntrack_mtinfo3 up;
+
+	cinfo_transform(&up, info);
+	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,
+		.name          = "conntrack",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack_print,
+		.save          = conntrack_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack_mt_opts_v0,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack1_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack1_mt4_print,
+		.save          = conntrack1_mt4_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack1_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack1_mt6_print,
+		.save          = conntrack1_mt6_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 2,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack2_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack2_mt_print,
+		.save          = conntrack2_mt_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 2,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack2_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack2_mt6_print,
+		.save          = conntrack2_mt6_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack2_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 3,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack3_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack3_mt_print,
+		.save          = conntrack3_mt_save,
+		.alias	       = conntrack_print_name_alias,
+		.x6_options    = conntrack3_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "conntrack",
+		.revision      = 3,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo3)),
+		.help          = conntrack_mt_help,
+		.x6_parse      = conntrack3_mt_parse,
+		.x6_fcheck     = conntrack_mt_check,
+		.print         = conntrack3_mt6_print,
+		.save          = conntrack3_mt6_save,
+		.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,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
+}
diff --git a/extensions/libxt_conntrack.man b/extensions/libxt_conntrack.man
new file mode 100644
index 0000000..4b13f0f
--- /dev/null
+++ b/extensions/libxt_conntrack.man
@@ -0,0 +1,86 @@
+This module, when combined with connection tracking, allows access to the
+connection tracking state for this packet/connection.
+.TP
+[\fB!\fP] \fB\-\-ctstate\fP \fIstatelist\fP
+\fIstatelist\fP is a comma separated list of the connection states to match.
+Possible states are listed below.
+.TP
+[\fB!\fP] \fB\-\-ctproto\fP \fIl4proto\fP
+Layer-4 protocol to match (by number or name)
+.TP
+[\fB!\fP] \fB\-\-ctorigsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+.TP
+[\fB!\fP] \fB\-\-ctorigdst\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+.TP
+[\fB!\fP] \fB\-\-ctreplsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+.TP
+[\fB!\fP] \fB\-\-ctrepldst\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+Match against original/reply source/destination address
+.TP
+[\fB!\fP] \fB\-\-ctorigsrcport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-ctorigdstport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-ctreplsrcport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-ctrepldstport\fP \fIport\fP[\fB:\fP\fIport\fP]
+Match against original/reply source/destination port (TCP/UDP/etc.) or GRE key.
+Matching against port ranges is only supported in kernel versions above 2.6.38.
+.TP
+[\fB!\fP] \fB\-\-ctstatus\fP \fIstatelist\fP
+\fIstatuslist\fP is a comma separated list of the connection statuses to match.
+Possible statuses are listed below.
+.TP
+[\fB!\fP] \fB\-\-ctexpire\fP \fItime\fP[\fB:\fP\fItime\fP]
+Match remaining lifetime in seconds against given value or range of values
+(inclusive)
+.TP
+\fB\-\-ctdir\fP {\fBORIGINAL\fP|\fBREPLY\fP}
+Match packets that are flowing in the specified direction. If this flag is not
+specified at all, matches packets in both directions.
+.PP
+States for \fB\-\-ctstate\fP:
+.TP
+\fBINVALID\fP
+The packet is associated with no known connection.
+.TP
+\fBNEW\fP
+The packet has started a new connection or otherwise associated
+with a connection which has not seen packets in both directions.
+.TP
+\fBESTABLISHED\fP
+The packet is associated with a connection which has seen packets
+in both directions.
+.TP
+\fBRELATED\fP
+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
+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
+destination.
+.TP
+\fBDNAT\fP
+A virtual state, matching if the original destination differs from the reply
+source.
+.PP
+Statuses for \fB\-\-ctstatus\fP:
+.TP
+\fBNONE\fP
+None of the below.
+.TP
+\fBEXPECTED\fP
+This is an expected connection (i.e. a conntrack helper set it up).
+.TP
+\fBSEEN_REPLY\fP
+Conntrack has seen packets in both directions.
+.TP
+\fBASSURED\fP
+Conntrack entry should never be early-expired.
+.TP
+\fBCONFIRMED\fP
+Connection is confirmed: originating packet has left box.
diff --git a/extensions/libxt_cpu.c b/extensions/libxt_cpu.c
new file mode 100644
index 0000000..404a6a6
--- /dev/null
+++ b/extensions/libxt_cpu.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_cpu.h>
+
+enum {
+	O_CPU = 0,
+};
+
+static void cpu_help(void)
+{
+	printf(
+"cpu match options:\n"
+"[!] --cpu number   Match CPU number\n");
+}
+
+static const struct xt_option_entry cpu_opts[] = {
+	{.name = "cpu", .id = O_CPU, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_MAND | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_cpu_info, cpu)},
+	XTOPT_TABLEEND,
+};
+
+static void cpu_parse(struct xt_option_call *cb)
+{
+	struct xt_cpu_info *cpuinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		cpuinfo->invert = true;
+}
+
+static void
+cpu_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_cpu_info *info = (void *)match->data;
+
+	printf(" cpu %s%u", info->invert ? "! ":"", info->cpu);
+}
+
+static void cpu_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_cpu_info *info = (void *)match->data;
+
+	printf("%s --cpu %u", info->invert ? " !" : "", info->cpu);
+}
+
+static struct xtables_match cpu_match = {
+	.family		= NFPROTO_UNSPEC,
+ 	.name		= "cpu",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_cpu_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_cpu_info)),
+	.help		= cpu_help,
+	.print		= cpu_print,
+	.save		= cpu_save,
+	.x6_parse	= cpu_parse,
+	.x6_options	= cpu_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&cpu_match);
+}
diff --git a/extensions/libxt_cpu.man b/extensions/libxt_cpu.man
new file mode 100644
index 0000000..d9ea5c2
--- /dev/null
+++ b/extensions/libxt_cpu.man
@@ -0,0 +1,15 @@
+.TP
+[\fB!\fP] \fB\-\-cpu\fP \fInumber\fP
+Match cpu handling this packet. cpus are numbered from 0 to NR_CPUS-1
+Can be used in combination with RPS (Remote Packet Steering) or
+multiqueue NICs to spread network traffic on different queues.
+.PP
+Example:
+.PP
+iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 0 
+\-j REDIRECT \-\-to\-port 8080
+.PP
+iptables \-t nat \-A PREROUTING \-p tcp \-\-dport 80 \-m cpu \-\-cpu 1 
+\-j REDIRECT \-\-to\-port 8081
+.PP
+Available since Linux 2.6.36.
diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c
new file mode 100644
index 0000000..a35cabb
--- /dev/null
+++ b/extensions/libxt_dccp.c
@@ -0,0 +1,296 @@
+/* Shared library add-on to iptables for DCCP matching
+ *
+ * (C) 2005 by Harald Welte <laforge@netfilter.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <xtables.h>
+#include <linux/dccp.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_dccp.h>
+
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+enum {
+	O_SOURCE_PORT = 0,
+	O_DEST_PORT,
+	O_DCCP_TYPES,
+	O_DCCP_OPTION,
+};
+
+static void dccp_help(void)
+{
+	printf(
+"dccp match options\n"
+"[!] --source-port port[:port]                          match source port(s)\n"
+" --sport ...\n"
+"[!] --destination-port port[:port]                     match destination port(s)\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
+static const struct xt_option_entry dccp_opts[] = {
+	{.name = "source-port", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "sport", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "destination-port", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .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,
+	 .flags = XTOPT_INVERT},
+	{.name = "dccp-option", .id = O_DCCP_OPTION, .type = XTTYPE_UINT8,
+	 .min = 1, .max = UINT8_MAX, .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, option)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static const char *const dccp_pkt_types[] = {
+	[DCCP_PKT_REQUEST] 	= "REQUEST",
+	[DCCP_PKT_RESPONSE]	= "RESPONSE",
+	[DCCP_PKT_DATA]		= "DATA",
+	[DCCP_PKT_ACK]		= "ACK",
+	[DCCP_PKT_DATAACK]	= "DATAACK",
+	[DCCP_PKT_CLOSEREQ]	= "CLOSEREQ",
+	[DCCP_PKT_CLOSE]	= "CLOSE",
+	[DCCP_PKT_RESET]	= "RESET",
+	[DCCP_PKT_SYNC]		= "SYNC",
+	[DCCP_PKT_SYNCACK]	= "SYNCACK",
+	[DCCP_PKT_INVALID]	= "INVALID",
+};
+
+static uint16_t
+parse_dccp_types(const char *typestring)
+{
+	uint16_t typemask = 0;
+	char *ptr, *buffer;
+
+	buffer = strdup(typestring);
+
+	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+		unsigned int i;
+		for (i = 0; i < ARRAY_SIZE(dccp_pkt_types); ++i)
+			if (!strcasecmp(dccp_pkt_types[i], ptr)) {
+				typemask |= (1 << i);
+				break;
+			}
+		if (i == ARRAY_SIZE(dccp_pkt_types))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Unknown DCCP type `%s'", ptr);
+	}
+
+	free(buffer);
+	return typemask;
+}
+
+static void dccp_parse(struct xt_option_call *cb)
+{
+	struct xt_dccp_info *einfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORT:
+		einfo->flags |= XT_DCCP_SRC_PORTS;
+		if (cb->invert)
+			einfo->invflags |= XT_DCCP_SRC_PORTS;
+		break;
+	case O_DEST_PORT:
+		einfo->flags |= XT_DCCP_DEST_PORTS;
+		if (cb->invert)
+			einfo->invflags |= XT_DCCP_DEST_PORTS;
+		break;
+	case O_DCCP_TYPES:
+		einfo->flags |= XT_DCCP_TYPE;
+		einfo->typemask = parse_dccp_types(cb->arg);
+		if (cb->invert)
+			einfo->invflags |= XT_DCCP_TYPE;
+		break;
+	case O_DCCP_OPTION:
+		einfo->flags |= XT_DCCP_OPTION;
+		if (cb->invert)
+			einfo->invflags |= XT_DCCP_OPTION;
+		break;
+	}
+}
+
+static const char *
+port_to_service(int port)
+{
+	const struct servent *service;
+
+	if ((service = getservbyport(htons(port), "dccp")))
+		return service->s_name;
+
+	return NULL;
+}
+
+static void
+print_port(uint16_t port, int numeric)
+{
+	const char *service;
+
+	if (numeric || (service = port_to_service(port)) == NULL)
+		printf("%u", port);
+	else
+		printf("%s", service);
+}
+
+static void
+print_ports(const char *name, uint16_t min, uint16_t max,
+	    int invert, int numeric)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFF || invert) {
+		printf(" %s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			print_port(min, numeric);
+		} else {
+			printf("s:%s", inv);
+			print_port(min, numeric);
+			printf(":");
+			print_port(max, numeric);
+		}
+	}
+}
+
+static void
+print_types(uint16_t types, int inverted, int numeric)
+{
+	int have_type = 0;
+
+	if (inverted)
+		printf(" !");
+
+	printf(" ");
+	while (types) {
+		unsigned int i;
+
+		for (i = 0; !(types & (1 << i)); i++);
+
+		if (have_type)
+			printf(",");
+		else
+			have_type = 1;
+
+		if (numeric)
+			printf("%u", i);
+		else
+			printf("%s", dccp_pkt_types[i]);
+
+		types &= ~(1 << i);
+	}
+}
+
+static void
+print_option(uint8_t option, int invert, int numeric)
+{
+	if (option || invert)
+		printf(" option=%s%u", invert ? "!" : "", option);
+}
+
+static void
+dccp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_dccp_info *einfo =
+		(const struct xt_dccp_info *)match->data;
+
+	printf(" dccp");
+
+	if (einfo->flags & XT_DCCP_SRC_PORTS) {
+		print_ports("spt", einfo->spts[0], einfo->spts[1],
+			einfo->invflags & XT_DCCP_SRC_PORTS,
+			numeric);
+	}
+
+	if (einfo->flags & XT_DCCP_DEST_PORTS) {
+		print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+			einfo->invflags & XT_DCCP_DEST_PORTS,
+			numeric);
+	}
+
+	if (einfo->flags & XT_DCCP_TYPE) {
+		print_types(einfo->typemask,
+			   einfo->invflags & XT_DCCP_TYPE,
+			   numeric);
+	}
+
+	if (einfo->flags & XT_DCCP_OPTION) {
+		print_option(einfo->option,
+			     einfo->invflags & XT_DCCP_OPTION, numeric);
+	}
+}
+
+static void dccp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_dccp_info *einfo =
+		(const struct xt_dccp_info *)match->data;
+
+	if (einfo->flags & XT_DCCP_SRC_PORTS) {
+		if (einfo->invflags & XT_DCCP_SRC_PORTS)
+			printf(" !");
+		if (einfo->spts[0] != einfo->spts[1])
+			printf(" --sport %u:%u",
+			       einfo->spts[0], einfo->spts[1]);
+		else
+			printf(" --sport %u", einfo->spts[0]);
+	}
+
+	if (einfo->flags & XT_DCCP_DEST_PORTS) {
+		if (einfo->invflags & XT_DCCP_DEST_PORTS)
+			printf(" !");
+		if (einfo->dpts[0] != einfo->dpts[1])
+			printf(" --dport %u:%u",
+			       einfo->dpts[0], einfo->dpts[1]);
+		else
+			printf(" --dport %u", einfo->dpts[0]);
+	}
+
+	if (einfo->flags & XT_DCCP_TYPE) {
+		printf("%s --dccp-types",
+		       einfo->invflags & XT_DCCP_TYPE ? " !" : "");
+		print_types(einfo->typemask, false, 0);
+	}
+
+	if (einfo->flags & XT_DCCP_OPTION) {
+		printf("%s --dccp-option %u",
+			einfo->invflags & XT_DCCP_OPTION ? " !" : "",
+			einfo->option);
+	}
+}
+
+static struct xtables_match dccp_match = {
+	.name		= "dccp",
+	.family		= NFPROTO_UNSPEC,
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_dccp_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_dccp_info)),
+	.help		= dccp_help,
+	.print		= dccp_print,
+	.save		= dccp_save,
+	.x6_parse	= dccp_parse,
+	.x6_options	= dccp_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&dccp_match);
+}
diff --git a/extensions/libxt_dccp.man b/extensions/libxt_dccp.man
new file mode 100644
index 0000000..71beb4b
--- /dev/null
+++ b/extensions/libxt_dccp.man
@@ -0,0 +1,12 @@
+.TP
+[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-dccp\-types\fP \fImask\fP
+Match when the DCCP packet type is one of 'mask'. 'mask' is a comma-separated
+list of packet types.  Packet types are: 
+.BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" .
+.TP
+[\fB!\fP] \fB\-\-dccp\-option\fP \fInumber\fP
+Match if DCCP option set.
diff --git a/extensions/libxt_devgroup.c b/extensions/libxt_devgroup.c
new file mode 100644
index 0000000..4a69c82
--- /dev/null
+++ b/extensions/libxt_devgroup.c
@@ -0,0 +1,172 @@
+/* Shared library add-on to iptables to add devgroup matching support.
+ *
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_devgroup.h>
+
+static void devgroup_help(void)
+{
+	printf(
+"devgroup match options:\n"
+"[!] --src-group value[/mask]	Match device group of incoming device\n"
+"[!] --dst-group value[/mask]	Match device group of outgoing device\n"
+		);
+}
+
+enum {
+	O_SRC_GROUP = 0,
+	O_DST_GROUP,
+};
+
+static const struct xt_option_entry devgroup_opts[] = {
+	{.name = "src-group", .id = O_SRC_GROUP, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "dst-group", .id = O_DST_GROUP, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+/* array of devgroups from /etc/iproute2/group_map */
+static struct xtables_lmap *devgroups;
+
+static void devgroup_init(struct xt_entry_match *match)
+{
+	const char file[] = "/etc/iproute2/group_map";
+	devgroups = xtables_lmap_init(file);
+	if (devgroups == NULL && errno != ENOENT)
+		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, mask;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SRC_GROUP:
+		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:
+		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;
+		break;
+	}
+}
+
+static void
+print_devgroup(unsigned int id, unsigned int mask, int numeric)
+{
+	const char *name = NULL;
+
+	if (mask != 0xffffffff)
+		printf("0x%x/0x%x", id, mask);
+	else {
+		if (numeric == 0)
+			name = xtables_lmap_id2name(devgroups, id);
+		if (name)
+			printf("%s", name);
+		else
+			printf("0x%x", id);
+	}
+}
+
+static void devgroup_show(const char *pfx, const struct xt_devgroup_info *info,
+			  int numeric)
+{
+	if (info->flags & XT_DEVGROUP_MATCH_SRC) {
+		if (info->flags & XT_DEVGROUP_INVERT_SRC)
+			printf(" !");
+		printf(" %ssrc-group ", pfx);
+		print_devgroup(info->src_group, info->src_mask, numeric);
+	}
+
+	if (info->flags & XT_DEVGROUP_MATCH_DST) {
+		if (info->flags & XT_DEVGROUP_INVERT_DST)
+			printf(" !");
+		printf(" %sdst-group ", pfx);
+		print_devgroup(info->src_group, info->src_mask, numeric);
+	}
+}
+
+static void devgroup_print(const void *ip, const struct xt_entry_match *match,
+                        int numeric)
+{
+	const struct xt_devgroup_info *info = (const void *)match->data;
+
+	devgroup_show("", info, numeric);
+}
+
+static void devgroup_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_devgroup_info *info = (const void *)match->data;
+
+	devgroup_show("--", info, 0);
+}
+
+static void devgroup_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+			      "devgroup match: You must specify either "
+			      "'--src-group' or '--dst-group'");
+}
+
+static struct xtables_match devgroup_mt_reg = {
+	.name		= "devgroup",
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_UNSPEC,
+	.size		= XT_ALIGN(sizeof(struct xt_devgroup_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_devgroup_info)),
+	.init		= devgroup_init,
+	.help		= devgroup_help,
+	.print		= devgroup_print,
+	.save		= devgroup_save,
+	.x6_parse	= devgroup_parse,
+	.x6_fcheck	= devgroup_check,
+	.x6_options	= devgroup_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&devgroup_mt_reg);
+}
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
new file mode 100644
index 0000000..02b22a4
--- /dev/null
+++ b/extensions/libxt_dscp.c
@@ -0,0 +1,111 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_dscp.c borrowed heavily from libipt_tos.c
+ *
+ * --class support added by Iain Barnes
+ * 
+ * For a list of DSCP codepoints see 
+ * http://www.iana.org/assignments/dscp-registry
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_dscp.h>
+
+/* This is evil, but it's my code - HW*/
+#include "dscp_helper.c"
+
+enum {
+	O_DSCP = 0,
+	O_DSCP_CLASS,
+	F_DSCP       = 1 << O_DSCP,
+	F_DSCP_CLASS = 1 << O_DSCP_CLASS,
+};
+
+static void dscp_help(void)
+{
+	printf(
+"dscp match options\n"
+"[!] --dscp value		Match DSCP codepoint with numerical value\n"
+"  		                This value can be in decimal (ex: 32)\n"
+"               		or in hex (ex: 0x20)\n"
+"[!] --dscp-class name		Match the DiffServ class. This value may\n"
+"				be any of the BE,EF, AFxx or CSx classes\n"
+"\n"
+"				These two options are mutually exclusive !\n");
+}
+
+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_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_dscp_info, dscp)},
+	{.name = "dscp-class", .id = O_DSCP_CLASS, .excl = F_DSCP,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void dscp_parse(struct xt_option_call *cb)
+{
+	struct xt_dscp_info *dinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_DSCP:
+		if (cb->invert)
+			dinfo->invert = 1;
+		break;
+	case O_DSCP_CLASS:
+		dinfo->dscp = class_to_dscp(cb->arg);
+		if (cb->invert)
+			dinfo->invert = 1;
+		break;
+	}
+}
+
+static void dscp_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "DSCP match: Parameter --dscp is required");
+}
+
+static void
+dscp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_dscp_info *dinfo =
+		(const struct xt_dscp_info *)match->data;
+	printf(" DSCP match %s0x%02x", dinfo->invert ? "!" : "", dinfo->dscp);
+}
+
+static void dscp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_dscp_info *dinfo =
+		(const struct xt_dscp_info *)match->data;
+
+	printf("%s --dscp 0x%02x", dinfo->invert ? " !" : "", dinfo->dscp);
+}
+
+static struct xtables_match dscp_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name 		= "dscp",
+	.version 	= XTABLES_VERSION,
+	.size 		= XT_ALIGN(sizeof(struct xt_dscp_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_dscp_info)),
+	.help		= dscp_help,
+	.print		= dscp_print,
+	.save		= dscp_save,
+	.x6_parse	= dscp_parse,
+	.x6_fcheck	= dscp_check,
+	.x6_options	= dscp_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&dscp_match);
+}
diff --git a/extensions/libxt_dscp.man b/extensions/libxt_dscp.man
new file mode 100644
index 0000000..63a17da
--- /dev/null
+++ b/extensions/libxt_dscp.man
@@ -0,0 +1,10 @@
+This module matches the 6 bit DSCP field within the TOS field in the
+IP header.  DSCP has superseded TOS within the IETF.
+.TP
+[\fB!\fP] \fB\-\-dscp\fP \fIvalue\fP
+Match against a numeric (decimal or hex) value [0-63].
+.TP
+[\fB!\fP] \fB\-\-dscp\-class\fP \fIclass\fP
+Match the DiffServ class. This value may be any of the
+BE, EF, AFxx or CSx classes.  It will then be converted
+into its according numeric value.
diff --git a/extensions/libxt_ecn.c b/extensions/libxt_ecn.c
new file mode 100644
index 0000000..286782a
--- /dev/null
+++ b/extensions/libxt_ecn.c
@@ -0,0 +1,138 @@
+/* Shared library add-on to iptables for ECN matching
+ *
+ * (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
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_ecn.h>
+
+enum {
+	O_ECN_TCP_CWR = 0,
+	O_ECN_TCP_ECE,
+	O_ECN_IP_ECT,
+};
+
+static void ecn_help(void)
+{
+	printf(
+"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/IPv6 header\n");
+}
+
+static const struct xt_option_entry ecn_opts[] = {
+	{.name = "ecn-tcp-cwr", .id = O_ECN_TCP_CWR, .type = XTTYPE_NONE,
+	 .flags = XTOPT_INVERT},
+	{.name = "ecn-tcp-ece", .id = O_ECN_TCP_ECE, .type = XTTYPE_NONE,
+	 .flags = XTOPT_INVERT},
+	{.name = "ecn-ip-ect", .id = O_ECN_IP_ECT, .type = XTTYPE_UINT8,
+	 .min = 0, .max = 3, .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void ecn_parse(struct xt_option_call *cb)
+{
+	struct xt_ecn_info *einfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_ECN_TCP_CWR:
+		einfo->operation |= XT_ECN_OP_MATCH_CWR;
+		if (cb->invert)
+			einfo->invert |= XT_ECN_OP_MATCH_CWR;
+		break;
+	case O_ECN_TCP_ECE:
+		einfo->operation |= XT_ECN_OP_MATCH_ECE;
+		if (cb->invert)
+			einfo->invert |= XT_ECN_OP_MATCH_ECE;
+		break;
+	case O_ECN_IP_ECT:
+		if (cb->invert)
+			einfo->invert |= XT_ECN_OP_MATCH_IP;
+		einfo->operation |= XT_ECN_OP_MATCH_IP;
+		einfo->ip_ect = cb->val.u8;
+		break;
+	}
+}
+
+static void ecn_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+		           "ECN match: some option required");
+}
+
+static void ecn_print(const void *ip, const struct xt_entry_match *match,
+                      int numeric)
+{
+	const struct xt_ecn_info *einfo =
+		(const struct xt_ecn_info *)match->data;
+
+	printf(" ECN match");
+
+	if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
+		printf(" %sECE",
+		       (einfo->invert & XT_ECN_OP_MATCH_ECE) ? "!" : "");
+	}
+
+	if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
+		printf(" %sCWR",
+		       (einfo->invert & XT_ECN_OP_MATCH_CWR) ? "!" : "");
+	}
+
+	if (einfo->operation & XT_ECN_OP_MATCH_IP) {
+		printf(" %sECT=%d",
+		       (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 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 & XT_ECN_OP_MATCH_CWR) {
+		if (einfo->invert & XT_ECN_OP_MATCH_CWR)
+			printf(" !");
+		printf(" --ecn-tcp-cwr");
+	}
+
+	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);
+	}
+}
+
+static struct xtables_match ecn_mt_reg = {
+	.name          = "ecn",
+	.version       = XTABLES_VERSION,
+	.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,
+	.x6_parse      = ecn_parse,
+	.x6_fcheck     = ecn_check,
+	.x6_options    = ecn_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&ecn_mt_reg);
+}
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_esp.c b/extensions/libxt_esp.c
new file mode 100644
index 0000000..294338b
--- /dev/null
+++ b/extensions/libxt_esp.c
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_esp.h>
+
+enum {
+	O_ESPSPI = 0,
+};
+
+static void esp_help(void)
+{
+	printf(
+"esp match options:\n"
+"[!] --espspi spi[:spi]\n"
+"				match spi (range)\n");
+}
+
+static const struct xt_option_entry esp_opts[] = {
+	{.name = "espspi", .id = O_ESPSPI, .type = XTTYPE_UINT32RC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_esp, spis)},
+	XTOPT_TABLEEND,
+};
+
+static void esp_parse(struct xt_option_call *cb)
+{
+	struct xt_esp *espinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->nvals == 1)
+		espinfo->spis[1] = espinfo->spis[0];
+	if (cb->invert)
+		espinfo->invflags |= XT_ESP_INV_SPI;
+}
+
+static void
+print_spis(const char *name, uint32_t min, uint32_t max,
+	    int invert)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFFFFFF || invert) {
+		if (min == max)
+			printf(" %s:%s%u", name, inv, min);
+		else
+			printf(" %ss:%s%u:%u", name, inv, min, max);
+	}
+}
+
+static void
+esp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_esp *esp = (struct xt_esp *)match->data;
+
+	printf(" esp");
+	print_spis("spi", esp->spis[0], esp->spis[1],
+		    esp->invflags & XT_ESP_INV_SPI);
+	if (esp->invflags & ~XT_ESP_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       esp->invflags & ~XT_ESP_INV_MASK);
+}
+
+static void esp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_esp *espinfo = (struct xt_esp *)match->data;
+
+	if (!(espinfo->spis[0] == 0
+	    && espinfo->spis[1] == 0xFFFFFFFF)) {
+		printf("%s --espspi ",
+			(espinfo->invflags & XT_ESP_INV_SPI) ? " !" : "");
+		if (espinfo->spis[0]
+		    != espinfo->spis[1])
+			printf("%u:%u",
+			       espinfo->spis[0],
+			       espinfo->spis[1]);
+		else
+			printf("%u",
+			       espinfo->spis[0]);
+	}
+
+}
+
+static struct xtables_match esp_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name 		= "esp",
+	.version 	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_esp)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_esp)),
+	.help		= esp_help,
+	.print		= esp_print,
+	.save		= esp_save,
+	.x6_parse	= esp_parse,
+	.x6_options	= esp_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&esp_match);
+}
diff --git a/extensions/libxt_esp.man b/extensions/libxt_esp.man
new file mode 100644
index 0000000..699a41c
--- /dev/null
+++ b/extensions/libxt_esp.man
@@ -0,0 +1,3 @@
+This module matches the SPIs in ESP header of IPsec packets.
+.TP
+[\fB!\fP] \fB\-\-espspi\fP \fIspi\fP[\fB:\fP\fIspi\fP]
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
new file mode 100644
index 0000000..c5b8d77
--- /dev/null
+++ b/extensions/libxt_hashlimit.c
@@ -0,0 +1,728 @@
+/* ip6tables match extension for limiting packets per destination
+ *
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * Based on ipt_limit.c by
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ * 
+ * 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>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+#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
+
+struct hashlimit_mt_udata {
+	uint32_t mult;
+};
+
+static void hashlimit_help(void)
+{
+	printf(
+"hashlimit match options:\n"
+"--hashlimit <avg>		max average match rate\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--hashlimit-mode <mode>		mode is a comma-separated list of\n"
+"					dstip,srcip,dstport,srcport\n"
+"--hashlimit-name <name>		name for /proc/net/ipt_hashlimit/\n"
+"[--hashlimit-burst <num>]	number to match in a burst, default %u\n"
+"[--hashlimit-htable-size <num>]	number of hashtable buckets\n"
+"[--hashlimit-htable-max <num>]	number of hashtable entries\n"
+"[--hashlimit-htable-gcinterval]	interval between garbage collection runs\n"
+"[--hashlimit-htable-expire]	after which time are idle entries expired?\n",
+XT_HASHLIMIT_BURST);
+}
+
+enum {
+	O_UPTO = 0,
+	O_ABOVE,
+	O_LIMIT,
+	O_MODE,
+	O_SRCMASK,
+	O_DSTMASK,
+	O_NAME,
+	O_BURST,
+	O_HTABLE_SIZE,
+	O_HTABLE_MAX,
+	O_HTABLE_GCINT,
+	O_HTABLE_EXPIRE,
+	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)
+{
+	printf(
+"hashlimit match options:\n"
+"  --hashlimit-upto <avg>           max average match rate\n"
+"                                   [Packets per second unless followed by \n"
+"                                   /sec /minute /hour /day postfixes]\n"
+"  --hashlimit-above <avg>          min average match rate\n"
+"  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
+"                                   dstip,srcip,dstport,srcport (or none)\n"
+"  --hashlimit-srcmask <length>     source address grouping prefix length\n"
+"  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
+"  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
+"  --hashlimit-burst <num>	    number to match in a burst, default %u\n"
+"  --hashlimit-htable-size <num>    number of hashtable buckets\n"
+"  --hashlimit-htable-max <num>     number of hashtable entries\n"
+"  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
+"  --hashlimit-htable-expire        after which time are idle entries expired?\n"
+"\n", XT_HASHLIMIT_BURST);
+}
+
+#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},
+	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
+	 .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,
+	 XTOPT_POINTER(s, cfg.size)},
+	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.max)},
+	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.gc_interval)},
+	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.expire)},
+	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+#define s struct xt_hashlimit_mtinfo1
+static const struct xt_option_entry hashlimit_mt_opts[] = {
+	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
+	 .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_STRING},
+	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.size)},
+	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.max)},
+	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.gc_interval)},
+	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.expire)},
+	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
+	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
+	XTOPT_TABLEEND,
+};
+#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, struct hashlimit_mt_udata *ud)
+{
+	const char *delim;
+	uint32_t r;
+
+	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)
+			ud->mult = 1;
+		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+			ud->mult = 60;
+		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+			ud->mult = 60*60;
+		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+			ud->mult = 24*60*60;
+		else
+			return 0;
+	}
+	r = atoi(rate);
+	if (!r)
+		return 0;
+
+	*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);
+	return 1;
+}
+
+static void hashlimit_init(struct xt_entry_match *m)
+{
+	struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
+
+	r->cfg.burst = XT_HASHLIMIT_BURST;
+	r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+
+}
+
+static void hashlimit_mt4_init(struct xt_entry_match *match)
+{
+	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
+
+	info->cfg.mode        = 0;
+	info->cfg.burst       = XT_HASHLIMIT_BURST;
+	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+	info->cfg.srcmask     = 32;
+	info->cfg.dstmask     = 32;
+}
+
+static void hashlimit_mt6_init(struct xt_entry_match *match)
+{
+	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
+
+	info->cfg.mode        = 0;
+	info->cfg.burst       = XT_HASHLIMIT_BURST;
+	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+	info->cfg.srcmask     = 128;
+	info->cfg.dstmask     = 128;
+}
+
+/* Parse a 'mode' parameter into the required bitmask */
+static int parse_mode(uint32_t *mode, const char *option_arg)
+{
+	char *tok;
+	char *arg = strdup(option_arg);
+
+	if (!arg)
+		return -1;
+
+	for (tok = strtok(arg, ",|");
+	     tok;
+	     tok = strtok(NULL, ",|")) {
+		if (!strcmp(tok, "dstip"))
+			*mode |= XT_HASHLIMIT_HASH_DIP;
+		else if (!strcmp(tok, "srcip"))
+			*mode |= XT_HASHLIMIT_HASH_SIP;
+		else if (!strcmp(tok, "srcport"))
+			*mode |= XT_HASHLIMIT_HASH_SPT;
+		else if (!strcmp(tok, "dstport"))
+			*mode |= XT_HASHLIMIT_HASH_DPT;
+		else {
+			free(arg);
+			return -1;
+		}
+	}
+	free(arg);
+	return 0;
+}
+
+static void hashlimit_parse(struct xt_option_call *cb)
+{
+	struct xt_hashlimit_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_UPTO:
+		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-upto", cb->arg);
+		break;
+	case O_MODE:
+		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-mode", cb->arg);
+		break;
+	}
+}
+
+static void hashlimit_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_hashlimit_mtinfo1 *info = cb->data;
+
+	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_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_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;
+	case O_MODE:
+		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-mode", cb->arg);
+		break;
+	case O_SRCMASK:
+		info->cfg.srcmask = cb->val.hlen;
+		break;
+	case O_DSTMASK:
+		info->cfg.dstmask = cb->val.hlen;
+		break;
+	}
+}
+
+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
+{
+	const char *name;
+	uint32_t mult;
+} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
+	      { "hour", XT_HASHLIMIT_SCALE*60*60 },
+	      { "min", XT_HASHLIMIT_SCALE*60 },
+	      { "sec", XT_HASHLIMIT_SCALE } };
+
+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)
+{
+	bool prevmode = false;
+
+	putchar(' ');
+	if (mode & XT_HASHLIMIT_HASH_SIP) {
+		fputs("srcip", stdout);
+		prevmode = 1;
+	}
+	if (mode & XT_HASHLIMIT_HASH_SPT) {
+		if (prevmode)
+			putchar(separator);
+		fputs("srcport", stdout);
+		prevmode = 1;
+	}
+	if (mode & XT_HASHLIMIT_HASH_DIP) {
+		if (prevmode)
+			putchar(separator);
+		fputs("dstip", stdout);
+		prevmode = 1;
+	}
+	if (mode & XT_HASHLIMIT_HASH_DPT) {
+		if (prevmode)
+			putchar(separator);
+		fputs("dstport", stdout);
+	}
+}
+
+static void hashlimit_print(const void *ip,
+                            const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_hashlimit_info *r = (const void *)match->data;
+	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, '-');
+	if (r->cfg.size)
+		printf(" htable-size %u", r->cfg.size);
+	if (r->cfg.max)
+		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 != 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);
+
+	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);
+		print_mode(info->cfg.mode, '-');
+	}
+	if (info->cfg.size != 0)
+		printf(" htable-size %u", info->cfg.size);
+	if (info->cfg.max != 0)
+		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 != quantum)
+		printf(" htable-expire %u", info->cfg.expire);
+
+	if (info->cfg.srcmask != dmask)
+		printf(" srcmask %u", info->cfg.srcmask);
+	if (info->cfg.dstmask != dmask)
+		printf(" dstmask %u", info->cfg.dstmask);
+}
+
+static void
+hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
+                   int numeric)
+{
+	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+
+	hashlimit_mt_print(info, 32);
+}
+
+static void
+hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
+                   int numeric)
+{
+	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+
+	hashlimit_mt_print(info, 128);
+}
+
+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);
+	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)
+		printf(" --hashlimit-htable-size %u", r->cfg.size);
+	if (r->cfg.max)
+		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 != 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);
+
+	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)) {
+		fputs(" --hashlimit-mode", stdout);
+		print_mode(info->cfg.mode, ',');
+	}
+
+	printf(" --hashlimit-name %s", info->name);
+
+	if (info->cfg.size != 0)
+		printf(" --hashlimit-htable-size %u", info->cfg.size);
+	if (info->cfg.max != 0)
+		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 != quantum)
+		printf(" --hashlimit-htable-expire %u", info->cfg.expire);
+
+	if (info->cfg.srcmask != dmask)
+		printf(" --hashlimit-srcmask %u", info->cfg.srcmask);
+	if (info->cfg.dstmask != dmask)
+		printf(" --hashlimit-dstmask %u", info->cfg.dstmask);
+}
+
+static void
+hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+
+	hashlimit_mt_save(info, 32);
+}
+
+static void
+hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+
+	hashlimit_mt_save(info, 128);
+}
+
+static struct xtables_match hashlimit_mt_reg[] = {
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "hashlimit",
+		.version       = XTABLES_VERSION,
+		.revision      = 0,
+		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
+		.userspacesize = offsetof(struct xt_hashlimit_info, hinfo),
+		.help          = hashlimit_help,
+		.init          = hashlimit_init,
+		.x6_parse      = hashlimit_parse,
+		.x6_fcheck     = hashlimit_check,
+		.print         = hashlimit_print,
+		.save          = hashlimit_save,
+		.x6_options    = hashlimit_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "hashlimit",
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
+		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
+		.help          = hashlimit_mt_help,
+		.init          = hashlimit_mt4_init,
+		.x6_parse      = hashlimit_mt_parse,
+		.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,
+		.name          = "hashlimit",
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
+		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
+		.help          = hashlimit_mt_help,
+		.init          = hashlimit_mt6_init,
+		.x6_parse      = hashlimit_mt_parse,
+		.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),
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
+}
diff --git a/extensions/libxt_hashlimit.man b/extensions/libxt_hashlimit.man
new file mode 100644
index 0000000..6aac3f2
--- /dev/null
+++ b/extensions/libxt_hashlimit.man
@@ -0,0 +1,76 @@
+\fBhashlimit\fP uses hash buckets to express a rate limiting match (like the
+\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" 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 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.
+.TP
+\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.  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
+\-\-hashlimit\-mode option is given, hashlimit acts like limit, but at the
+expensive of doing the hash housekeeping.
+.TP
+\fB\-\-hashlimit\-srcmask\fP \fIprefix\fP
+When \-\-hashlimit\-mode srcip is used, all source addresses encountered will be
+grouped according to the given prefix length and the so-created subnet will be
+subject to hashlimit. \fIprefix\fP must be between (inclusive) 0 and 32. Note
+that \-\-hashlimit\-srcmask 0 is basically doing the same thing as not specifying
+srcip for \-\-hashlimit\-mode, but is technically more expensive.
+.TP
+\fB\-\-hashlimit\-dstmask\fP \fIprefix\fP
+Like \-\-hashlimit\-srcmask, but for destination addresses.
+.TP
+\fB\-\-hashlimit\-name\fP \fIfoo\fP
+The name for the /proc/net/ipt_hashlimit/foo entry.
+.TP
+\fB\-\-hashlimit\-htable\-size\fP \fIbuckets\fP
+The number of buckets of the hash table
+.TP
+\fB\-\-hashlimit\-htable\-max\fP \fIentries\fP
+Maximum entries in the hash.
+.TP
+\fB\-\-hashlimit\-htable\-expire\fP \fImsec\fP
+After how many milliseconds do hash entries expire.
+.TP
+\fB\-\-hashlimit\-htable\-gcinterval\fP \fImsec\fP
+How many milliseconds between garbage collection intervals.
+.PP
+Examples:
+.TP
+matching on source host
+"1000 packets per second for every host in 192.168.0.0/16" =>
+\-s 192.168.0.0/16 \-\-hashlimit\-mode srcip \-\-hashlimit\-upto 1000/sec
+.TP
+matching on source port
+"100 packets per second for every service of 192.168.1.1" =>
+\-s 192.168.1.1 \-\-hashlimit\-mode srcport \-\-hashlimit\-upto 100/sec
+.TP
+matching on subnet
+"10000 packets per minute for every /28 subnet (groups of 8 addresses)
+in 10.0.0.0/8" =>
+\-s 10.0.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_helper.c b/extensions/libxt_helper.c
new file mode 100644
index 0000000..c9f9435
--- /dev/null
+++ b/extensions/libxt_helper.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_helper.h>
+
+enum {
+	O_HELPER = 0,
+};
+
+static void helper_help(void)
+{
+	printf(
+"helper match options:\n"
+"[!] --helper string        Match helper identified by string\n");
+}
+
+static const struct xt_option_entry helper_opts[] = {
+	{.name = "helper", .id = O_HELPER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_helper_info, name)},
+	XTOPT_TABLEEND,
+};
+
+static void helper_parse(struct xt_option_call *cb)
+{
+	struct xt_helper_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		info->invert = 1;
+}
+
+static void
+helper_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_helper_info *info = (const void *)match->data;
+
+	printf(" helper match %s\"%s\"", info->invert ? "! " : "", info->name);
+}
+
+static void helper_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_helper_info *info = (const void *)match->data;
+
+	printf("%s --helper", info->invert ? " !" : "");
+	xtables_save_string(info->name);
+}
+
+static struct xtables_match helper_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "helper",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_helper_info)),
+	.help		= helper_help,
+	.print		= helper_print,
+	.save		= helper_save,
+	.x6_parse	= helper_parse,
+	.x6_options	= helper_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&helper_match);
+}
diff --git a/extensions/libxt_helper.man b/extensions/libxt_helper.man
new file mode 100644
index 0000000..772b135
--- /dev/null
+++ b/extensions/libxt_helper.man
@@ -0,0 +1,11 @@
+This module matches packets related to a specific conntrack-helper.
+.TP
+[\fB!\fP] \fB\-\-helper\fP \fIstring\fP
+Matches packets related to the specified conntrack-helper.
+.RS
+.PP
+string can be "ftp" for packets related to a ftp-session on default port.
+For other ports append \-portnr to the value, ie. "ftp\-2121".
+.PP
+Same rules apply for other conntrack-helpers.
+.RE
diff --git a/extensions/libxt_iprange.c b/extensions/libxt_iprange.c
new file mode 100644
index 0000000..2c9ea99
--- /dev/null
+++ b/extensions/libxt_iprange.c
@@ -0,0 +1,347 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/xt_iprange.h>
+
+struct ipt_iprange {
+	/* Inclusive: network order. */
+	__be32 min_ip, max_ip;
+};
+
+struct ipt_iprange_info {
+	struct ipt_iprange src;
+	struct ipt_iprange dst;
+
+	/* Flags from above */
+	uint8_t flags;
+};
+
+enum {
+	O_SRC_RANGE = 0,
+	O_DST_RANGE,
+};
+
+static void iprange_mt_help(void)
+{
+	printf(
+"iprange match options:\n"
+"[!] --src-range ip[-ip]    Match source IP in the specified range\n"
+"[!] --dst-range ip[-ip]    Match destination IP in the specified range\n");
+}
+
+static const struct xt_option_entry iprange_mt_opts[] = {
+	{.name = "src-range", .id = O_SRC_RANGE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "dst-range", .id = O_DST_RANGE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void
+iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range,
+		   uint8_t family, const char *optname)
+{
+	const char *spec[2] = {from, to};
+	struct in6_addr *ia6;
+	struct in_addr *ia4;
+	unsigned int i;
+
+	memset(range, 0, sizeof(union nf_inet_addr) * 2);
+
+	if (family == NFPROTO_IPV6) {
+		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
+			ia6 = xtables_numeric_to_ip6addr(spec[i]);
+			if (ia6 == NULL)
+				xtables_param_act(XTF_BAD_VALUE, "iprange",
+					optname, spec[i]);
+			range[i].in6 = *ia6;
+		}
+	} else {
+		for (i = 0; i < ARRAY_SIZE(spec); ++i) {
+			ia4 = xtables_numeric_to_ipaddr(spec[i]);
+			if (ia4 == NULL)
+				xtables_param_act(XTF_BAD_VALUE, "iprange",
+					optname, spec[i]);
+			range[i].in = *ia4;
+		}
+	}
+}
+
+static void iprange_parse_range(const char *oarg, union nf_inet_addr *range,
+				uint8_t family, const char *optname)
+{
+	char *arg = strdup(oarg);
+	char *dash;
+
+	if (arg == NULL)
+		xtables_error(RESOURCE_PROBLEM, "strdup");
+	dash = strchr(arg, '-');
+	if (dash == NULL) {
+		iprange_parse_spec(arg, arg, range, family, optname);
+		free(arg);
+		return;
+	}
+
+	*dash = '\0';
+	iprange_parse_spec(arg, dash + 1, range, family, optname);
+	if (memcmp(&range[0], &range[1], sizeof(*range)) > 0)
+		fprintf(stderr, "xt_iprange: range %s-%s is reversed and "
+			"will never match\n", arg, dash + 1);
+	free(arg);
+}
+
+static void iprange_parse(struct xt_option_call *cb)
+{
+	struct ipt_iprange_info *info = cb->data;
+	union nf_inet_addr range[2];
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SRC_RANGE:
+		info->flags |= IPRANGE_SRC;
+		if (cb->invert)
+			info->flags |= IPRANGE_SRC_INV;
+		iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--src-range");
+		info->src.min_ip = range[0].ip;
+		info->src.max_ip = range[1].ip;
+		break;
+	case O_DST_RANGE:
+		info->flags |= IPRANGE_DST;
+		if (cb->invert)
+			info->flags |= IPRANGE_DST_INV;
+		iprange_parse_range(cb->arg, range, NFPROTO_IPV4, "--dst-range");
+		info->dst.min_ip = range[0].ip;
+		info->dst.max_ip = range[1].ip;
+		break;
+	}
+}
+
+static void iprange_mt_parse(struct xt_option_call *cb, uint8_t nfproto)
+{
+	struct xt_iprange_mtinfo *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SRC_RANGE:
+		iprange_parse_range(cb->arg, &info->src_min, nfproto,
+			"--src-range");
+		info->flags |= IPRANGE_SRC;
+		if (cb->invert)
+			info->flags |= IPRANGE_SRC_INV;
+		break;
+	case O_DST_RANGE:
+		iprange_parse_range(cb->arg, &info->dst_min, nfproto,
+			"--dst-range");
+		info->flags |= IPRANGE_DST;
+		if (cb->invert)
+			info->flags |= IPRANGE_DST_INV;
+		break;
+	}
+}
+
+static void iprange_mt4_parse(struct xt_option_call *cb)
+{
+	iprange_mt_parse(cb, NFPROTO_IPV4);
+}
+
+static void iprange_mt6_parse(struct xt_option_call *cb)
+{
+	iprange_mt_parse(cb, NFPROTO_IPV6);
+}
+
+static void iprange_mt_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+			   "iprange match: You must specify `--src-range' or `--dst-range'");
+}
+
+static void
+print_iprange(const struct ipt_iprange *range)
+{
+	const unsigned char *byte_min, *byte_max;
+
+	byte_min = (const unsigned char *)&range->min_ip;
+	byte_max = (const unsigned char *)&range->max_ip;
+	printf(" %u.%u.%u.%u-%u.%u.%u.%u",
+		byte_min[0], byte_min[1], byte_min[2], byte_min[3],
+		byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
+}
+
+static void iprange_print(const void *ip, const struct xt_entry_match *match,
+                          int numeric)
+{
+	const struct ipt_iprange_info *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		printf(" source IP range");
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		print_iprange(&info->src);
+	}
+	if (info->flags & IPRANGE_DST) {
+		printf(" destination IP range");
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		print_iprange(&info->dst);
+	}
+}
+
+static void
+iprange_mt4_print(const void *ip, const struct xt_entry_match *match,
+                  int numeric)
+{
+	const struct xt_iprange_mtinfo *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		printf(" source IP range");
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		/*
+		 * ipaddr_to_numeric() uses a static buffer, so cannot
+		 * combine the printf() calls.
+		 */
+		printf(" %s", xtables_ipaddr_to_numeric(&info->src_min.in));
+		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
+	}
+	if (info->flags & IPRANGE_DST) {
+		printf(" destination IP range");
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		printf(" %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
+		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
+	}
+}
+
+static void
+iprange_mt6_print(const void *ip, const struct xt_entry_match *match,
+                  int numeric)
+{
+	const struct xt_iprange_mtinfo *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		printf(" source IP range");
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		/*
+		 * ipaddr_to_numeric() uses a static buffer, so cannot
+		 * combine the printf() calls.
+		 */
+		printf(" %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
+		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
+	}
+	if (info->flags & IPRANGE_DST) {
+		printf(" destination IP range");
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		printf(" %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
+		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
+	}
+}
+
+static void iprange_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_iprange_info *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		printf(" --src-range");
+		print_iprange(&info->src);
+	}
+	if (info->flags & IPRANGE_DST) {
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		printf(" --dst-range");
+		print_iprange(&info->dst);
+	}
+}
+
+static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_iprange_mtinfo *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		printf(" --src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in));
+		printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in));
+	}
+	if (info->flags & IPRANGE_DST) {
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		printf(" --dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in));
+		printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in));
+	}
+}
+
+static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_iprange_mtinfo *info = (const void *)match->data;
+
+	if (info->flags & IPRANGE_SRC) {
+		if (info->flags & IPRANGE_SRC_INV)
+			printf(" !");
+		printf(" --src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6));
+		printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6));
+	}
+	if (info->flags & IPRANGE_DST) {
+		if (info->flags & IPRANGE_DST_INV)
+			printf(" !");
+		printf(" --dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6));
+		printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6));
+	}
+}
+
+static struct xtables_match iprange_mt_reg[] = {
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "iprange",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct ipt_iprange_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)),
+		.help          = iprange_mt_help,
+		.x6_parse      = iprange_parse,
+		.x6_fcheck     = iprange_mt_check,
+		.print         = iprange_print,
+		.save          = iprange_save,
+		.x6_options    = iprange_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "iprange",
+		.revision      = 1,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
+		.help          = iprange_mt_help,
+		.x6_parse      = iprange_mt4_parse,
+		.x6_fcheck     = iprange_mt_check,
+		.print         = iprange_mt4_print,
+		.save          = iprange_mt4_save,
+		.x6_options    = iprange_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "iprange",
+		.revision      = 1,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)),
+		.help          = iprange_mt_help,
+		.x6_parse      = iprange_mt6_parse,
+		.x6_fcheck     = iprange_mt_check,
+		.print         = iprange_mt6_print,
+		.save          = iprange_mt6_save,
+		.x6_options    = iprange_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
+}
diff --git a/extensions/libxt_iprange.man b/extensions/libxt_iprange.man
new file mode 100644
index 0000000..9bbaac3
--- /dev/null
+++ b/extensions/libxt_iprange.man
@@ -0,0 +1,7 @@
+This matches on a given arbitrary range of IP addresses.
+.TP
+[\fB!\fP] \fB\-\-src\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP]
+Match source IP in the specified range.
+.TP
+[\fB!\fP] \fB\-\-dst\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP]
+Match destination IP in the specified range.
diff --git a/extensions/libxt_ipvs.c b/extensions/libxt_ipvs.c
new file mode 100644
index 0000000..4672766
--- /dev/null
+++ b/extensions/libxt_ipvs.c
@@ -0,0 +1,285 @@
+/*
+ * Shared library add-on to iptables to add IPVS matching.
+ *
+ * Detailed doc is in the kernel module source net/netfilter/xt_ipvs.c
+ *
+ * Author: Hannes Eder <heder@google.com>
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/ip_vs.h>
+#include <linux/netfilter/xt_ipvs.h>
+
+enum {
+	/* For xt_ipvs: make sure this matches up with %XT_IPVS_*'s order */
+	O_IPVS = 0,
+	O_VPROTO,
+	O_VADDR,
+	O_VPORT,
+	O_VDIR,
+	O_VMETHOD,
+	O_VPORTCTL,
+};
+
+#define s struct xt_ipvs_mtinfo
+static const struct xt_option_entry ipvs_mt_opts[] = {
+	{.name = "ipvs", .id = O_IPVS, .type = XTTYPE_NONE,
+	 .flags = XTOPT_INVERT},
+	{.name = "vproto", .id = O_VPROTO, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, l4proto)},
+	{.name = "vaddr", .id = O_VADDR, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_INVERT},
+	{.name = "vport", .id = O_VPORT, .type = XTTYPE_PORT,
+	 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, vport)},
+	{.name = "vdir", .id = O_VDIR, .type = XTTYPE_STRING},
+	{.name = "vmethod", .id = O_VMETHOD, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "vportctl", .id = O_VPORTCTL, .type = XTTYPE_PORT,
+	 .flags = XTOPT_NBO | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, vportctl)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void ipvs_mt_help(void)
+{
+	printf(
+"IPVS match options:\n"
+"[!] --ipvs                      packet belongs to an IPVS connection\n"
+"\n"
+"Any of the following options implies --ipvs (even negated)\n"
+"[!] --vproto protocol           VIP protocol to match; by number or name,\n"
+"                                e.g. \"tcp\"\n"
+"[!] --vaddr address[/mask]      VIP address to match\n"
+"[!] --vport port                VIP port to match; by number or name,\n"
+"                                e.g. \"http\"\n"
+"    --vdir {ORIGINAL|REPLY}     flow direction of packet\n"
+"[!] --vmethod {GATE|IPIP|MASQ}  IPVS forwarding method used\n"
+"[!] --vportctl port             VIP port of the controlling connection to\n"
+"                                match, e.g. 21 for FTP\n"
+		);
+}
+
+static void ipvs_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_ipvs_mtinfo *data = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_VPROTO:
+		data->l4proto = cb->val.protocol;
+		break;
+	case O_VADDR:
+		memcpy(&data->vaddr, &cb->val.haddr, sizeof(cb->val.haddr));
+		memcpy(&data->vmask, &cb->val.hmask, sizeof(cb->val.hmask));
+		break;
+	case O_VDIR:
+		if (strcasecmp(cb->arg, "ORIGINAL") == 0) {
+			data->bitmask |= XT_IPVS_DIR;
+			data->invert   &= ~XT_IPVS_DIR;
+		} else if (strcasecmp(cb->arg, "REPLY") == 0) {
+			data->bitmask |= XT_IPVS_DIR;
+			data->invert  |= XT_IPVS_DIR;
+		} else {
+			xtables_param_act(XTF_BAD_VALUE,
+					  "ipvs", "--vdir", cb->arg);
+		}
+		break;
+	case O_VMETHOD:
+		if (strcasecmp(cb->arg, "GATE") == 0)
+			data->fwd_method = IP_VS_CONN_F_DROUTE;
+		else if (strcasecmp(cb->arg, "IPIP") == 0)
+			data->fwd_method = IP_VS_CONN_F_TUNNEL;
+		else if (strcasecmp(cb->arg, "MASQ") == 0)
+			data->fwd_method = IP_VS_CONN_F_MASQ;
+		else
+			xtables_param_act(XTF_BAD_VALUE,
+					  "ipvs", "--vmethod", cb->arg);
+		break;
+	}
+	data->bitmask |= 1 << cb->entry->id;
+	if (cb->invert)
+		data->invert |= 1 << cb->entry->id;
+}
+
+static void ipvs_mt_check(struct xt_fcheck_call *cb)
+{
+	struct xt_ipvs_mtinfo *info = cb->data;
+
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM,
+			      "IPVS: At least one option is required");
+	if (info->bitmask & XT_IPVS_ONCE_MASK) {
+		if (info->invert & XT_IPVS_IPVS_PROPERTY)
+			xtables_error(PARAMETER_PROBLEM,
+				      "! --ipvs cannot be together with"
+				      " other options");
+		info->bitmask |= XT_IPVS_IPVS_PROPERTY;
+	}
+}
+
+/* Shamelessly copied from libxt_conntrack.c */
+static void ipvs_mt_dump_addr(const union nf_inet_addr *addr,
+			      const union nf_inet_addr *mask,
+			      unsigned int family, bool numeric)
+{
+	char buf[BUFSIZ];
+
+	if (family == NFPROTO_IPV4) {
+		if (!numeric && addr->ip == 0) {
+			printf(" anywhere");
+			return;
+		}
+		if (numeric)
+			strcpy(buf, xtables_ipaddr_to_numeric(&addr->in));
+		else
+			strcpy(buf, xtables_ipaddr_to_anyname(&addr->in));
+		strcat(buf, xtables_ipmask_to_numeric(&mask->in));
+		printf(" %s", buf);
+	} else if (family == NFPROTO_IPV6) {
+		if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 &&
+		    addr->ip6[2] == 0 && addr->ip6[3] == 0) {
+			printf(" anywhere");
+			return;
+		}
+		if (numeric)
+			strcpy(buf, xtables_ip6addr_to_numeric(&addr->in6));
+		else
+			strcpy(buf, xtables_ip6addr_to_anyname(&addr->in6));
+		strcat(buf, xtables_ip6mask_to_numeric(&mask->in6));
+		printf(" %s", buf);
+	}
+}
+
+static void ipvs_mt_dump(const void *ip, const struct xt_ipvs_mtinfo *data,
+			 unsigned int family, bool numeric, const char *prefix)
+{
+	if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
+		if (data->invert & XT_IPVS_IPVS_PROPERTY)
+			printf(" !");
+		printf(" %sipvs", prefix);
+	}
+
+	if (data->bitmask & XT_IPVS_PROTO) {
+		if (data->invert & XT_IPVS_PROTO)
+			printf(" !");
+		printf(" %sproto %u", prefix, data->l4proto);
+	}
+
+	if (data->bitmask & XT_IPVS_VADDR) {
+		if (data->invert & XT_IPVS_VADDR)
+			printf(" !");
+
+		printf(" %svaddr", prefix);
+		ipvs_mt_dump_addr(&data->vaddr, &data->vmask, family, numeric);
+	}
+
+	if (data->bitmask & XT_IPVS_VPORT) {
+		if (data->invert & XT_IPVS_VPORT)
+			printf(" !");
+
+		printf(" %svport %u", prefix, ntohs(data->vport));
+	}
+
+	if (data->bitmask & XT_IPVS_DIR) {
+		if (data->invert & XT_IPVS_DIR)
+			printf(" %svdir REPLY", prefix);
+		else
+			printf(" %svdir ORIGINAL", prefix);
+	}
+
+	if (data->bitmask & XT_IPVS_METHOD) {
+		if (data->invert & XT_IPVS_METHOD)
+			printf(" !");
+
+		printf(" %svmethod", prefix);
+		switch (data->fwd_method) {
+		case IP_VS_CONN_F_DROUTE:
+			printf(" GATE");
+			break;
+		case IP_VS_CONN_F_TUNNEL:
+			printf(" IPIP");
+			break;
+		case IP_VS_CONN_F_MASQ:
+			printf(" MASQ");
+			break;
+		default:
+			/* Hu? */
+			printf(" UNKNOWN");
+			break;
+		}
+	}
+
+	if (data->bitmask & XT_IPVS_VPORTCTL) {
+		if (data->invert & XT_IPVS_VPORTCTL)
+			printf(" !");
+
+		printf(" %svportctl %u", prefix, ntohs(data->vportctl));
+	}
+}
+
+static void ipvs_mt4_print(const void *ip, const struct xt_entry_match *match,
+			   int numeric)
+{
+	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
+	ipvs_mt_dump(ip, data, NFPROTO_IPV4, numeric, "");
+}
+
+static void ipvs_mt6_print(const void *ip, const struct xt_entry_match *match,
+			   int numeric)
+{
+	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
+	ipvs_mt_dump(ip, data, NFPROTO_IPV6, numeric, "");
+}
+
+static void ipvs_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
+	ipvs_mt_dump(ip, data, NFPROTO_IPV4, true, "--");
+}
+
+static void ipvs_mt6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_ipvs_mtinfo *data = (const void *)match->data;
+	ipvs_mt_dump(ip, data, NFPROTO_IPV6, true, "--");
+}
+
+static struct xtables_match ipvs_matches_reg[] = {
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "ipvs",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
+		.help          = ipvs_mt_help,
+		.x6_parse      = ipvs_mt_parse,
+		.x6_fcheck     = ipvs_mt_check,
+		.print         = ipvs_mt4_print,
+		.save          = ipvs_mt4_save,
+		.x6_options    = ipvs_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "ipvs",
+		.revision      = 0,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
+		.help          = ipvs_mt_help,
+		.x6_parse      = ipvs_mt_parse,
+		.x6_fcheck     = ipvs_mt_check,
+		.print         = ipvs_mt6_print,
+		.save          = ipvs_mt6_save,
+		.x6_options    = ipvs_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(ipvs_matches_reg,
+				 ARRAY_SIZE(ipvs_matches_reg));
+}
diff --git a/extensions/libxt_ipvs.man b/extensions/libxt_ipvs.man
new file mode 100644
index 0000000..db9bc66
--- /dev/null
+++ b/extensions/libxt_ipvs.man
@@ -0,0 +1,24 @@
+Match IPVS connection properties.
+.TP
+[\fB!\fP] \fB\-\-ipvs\fP
+packet belongs to an IPVS connection
+.TP
+Any of the following options implies \-\-ipvs (even negated)
+.TP
+[\fB!\fP] \fB\-\-vproto\fP \fIprotocol\fP
+VIP protocol to match; by number or name, e.g. "tcp"
+.TP
+[\fB!\fP] \fB\-\-vaddr\fP \fIaddress\fP[\fB/\fP\fImask\fP]
+VIP address to match
+.TP
+[\fB!\fP] \fB\-\-vport\fP \fIport\fP
+VIP port to match; by number or name, e.g. "http"
+.TP
+\fB\-\-vdir\fP {\fBORIGINAL\fP|\fBREPLY\fP}
+flow direction of packet
+.TP
+[\fB!\fP] \fB\-\-vmethod\fP {\fBGATE\fP|\fBIPIP\fP|\fBMASQ\fP}
+IPVS forwarding method used
+.TP
+[\fB!\fP] \fB\-\-vportctl\fP \fIport\fP
+VIP port of the controlling connection to match, e.g. 21 for FTP
diff --git a/extensions/libxt_length.c b/extensions/libxt_length.c
new file mode 100644
index 0000000..6ea7646
--- /dev/null
+++ b/extensions/libxt_length.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_length.h>
+
+enum {
+	O_LENGTH = 0,
+};
+
+static void length_help(void)
+{
+	printf(
+"length match options:\n"
+"[!] --length length[:length]    Match packet length against value or range\n"
+"                                of values (inclusive)\n");
+}
+  
+static const struct xt_option_entry length_opts[] = {
+	{.name = "length", .id = O_LENGTH, .type = XTTYPE_UINT16RC,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void length_parse(struct xt_option_call *cb)
+{
+	struct xt_length_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	info->min = cb->val.u16_range[0];
+	info->max = cb->val.u16_range[0];
+	if (cb->nvals >= 2)
+		info->max = cb->val.u16_range[1];
+	if (cb->invert)
+		info->invert = 1;
+}
+
+static void
+length_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_length_info *info = (void *)match->data;
+
+	printf(" length %s", info->invert ? "!" : "");
+	if (info->min == info->max)
+		printf("%u", info->min);
+	else
+		printf("%u:%u", info->min, info->max);
+}
+
+static void length_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_length_info *info = (void *)match->data;
+
+	printf("%s --length ", info->invert ? " !" : "");
+	if (info->min == info->max)
+		printf("%u", info->min);
+	else
+		printf("%u:%u", info->min, info->max);
+}
+
+static struct xtables_match length_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "length",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_length_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_length_info)),
+	.help		= length_help,
+	.print		= length_print,
+	.save		= length_save,
+	.x6_parse	= length_parse,
+	.x6_options	= length_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&length_match);
+}
diff --git a/extensions/libxt_length.man b/extensions/libxt_length.man
new file mode 100644
index 0000000..07b6ea6
--- /dev/null
+++ b/extensions/libxt_length.man
@@ -0,0 +1,5 @@
+This module matches the length of the layer-3 payload (e.g. layer-4 packet)
+of a packet against a specific value
+or range of values.
+.TP
+[\fB!\fP] \fB\-\-length\fP \fIlength\fP[\fB:\fP\fIlength\fP]
diff --git a/extensions/libxt_limit.c b/extensions/libxt_limit.c
new file mode 100644
index 0000000..f75ef2f
--- /dev/null
+++ b/extensions/libxt_limit.c
@@ -0,0 +1,172 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * 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>
+#include <xtables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_limit.h>
+
+#define XT_LIMIT_AVG	"3/hour"
+#define XT_LIMIT_BURST	5
+
+enum {
+	O_LIMIT = 0,
+	O_BURST,
+};
+
+static void limit_help(void)
+{
+	printf(
+"limit match options:\n"
+"--limit avg			max average match rate: default "XT_LIMIT_AVG"\n"
+"                                [Packets per second unless followed by \n"
+"                                /sec /minute /hour /day postfixes]\n"
+"--limit-burst number		number to match in a burst, default %u\n",
+XT_LIMIT_BURST);
+}
+
+static const struct xt_option_entry limit_opts[] = {
+	{.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING},
+	{.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst),
+	 .min = 0, .max = 10000},
+	XTOPT_TABLEEND,
+};
+
+static
+int parse_rate(const char *rate, uint32_t *val)
+{
+	const char *delim;
+	uint32_t r;
+	uint32_t 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;
+		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+			mult = 60;
+		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+			mult = 60*60;
+		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+			mult = 24*60*60;
+		else
+			return 0;
+	}
+	r = atoi(rate);
+	if (!r)
+		return 0;
+
+	*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;
+}
+
+static void limit_init(struct xt_entry_match *m)
+{
+	struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
+
+	parse_rate(XT_LIMIT_AVG, &r->avg);
+	r->burst = XT_LIMIT_BURST;
+
+}
+
+/* FIXME: handle overflow:
+	if (r->avg*r->burst/r->burst != r->avg)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Sorry: burst too large for that avg rate.\n");
+*/
+
+static void limit_parse(struct xt_option_call *cb)
+{
+	struct xt_rateinfo *r = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_LIMIT:
+		if (!parse_rate(cb->arg, &r->avg))
+			xtables_error(PARAMETER_PROBLEM,
+				   "bad rate \"%s\"'", cb->arg);
+		break;
+	}
+	if (cb->invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "limit does not support invert");
+}
+
+static const struct rates
+{
+	const char *name;
+	uint32_t mult;
+} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
+	      { "hour", XT_LIMIT_SCALE*60*60 },
+	      { "min", XT_LIMIT_SCALE*60 },
+	      { "sec", XT_LIMIT_SCALE } };
+
+static void print_rate(uint32_t period)
+{
+	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)
+			break;
+
+	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
+}
+
+static void
+limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_rateinfo *r = (const void *)match->data;
+	printf(" limit: avg"); print_rate(r->avg);
+	printf(" burst %u", r->burst);
+}
+
+static void limit_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_rateinfo *r = (const void *)match->data;
+
+	printf(" --limit"); print_rate(r->avg);
+	if (r->burst != XT_LIMIT_BURST)
+		printf(" --limit-burst %u", r->burst);
+}
+
+static struct xtables_match limit_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "limit",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
+	.userspacesize	= offsetof(struct xt_rateinfo, prev),
+	.help		= limit_help,
+	.init		= limit_init,
+	.x6_parse	= limit_parse,
+	.print		= limit_print,
+	.save		= limit_save,
+	.x6_options	= limit_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&limit_match);
+}
diff --git a/extensions/libxt_limit.man b/extensions/libxt_limit.man
new file mode 100644
index 0000000..6fb94cc
--- /dev/null
+++ b/extensions/libxt_limit.man
@@ -0,0 +1,18 @@
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached.
+It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.PP
+xt_limit has no negation support - you will have to use \-m hashlimit !
+\-\-hashlimit \fIrate\fP in this case whilst omitting \-\-hashlimit\-mode.
+.TP
+\fB\-\-limit\fP \fIrate\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP]
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+\fB\-\-limit\-burst\fP \fInumber\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.
diff --git a/extensions/libxt_mac.c b/extensions/libxt_mac.c
new file mode 100644
index 0000000..f171d15
--- /dev/null
+++ b/extensions/libxt_mac.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <xtables.h>
+#include <linux/netfilter/xt_mac.h>
+
+enum {
+	O_MAC = 0,
+};
+
+static void mac_help(void)
+{
+	printf(
+"mac match options:\n"
+"[!] --mac-source XX:XX:XX:XX:XX:XX\n"
+"				Match source MAC address\n");
+}
+
+#define s struct xt_mac_info
+static const struct xt_option_entry mac_opts[] = {
+	{.name = "mac-source", .id = O_MAC, .type = XTTYPE_ETHERMAC,
+	 .flags = XTOPT_MAND | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, srcaddr)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void mac_parse(struct xt_option_call *cb)
+{
+	struct xt_mac_info *macinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		macinfo->invert = 1;
+}
+
+static void print_mac(const unsigned char *macaddress)
+{
+	unsigned int i;
+
+	printf(" %02X", macaddress[0]);
+	for (i = 1; i < ETH_ALEN; ++i)
+		printf(":%02X", macaddress[i]);
+}
+
+static void
+mac_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_mac_info *info = (void *)match->data;
+	printf(" MAC");
+
+	if (info->invert)
+		printf(" !");
+	
+	print_mac(info->srcaddr);
+}
+
+static void mac_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_mac_info *info = (void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	printf(" --mac-source");
+	print_mac(info->srcaddr);
+}
+
+static struct xtables_match mac_match = {
+	.family		= NFPROTO_UNSPEC,
+ 	.name		= "mac",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_mac_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_mac_info)),
+	.help		= mac_help,
+	.x6_parse	= mac_parse,
+	.print		= mac_print,
+	.save		= mac_save,
+	.x6_options	= mac_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&mac_match);
+}
diff --git a/extensions/libxt_mac.man b/extensions/libxt_mac.man
new file mode 100644
index 0000000..66072a2
--- /dev/null
+++ b/extensions/libxt_mac.man
@@ -0,0 +1,10 @@
+.TP
+[\fB!\fP] \fB\-\-mac\-source\fP \fIaddress\fP
+Match source MAC address.  It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
diff --git a/extensions/libxt_mark.c b/extensions/libxt_mark.c
new file mode 100644
index 0000000..7f8c995
--- /dev/null
+++ b/extensions/libxt_mark.c
@@ -0,0 +1,137 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_mark.h>
+
+struct xt_mark_info {
+	unsigned long mark, mask;
+	uint8_t invert;
+};
+
+enum {
+	O_MARK = 0,
+};
+
+static void mark_mt_help(void)
+{
+	printf(
+"mark match options:\n"
+"[!] --mark value[/mask]    Match nfmark value with optional mask\n");
+}
+
+static const struct xt_option_entry mark_mt_opts[] = {
+	{.name = "mark", .id = O_MARK, .type = XTTYPE_MARKMASK32,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void mark_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_mark_mtinfo1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		info->invert = true;
+	info->mark = cb->val.mark;
+	info->mask = cb->val.mask;
+}
+
+static void mark_parse(struct xt_option_call *cb)
+{
+	struct xt_mark_info *markinfo = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		markinfo->invert = 1;
+	markinfo->mark = cb->val.mark;
+	markinfo->mask = cb->val.mask;
+}
+
+static void print_mark(unsigned int mark, unsigned int mask)
+{
+	if (mask != 0xffffffffU)
+		printf(" 0x%x/0x%x", mark, mask);
+	else
+		printf(" 0x%x", mark);
+}
+
+static void
+mark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_mark_mtinfo1 *info = (const void *)match->data;
+
+	printf(" mark match");
+	if (info->invert)
+		printf(" !");
+	print_mark(info->mark, info->mask);
+}
+
+static void
+mark_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_mark_info *info = (const void *)match->data;
+
+	printf(" MARK match");
+
+	if (info->invert)
+		printf(" !");
+	
+	print_mark(info->mark, info->mask);
+}
+
+static void mark_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_mark_mtinfo1 *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	printf(" --mark");
+	print_mark(info->mark, info->mask);
+}
+
+static void
+mark_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_mark_info *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+	
+	printf(" --mark");
+	print_mark(info->mark, info->mask);
+}
+
+static struct xtables_match mark_mt_reg[] = {
+	{
+		.family        = NFPROTO_UNSPEC,
+		.name          = "mark",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_mark_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_info)),
+		.help          = mark_mt_help,
+		.print         = mark_print,
+		.save          = mark_save,
+		.x6_parse      = mark_parse,
+		.x6_options    = mark_mt_opts,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "mark",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_mark_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_mtinfo1)),
+		.help          = mark_mt_help,
+		.print         = mark_mt_print,
+		.save          = mark_mt_save,
+		.x6_parse      = mark_mt_parse,
+		.x6_options    = mark_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(mark_mt_reg, ARRAY_SIZE(mark_mt_reg));
+}
diff --git a/extensions/libxt_mark.man b/extensions/libxt_mark.man
new file mode 100644
index 0000000..264b17d
--- /dev/null
+++ b/extensions/libxt_mark.man
@@ -0,0 +1,9 @@
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+[\fB!\fP] \fB\-\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Matches packets with the given unsigned mark value (if a \fImask\fP is
+specified, this is logically ANDed with the \fImask\fP before the
+comparison).
diff --git a/extensions/libxt_multiport.c b/extensions/libxt_multiport.c
new file mode 100644
index 0000000..03af5a9
--- /dev/null
+++ b/extensions/libxt_multiport.c
@@ -0,0 +1,534 @@
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <xtables.h>
+#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/xt_multiport.h>
+
+enum {
+	O_SOURCE_PORTS = 0,
+	O_DEST_PORTS,
+	O_SD_PORTS,
+	F_SOURCE_PORTS = 1 << O_SOURCE_PORTS,
+	F_DEST_PORTS   = 1 << O_DEST_PORTS,
+	F_SD_PORTS     = 1 << O_SD_PORTS,
+	F_ANY          = F_SOURCE_PORTS | F_DEST_PORTS | F_SD_PORTS,
+};
+
+/* Function which prints out usage message. */
+static void multiport_help(void)
+{
+	printf(
+"multiport match options:\n"
+" --source-ports port[,port,port...]\n"
+" --sports ...\n"
+"				match source port(s)\n"
+" --destination-ports port[,port,port...]\n"
+" --dports ...\n"
+"				match destination port(s)\n"
+" --ports port[,port,port]\n"
+"				match both source and destination port(s)\n"
+" NOTE: this kernel does not support port ranges in multiport.\n");
+}
+
+static void multiport_help_v1(void)
+{
+	printf(
+"multiport match options:\n"
+"[!] --source-ports port[,port:port,port...]\n"
+" --sports ...\n"
+"				match source port(s)\n"
+"[!] --destination-ports port[,port:port,port...]\n"
+" --dports ...\n"
+"				match destination port(s)\n"
+"[!] --ports port[,port:port,port]\n"
+"				match both source and destination port(s)\n");
+}
+
+static const struct xt_option_entry multiport_opts[] = {
+	{.name = "source-ports", .id = O_SOURCE_PORTS, .type = XTTYPE_STRING,
+	 .excl = F_ANY, .flags = XTOPT_INVERT},
+	{.name = "sports", .id = O_SOURCE_PORTS, .type = XTTYPE_STRING,
+	 .excl = F_ANY, .flags = XTOPT_INVERT},
+	{.name = "destination-ports", .id = O_DEST_PORTS,
+	 .type = XTTYPE_STRING, .excl = F_ANY, .flags = XTOPT_INVERT},
+	{.name = "dports", .id = O_DEST_PORTS, .type = XTTYPE_STRING,
+	 .excl = F_ANY, .flags = XTOPT_INVERT},
+	{.name = "ports", .id = O_SD_PORTS, .type = XTTYPE_STRING,
+	 .excl = F_ANY, .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static const char *
+proto_to_name(uint8_t proto)
+{
+	switch (proto) {
+	case IPPROTO_TCP:
+		return "tcp";
+	case IPPROTO_UDP:
+		return "udp";
+	case IPPROTO_UDPLITE:
+		return "udplite";
+	case IPPROTO_SCTP:
+		return "sctp";
+	case IPPROTO_DCCP:
+		return "dccp";
+	default:
+		return NULL;
+	}
+}
+
+static unsigned int
+parse_multi_ports(const char *portstring, uint16_t *ports, const char *proto)
+{
+	char *buffer, *cp, *next;
+	unsigned int i;
+
+	buffer = strdup(portstring);
+	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
+
+	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next,i++)
+	{
+		next=strchr(cp, ',');
+		if (next) *next++='\0';
+		ports[i] = xtables_parse_port(cp, proto);
+	}
+	if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified");
+	free(buffer);
+	return i;
+}
+
+static void
+parse_multi_ports_v1(const char *portstring, 
+		     struct xt_multiport_v1 *multiinfo,
+		     const char *proto)
+{
+	char *buffer, *cp, *next, *range;
+	unsigned int i;
+	uint16_t m;
+
+	buffer = strdup(portstring);
+	if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed");
+
+	for (i=0; i<XT_MULTI_PORTS; i++)
+		multiinfo->pflags[i] = 0;
+ 
+	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) {
+		next=strchr(cp, ',');
+ 		if (next) *next++='\0';
+		range = strchr(cp, ':');
+		if (range) {
+			if (i == XT_MULTI_PORTS-1)
+				xtables_error(PARAMETER_PROBLEM,
+					   "too many ports specified");
+			*range++ = '\0';
+		}
+		multiinfo->ports[i] = xtables_parse_port(cp, proto);
+		if (range) {
+			multiinfo->pflags[i] = 1;
+			multiinfo->ports[++i] = xtables_parse_port(range, proto);
+			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
+				xtables_error(PARAMETER_PROBLEM,
+					   "invalid portrange specified");
+			m <<= 1;
+		}
+ 	}
+	multiinfo->count = i;
+	if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified");
+ 	free(buffer);
+}
+
+static const char *
+check_proto(uint16_t pnum, uint8_t invflags)
+{
+	const char *proto;
+
+	if (invflags & XT_INV_PROTO)
+		xtables_error(PARAMETER_PROBLEM,
+			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
+
+	if ((proto = proto_to_name(pnum)) != NULL)
+		return proto;
+	else if (!pnum)
+		xtables_error(PARAMETER_PROBLEM,
+			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
+			   "`-p sctp' or `-p dccp'");
+	else
+		xtables_error(PARAMETER_PROBLEM,
+			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
+}
+
+static void __multiport_parse(struct xt_option_call *cb, uint16_t pnum,
+			      uint8_t invflags)
+{
+	const char *proto;
+	struct xt_multiport *multiinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORTS:
+		proto = check_proto(pnum, invflags);
+		multiinfo->count = parse_multi_ports(cb->arg,
+						     multiinfo->ports, proto);
+		multiinfo->flags = XT_MULTIPORT_SOURCE;
+		break;
+	case O_DEST_PORTS:
+		proto = check_proto(pnum, invflags);
+		multiinfo->count = parse_multi_ports(cb->arg,
+						     multiinfo->ports, proto);
+		multiinfo->flags = XT_MULTIPORT_DESTINATION;
+		break;
+	case O_SD_PORTS:
+		proto = check_proto(pnum, invflags);
+		multiinfo->count = parse_multi_ports(cb->arg,
+						     multiinfo->ports, proto);
+		multiinfo->flags = XT_MULTIPORT_EITHER;
+		break;
+	}
+	if (cb->invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "multiport.0 does not support invert");
+}
+
+static void multiport_parse(struct xt_option_call *cb)
+{
+	const struct ipt_entry *entry = cb->xt_entry;
+	return __multiport_parse(cb,
+	       entry->ip.proto, entry->ip.invflags);
+}
+
+static void multiport_parse6(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	return __multiport_parse(cb,
+	       entry->ipv6.proto, entry->ipv6.invflags);
+}
+
+static void __multiport_parse_v1(struct xt_option_call *cb, uint16_t pnum,
+				 uint8_t invflags)
+{
+	const char *proto;
+	struct xt_multiport_v1 *multiinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORTS:
+		proto = check_proto(pnum, invflags);
+		parse_multi_ports_v1(cb->arg, multiinfo, proto);
+		multiinfo->flags = XT_MULTIPORT_SOURCE;
+		break;
+	case O_DEST_PORTS:
+		proto = check_proto(pnum, invflags);
+		parse_multi_ports_v1(cb->arg, multiinfo, proto);
+		multiinfo->flags = XT_MULTIPORT_DESTINATION;
+		break;
+	case O_SD_PORTS:
+		proto = check_proto(pnum, invflags);
+		parse_multi_ports_v1(cb->arg, multiinfo, proto);
+		multiinfo->flags = XT_MULTIPORT_EITHER;
+		break;
+	}
+	if (cb->invert)
+		multiinfo->invert = 1;
+}
+
+static void multiport_parse_v1(struct xt_option_call *cb)
+{
+	const struct ipt_entry *entry = cb->xt_entry;
+	return __multiport_parse_v1(cb,
+	       entry->ip.proto, entry->ip.invflags);
+}
+
+static void multiport_parse6_v1(struct xt_option_call *cb)
+{
+	const struct ip6t_entry *entry = cb->xt_entry;
+	return __multiport_parse_v1(cb,
+	       entry->ipv6.proto, entry->ipv6.invflags);
+}
+
+static void multiport_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, "multiport expection an option");
+}
+
+static const char *
+port_to_service(int port, uint8_t proto)
+{
+	const struct servent *service;
+
+	if ((service = getservbyport(htons(port), proto_to_name(proto))))
+		return service->s_name;
+
+	return NULL;
+}
+
+static void
+print_port(uint16_t port, uint8_t protocol, int numeric)
+{
+	const char *service;
+
+	if (numeric || (service = port_to_service(port, protocol)) == NULL)
+		printf("%u", port);
+	else
+		printf("%s", service);
+}
+
+static void
+__multiport_print(const struct xt_entry_match *match, int numeric,
+                  uint16_t proto)
+{
+	const struct xt_multiport *multiinfo
+		= (const struct xt_multiport *)match->data;
+	unsigned int i;
+
+	printf(" multiport ");
+
+	switch (multiinfo->flags) {
+	case XT_MULTIPORT_SOURCE:
+		printf("sports ");
+		break;
+
+	case XT_MULTIPORT_DESTINATION:
+		printf("dports ");
+		break;
+
+	case XT_MULTIPORT_EITHER:
+		printf("ports ");
+		break;
+
+	default:
+		printf("ERROR ");
+		break;
+	}
+
+	for (i=0; i < multiinfo->count; i++) {
+		printf("%s", i ? "," : "");
+		print_port(multiinfo->ports[i], proto, numeric);
+	}
+}
+
+static void multiport_print(const void *ip_void,
+                            const struct xt_entry_match *match, int numeric)
+{
+	const struct ipt_ip *ip = ip_void;
+	__multiport_print(match, numeric, ip->proto);
+}
+
+static void multiport_print6(const void *ip_void,
+                             const struct xt_entry_match *match, int numeric)
+{
+	const struct ip6t_ip6 *ip = ip_void;
+	__multiport_print(match, numeric, ip->proto);
+}
+
+static void __multiport_print_v1(const struct xt_entry_match *match,
+                                 int numeric, uint16_t proto)
+{
+	const struct xt_multiport_v1 *multiinfo
+		= (const struct xt_multiport_v1 *)match->data;
+	unsigned int i;
+
+	printf(" multiport ");
+
+	switch (multiinfo->flags) {
+	case XT_MULTIPORT_SOURCE:
+		printf("sports ");
+		break;
+
+	case XT_MULTIPORT_DESTINATION:
+		printf("dports ");
+		break;
+
+	case XT_MULTIPORT_EITHER:
+		printf("ports ");
+		break;
+
+	default:
+		printf("ERROR ");
+		break;
+	}
+
+	if (multiinfo->invert)
+		printf(" !");
+
+	for (i=0; i < multiinfo->count; i++) {
+		printf("%s", i ? "," : "");
+		print_port(multiinfo->ports[i], proto, numeric);
+		if (multiinfo->pflags[i]) {
+			printf(":");
+			print_port(multiinfo->ports[++i], proto, numeric);
+		}
+	}
+}
+
+static void multiport_print_v1(const void *ip_void,
+                               const struct xt_entry_match *match, int numeric)
+{
+	const struct ipt_ip *ip = ip_void;
+	__multiport_print_v1(match, numeric, ip->proto);
+}
+
+static void multiport_print6_v1(const void *ip_void,
+                                const struct xt_entry_match *match, int numeric)
+{
+	const struct ip6t_ip6 *ip = ip_void;
+	__multiport_print_v1(match, numeric, ip->proto);
+}
+
+static void __multiport_save(const struct xt_entry_match *match,
+                             uint16_t proto)
+{
+	const struct xt_multiport *multiinfo
+		= (const struct xt_multiport *)match->data;
+	unsigned int i;
+
+	switch (multiinfo->flags) {
+	case XT_MULTIPORT_SOURCE:
+		printf(" --sports ");
+		break;
+
+	case XT_MULTIPORT_DESTINATION:
+		printf(" --dports ");
+		break;
+
+	case XT_MULTIPORT_EITHER:
+		printf(" --ports ");
+		break;
+	}
+
+	for (i=0; i < multiinfo->count; i++) {
+		printf("%s", i ? "," : "");
+		print_port(multiinfo->ports[i], proto, 1);
+	}
+}
+
+static void multiport_save(const void *ip_void,
+                           const struct xt_entry_match *match)
+{
+	const struct ipt_ip *ip = ip_void;
+	__multiport_save(match, ip->proto);
+}
+
+static void multiport_save6(const void *ip_void,
+                            const struct xt_entry_match *match)
+{
+	const struct ip6t_ip6 *ip = ip_void;
+	__multiport_save(match, ip->proto);
+}
+
+static void __multiport_save_v1(const struct xt_entry_match *match,
+                                uint16_t proto)
+{
+	const struct xt_multiport_v1 *multiinfo
+		= (const struct xt_multiport_v1 *)match->data;
+	unsigned int i;
+
+	if (multiinfo->invert)
+		printf(" !");
+
+	switch (multiinfo->flags) {
+	case XT_MULTIPORT_SOURCE:
+		printf(" --sports ");
+		break;
+
+	case XT_MULTIPORT_DESTINATION:
+		printf(" --dports ");
+		break;
+
+	case XT_MULTIPORT_EITHER:
+		printf(" --ports ");
+		break;
+	}
+
+	for (i=0; i < multiinfo->count; i++) {
+		printf("%s", i ? "," : "");
+		print_port(multiinfo->ports[i], proto, 1);
+		if (multiinfo->pflags[i]) {
+			printf(":");
+			print_port(multiinfo->ports[++i], proto, 1);
+		}
+	}
+}
+
+static void multiport_save_v1(const void *ip_void,
+                              const struct xt_entry_match *match)
+{
+	const struct ipt_ip *ip = ip_void;
+	__multiport_save_v1(match, ip->proto);
+}
+
+static void multiport_save6_v1(const void *ip_void,
+                               const struct xt_entry_match *match)
+{
+	const struct ip6t_ip6 *ip = ip_void;
+	__multiport_save_v1(match, ip->proto);
+}
+
+static struct xtables_match multiport_mt_reg[] = {
+	{
+		.family        = NFPROTO_IPV4,
+		.name          = "multiport",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
+		.help          = multiport_help,
+		.x6_parse      = multiport_parse,
+		.x6_fcheck     = multiport_check,
+		.print         = multiport_print,
+		.save          = multiport_save,
+		.x6_options    = multiport_opts,
+	},
+	{
+		.family        = NFPROTO_IPV6,
+		.name          = "multiport",
+		.revision      = 0,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
+		.help          = multiport_help,
+		.x6_parse      = multiport_parse6,
+		.x6_fcheck     = multiport_check,
+		.print         = multiport_print6,
+		.save          = multiport_save6,
+		.x6_options    = multiport_opts,
+	},
+	{
+		.family        = NFPROTO_IPV4,
+		.name          = "multiport",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
+		.help          = multiport_help_v1,
+		.x6_parse      = multiport_parse_v1,
+		.x6_fcheck     = multiport_check,
+		.print         = multiport_print_v1,
+		.save          = multiport_save_v1,
+		.x6_options    = multiport_opts,
+	},
+	{
+		.family        = NFPROTO_IPV6,
+		.name          = "multiport",
+		.version       = XTABLES_VERSION,
+		.revision      = 1,
+		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
+		.help          = multiport_help_v1,
+		.x6_parse      = multiport_parse6_v1,
+		.x6_fcheck     = multiport_check,
+		.print         = multiport_print6_v1,
+		.save          = multiport_save6_v1,
+		.x6_options    = multiport_opts,
+	},
+};
+
+void
+_init(void)
+{
+	xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg));
+}
diff --git a/extensions/libxt_multiport.man b/extensions/libxt_multiport.man
new file mode 100644
index 0000000..7eb083e
--- /dev/null
+++ b/extensions/libxt_multiport.man
@@ -0,0 +1,22 @@
+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 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
+\fB\-\-sports\fP
+is a convenient alias for this option. Multiple ports or port ranges are
+separated using a comma, and a port range is specified using a colon.
+\fB53,1024:65535\fP would therefore match ports 53 and all from 1024 through
+65535.
+.TP
+[\fB!\fP] \fB\-\-destination\-ports\fP,\fB\-\-dports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]...
+Match if the destination port is one of the given ports.  The flag
+\fB\-\-dports\fP
+is a convenient alias for this option.
+.TP
+[\fB!\fP] \fB\-\-ports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]...
+Match if either the source or destination ports are equal to one of
+the given ports.
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
new file mode 100644
index 0000000..52dba47
--- /dev/null
+++ b/extensions/libxt_osf.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * xtables interface for OS fingerprint matching module.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <linux/netfilter/xt_osf.h>
+
+enum {
+	O_GENRE = 0,
+	O_TTL,
+	O_LOGLEVEL,
+};
+
+static void osf_help(void)
+{
+	printf("OS fingerprint match options:\n"
+		"[!] --genre string     Match a OS genre by passive fingerprinting.\n"
+		"--ttl level            Use some TTL check extensions to determine OS:\n"
+		"       0                       true ip and fingerprint TTL comparison. Works for LAN.\n"
+		"       1                       check if ip TTL is less than fingerprint one. Works for global addresses.\n"
+		"       2                       do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n"
+		"--log level            Log determined genres into dmesg even if they do not match desired one:\n"
+		"       0                       log all matched or unknown signatures.\n"
+		"       1                       log only first one.\n"
+		"       2                       log all known matched signatures.\n"
+		);
+}
+
+#define s struct xt_osf_info
+static const struct xt_option_entry osf_opts[] = {
+	{.name = "genre", .id = O_GENRE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(s, genre)},
+	{.name = "ttl", .id = O_TTL, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, ttl), .min = 0, .max = 2},
+	{.name = "log", .id = O_LOGLEVEL, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, loglevel), .min = 0, .max = 2},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void osf_parse(struct xt_option_call *cb)
+{
+	struct xt_osf_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+		case O_GENRE:
+			if (cb->invert)
+				info->flags |= XT_OSF_INVERT;
+			info->len = strlen(info->genre);
+			break;
+		case O_TTL:
+			info->flags |= XT_OSF_TTL;
+			break;
+		case O_LOGLEVEL:
+			info->flags |= XT_OSF_LOG;
+			break;
+	}
+}
+
+static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_osf_info *info = (const struct xt_osf_info*) match->data;
+
+	printf(" OS fingerprint match %s%s", (info->flags & XT_OSF_INVERT) ? "! " : "", info->genre);
+}
+
+static void osf_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_osf_info *info = (const struct xt_osf_info*) match->data;
+
+	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 = {
+	.name		= "osf",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_osf_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_osf_info)),
+	.help		= osf_help,
+	.x6_parse	= osf_parse,
+	.print		= osf_print,
+	.save		= osf_save,
+	.x6_options	= osf_opts,
+	.family		= NFPROTO_IPV4,
+};
+
+void _init(void)
+{
+	xtables_register_match(&osf_match);
+}
diff --git a/extensions/libxt_osf.man b/extensions/libxt_osf.man
new file mode 100644
index 0000000..f3a85fb
--- /dev/null
+++ b/extensions/libxt_osf.man
@@ -0,0 +1,45 @@
+The osf module does passive operating system fingerprinting. This modules
+compares some data (Window Size, MSS, options and their order, TTL, DF,
+and others) from packets with the SYN bit set. 
+.TP
+[\fB!\fP] \fB\-\-genre\fP \fIstring\fP
+Match an operating system genre by using a passive fingerprinting.
+.TP
+\fB\-\-ttl\fP \fIlevel\fP
+Do additional TTL checks on the packet to determine the operating system.
+\fIlevel\fP can be one of the following values:
+.IP \(bu 4
+0 - True IP address and fingerprint TTL comparison. This generally works for
+LANs.
+.IP \(bu 4
+1 - Check if the IP header's TTL is less than the fingerprint one. Works for
+globally-routable addresses.
+.IP \(bu 4
+2 - Do not compare the TTL at all.
+.TP
+\fB\-\-log\fP \fIlevel\fP
+Log determined genres into dmesg even if they do not match the desired one.
+\fIlevel\fP can be one of the following values:
+.IP \(bu 4
+0 - Log all matched or unknown signatures
+.IP \(bu 4
+1 - Log only the first one
+.IP \(bu 4
+2 - Log all known matched signatures
+.PP
+You may find something like this in syslog:
+.PP
+Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 ->
+11.22.33.44:139 hops=3 Linux [2.5-2.6:] : 1.2.3.4:42624 -> 1.2.3.5:22 hops=4
+.PP
+OS fingerprints are loadable using the \fBnfnl_osf\fP program. To load
+fingerprints from a file, use:
+.PP
+\fBnfnl_osf -f /usr/share/xtables/pf.os\fP
+.PP
+To remove them again,
+.PP
+\fBnfnl_osf -f /usr/share/xtables/pf.os -d\fP
+.PP
+The fingerprint database can be downlaoded from
+http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os .
diff --git a/extensions/libxt_owner.c b/extensions/libxt_owner.c
new file mode 100644
index 0000000..d9adc12
--- /dev/null
+++ b/extensions/libxt_owner.c
@@ -0,0 +1,543 @@
+/*
+ *	libxt_owner - iptables addon for xt_owner
+ *
+ *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
+ *	Jan Engelhardt <jengelh@computergmbh.de>
+ */
+#include <grp.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <limits.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_owner.h>
+
+/* match and invert flags */
+enum {
+	IPT_OWNER_UID   = 0x01,
+	IPT_OWNER_GID   = 0x02,
+	IPT_OWNER_PID   = 0x04,
+	IPT_OWNER_SID   = 0x08,
+	IPT_OWNER_COMM  = 0x10,
+	IP6T_OWNER_UID  = IPT_OWNER_UID,
+	IP6T_OWNER_GID  = IPT_OWNER_GID,
+	IP6T_OWNER_PID  = IPT_OWNER_PID,
+	IP6T_OWNER_SID  = IPT_OWNER_SID,
+	IP6T_OWNER_COMM = IPT_OWNER_COMM,
+};
+
+struct ipt_owner_info {
+	uid_t uid;
+	gid_t gid;
+	pid_t pid;
+	pid_t sid;
+	char comm[16];
+	uint8_t match, invert;	/* flags */
+};
+
+struct ip6t_owner_info {
+	uid_t uid;
+	gid_t gid;
+	pid_t pid;
+	pid_t sid;
+	char comm[16];
+	uint8_t match, invert;	/* flags */
+};
+
+/*
+ *	Note: "UINT32_MAX - 1" is used in the code because -1 is a reserved
+ *	UID/GID value anyway.
+ */
+
+enum {
+	O_USER = 0,
+	O_GROUP,
+	O_SOCK_EXISTS,
+	O_PROCESS,
+	O_SESSION,
+	O_COMM,
+};
+
+static void owner_mt_help_v0(void)
+{
+	printf(
+"owner match options:\n"
+"[!] --uid-owner userid       Match local UID\n"
+"[!] --gid-owner groupid      Match local GID\n"
+"[!] --pid-owner processid    Match local PID\n"
+"[!] --sid-owner sessionid    Match local SID\n"
+"[!] --cmd-owner name         Match local command name\n"
+"NOTE: PID, SID and command matching are broken on SMP\n");
+}
+
+static void owner_mt6_help_v0(void)
+{
+	printf(
+"owner match options:\n"
+"[!] --uid-owner userid       Match local UID\n"
+"[!] --gid-owner groupid      Match local GID\n"
+"[!] --pid-owner processid    Match local PID\n"
+"[!] --sid-owner sessionid    Match local SID\n"
+"NOTE: PID and SID matching are broken on SMP\n");
+}
+
+static void owner_mt_help(void)
+{
+	printf(
+"owner match options:\n"
+"[!] --uid-owner userid[-userid]      Match local UID\n"
+"[!] --gid-owner groupid[-groupid]    Match local GID\n"
+"[!] --socket-exists                  Match if socket exists\n");
+}
+
+#define s struct ipt_owner_info
+static const struct xt_option_entry owner_mt_opts_v0[] = {
+	{.name = "uid-owner", .id = O_USER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "gid-owner", .id = O_GROUP, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "pid-owner", .id = O_PROCESS, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, pid),
+	 .max = INT_MAX},
+	{.name = "sid-owner", .id = O_SESSION, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, sid),
+	 .max = INT_MAX},
+	{.name = "cmd-owner", .id = O_COMM, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, comm)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+#define s struct ip6t_owner_info
+static const struct xt_option_entry owner_mt6_opts_v0[] = {
+	{.name = "uid-owner", .id = O_USER, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "gid-owner", .id = O_GROUP, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT},
+	{.name = "pid-owner", .id = O_PROCESS, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, pid),
+	 .max = INT_MAX},
+	{.name = "sid-owner", .id = O_SESSION, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, sid),
+	 .max = INT_MAX},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static const struct xt_option_entry owner_mt_opts[] = {
+	{.name = "uid-owner", .id = O_USER, .type = XTTYPE_STRING,
+	 .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,
+	 .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void owner_mt_parse_v0(struct xt_option_call *cb)
+{
+	struct ipt_owner_info *info = cb->data;
+	struct passwd *pwd;
+	struct group *grp;
+	unsigned int id;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_USER:
+		if ((pwd = getpwnam(cb->arg)) != NULL)
+			id = pwd->pw_uid;
+		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
+			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", cb->arg);
+		if (cb->invert)
+			info->invert |= IPT_OWNER_UID;
+		info->match |= IPT_OWNER_UID;
+		info->uid    = id;
+		break;
+	case O_GROUP:
+		if ((grp = getgrnam(cb->arg)) != NULL)
+			id = grp->gr_gid;
+		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
+			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", cb->arg);
+		if (cb->invert)
+			info->invert |= IPT_OWNER_GID;
+		info->match |= IPT_OWNER_GID;
+		info->gid    = id;
+		break;
+	case O_PROCESS:
+		if (cb->invert)
+			info->invert |= IPT_OWNER_PID;
+		info->match |= IPT_OWNER_PID;
+		break;
+	case O_SESSION:
+		if (cb->invert)
+			info->invert |= IPT_OWNER_SID;
+		info->match |= IPT_OWNER_SID;
+		break;
+	case O_COMM:
+		if (cb->invert)
+			info->invert |= IPT_OWNER_COMM;
+		info->match |= IPT_OWNER_COMM;
+		break;
+	}
+}
+
+static void owner_mt6_parse_v0(struct xt_option_call *cb)
+{
+	struct ip6t_owner_info *info = cb->data;
+	struct passwd *pwd;
+	struct group *grp;
+	unsigned int id;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_USER:
+		if ((pwd = getpwnam(cb->arg)) != NULL)
+			id = pwd->pw_uid;
+		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
+			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", cb->arg);
+		if (cb->invert)
+			info->invert |= IP6T_OWNER_UID;
+		info->match |= IP6T_OWNER_UID;
+		info->uid    = id;
+		break;
+	case O_GROUP:
+		if ((grp = getgrnam(cb->arg)) != NULL)
+			id = grp->gr_gid;
+		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
+			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", cb->arg);
+		if (cb->invert)
+			info->invert |= IP6T_OWNER_GID;
+		info->match |= IP6T_OWNER_GID;
+		info->gid    = id;
+		break;
+	case O_PROCESS:
+		if (cb->invert)
+			info->invert |= IP6T_OWNER_PID;
+		info->match |= IP6T_OWNER_PID;
+		break;
+	case O_SESSION:
+		if (cb->invert)
+			info->invert |= IP6T_OWNER_SID;
+		info->match |= IP6T_OWNER_SID;
+		break;
+	}
+}
+
+static void owner_parse_range(const char *s, unsigned int *from,
+                              unsigned int *to, const char *opt)
+{
+	char *end;
+
+	/* -1 is reversed, so the max is one less than that. */
+	if (!xtables_strtoui(s, &end, from, 0, UINT32_MAX - 1))
+		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
+	*to = *from;
+	if (*end == '-' || *end == ':')
+		if (!xtables_strtoui(end + 1, &end, to, 0, UINT32_MAX - 1))
+			xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
+	if (*end != '\0')
+		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
+}
+
+static void owner_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_owner_match_info *info = cb->data;
+	struct passwd *pwd;
+	struct group *grp;
+	unsigned int from, to;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_USER:
+		if ((pwd = getpwnam(cb->arg)) != NULL)
+			from = to = pwd->pw_uid;
+		else
+			owner_parse_range(cb->arg, &from, &to, "--uid-owner");
+		if (cb->invert)
+			info->invert |= XT_OWNER_UID;
+		info->match  |= XT_OWNER_UID;
+		info->uid_min = from;
+		info->uid_max = to;
+		break;
+	case O_GROUP:
+		if ((grp = getgrnam(cb->arg)) != NULL)
+			from = to = grp->gr_gid;
+		else
+			owner_parse_range(cb->arg, &from, &to, "--gid-owner");
+		if (cb->invert)
+			info->invert |= XT_OWNER_GID;
+		info->match  |= XT_OWNER_GID;
+		info->gid_min = from;
+		info->gid_max = to;
+		break;
+	case O_SOCK_EXISTS:
+		if (cb->invert)
+			info->invert |= XT_OWNER_SOCKET;
+		info->match |= XT_OWNER_SOCKET;
+		break;
+	}
+}
+
+static void owner_mt_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, "owner: At least one of "
+		           "--uid-owner, --gid-owner or --socket-exists "
+		           "is required");
+}
+
+static void
+owner_mt_print_item_v0(const struct ipt_owner_info *info, const char *label,
+                       uint8_t flag, bool numeric)
+{
+	if (!(info->match & flag))
+		return;
+	if (info->invert & flag)
+		printf(" !");
+	printf(" %s", label);
+
+	switch (info->match & flag) {
+	case IPT_OWNER_UID:
+		if (!numeric) {
+			struct passwd *pwd = getpwuid(info->uid);
+
+			if (pwd != NULL && pwd->pw_name != NULL) {
+				printf(" %s", pwd->pw_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->uid);
+		break;
+
+	case IPT_OWNER_GID:
+		if (!numeric) {
+			struct group *grp = getgrgid(info->gid);
+
+			if (grp != NULL && grp->gr_name != NULL) {
+				printf(" %s", grp->gr_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->gid);
+		break;
+
+	case IPT_OWNER_PID:
+		printf(" %u", (unsigned int)info->pid);
+		break;
+
+	case IPT_OWNER_SID:
+		printf(" %u", (unsigned int)info->sid);
+		break;
+
+	case IPT_OWNER_COMM:
+		printf(" %.*s", (int)sizeof(info->comm), info->comm);
+		break;
+	}
+}
+
+static void
+owner_mt6_print_item_v0(const struct ip6t_owner_info *info, const char *label,
+                        uint8_t flag, bool numeric)
+{
+	if (!(info->match & flag))
+		return;
+	if (info->invert & flag)
+		printf(" !");
+	printf(" %s", label);
+
+	switch (info->match & flag) {
+	case IP6T_OWNER_UID:
+		if (!numeric) {
+			struct passwd *pwd = getpwuid(info->uid);
+
+			if (pwd != NULL && pwd->pw_name != NULL) {
+				printf(" %s", pwd->pw_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->uid);
+		break;
+
+	case IP6T_OWNER_GID:
+		if (!numeric) {
+			struct group *grp = getgrgid(info->gid);
+
+			if (grp != NULL && grp->gr_name != NULL) {
+				printf(" %s", grp->gr_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->gid);
+		break;
+
+	case IP6T_OWNER_PID:
+		printf(" %u", (unsigned int)info->pid);
+		break;
+
+	case IP6T_OWNER_SID:
+		printf(" %u", (unsigned int)info->sid);
+		break;
+	}
+}
+
+static void
+owner_mt_print_item(const struct xt_owner_match_info *info, const char *label,
+                    uint8_t flag, bool numeric)
+{
+	if (!(info->match & flag))
+		return;
+	if (info->invert & flag)
+		printf(" !");
+	printf(" %s", label);
+
+	switch (info->match & flag) {
+	case XT_OWNER_UID:
+		if (info->uid_min != info->uid_max) {
+			printf(" %u-%u", (unsigned int)info->uid_min,
+			       (unsigned int)info->uid_max);
+			break;
+		} else if (!numeric) {
+			const struct passwd *pwd = getpwuid(info->uid_min);
+
+			if (pwd != NULL && pwd->pw_name != NULL) {
+				printf(" %s", pwd->pw_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->uid_min);
+		break;
+
+	case XT_OWNER_GID:
+		if (info->gid_min != info->gid_max) {
+			printf(" %u-%u", (unsigned int)info->gid_min,
+			       (unsigned int)info->gid_max);
+			break;
+		} else if (!numeric) {
+			const struct group *grp = getgrgid(info->gid_min);
+
+			if (grp != NULL && grp->gr_name != NULL) {
+				printf(" %s", grp->gr_name);
+				break;
+			}
+		}
+		printf(" %u", (unsigned int)info->gid_min);
+		break;
+	}
+}
+
+static void
+owner_mt_print_v0(const void *ip, const struct xt_entry_match *match,
+                  int numeric)
+{
+	const struct ipt_owner_info *info = (void *)match->data;
+
+	owner_mt_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
+	owner_mt_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
+	owner_mt_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
+	owner_mt_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
+	owner_mt_print_item_v0(info, "owner CMD match", IPT_OWNER_COMM, numeric);
+}
+
+static void
+owner_mt6_print_v0(const void *ip, const struct xt_entry_match *match,
+                   int numeric)
+{
+	const struct ip6t_owner_info *info = (void *)match->data;
+
+	owner_mt6_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
+	owner_mt6_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
+	owner_mt6_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
+	owner_mt6_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
+}
+
+static void owner_mt_print(const void *ip, const struct xt_entry_match *match,
+                           int numeric)
+{
+	const struct xt_owner_match_info *info = (void *)match->data;
+
+	owner_mt_print_item(info, "owner socket exists", XT_OWNER_SOCKET, numeric);
+	owner_mt_print_item(info, "owner UID match",     XT_OWNER_UID,    numeric);
+	owner_mt_print_item(info, "owner GID match",     XT_OWNER_GID,    numeric);
+}
+
+static void
+owner_mt_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_owner_info *info = (void *)match->data;
+
+	owner_mt_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
+	owner_mt_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
+	owner_mt_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
+	owner_mt_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
+	owner_mt_print_item_v0(info, "--cmd-owner", IPT_OWNER_COMM, true);
+}
+
+static void
+owner_mt6_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ip6t_owner_info *info = (void *)match->data;
+
+	owner_mt6_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
+	owner_mt6_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
+	owner_mt6_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
+	owner_mt6_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
+}
+
+static void owner_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_owner_match_info *info = (void *)match->data;
+
+	owner_mt_print_item(info, "--socket-exists",  XT_OWNER_SOCKET, true);
+	owner_mt_print_item(info, "--uid-owner",      XT_OWNER_UID,    true);
+	owner_mt_print_item(info, "--gid-owner",      XT_OWNER_GID,    true);
+}
+
+static struct xtables_match owner_mt_reg[] = {
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "owner",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct ipt_owner_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct ipt_owner_info)),
+		.help          = owner_mt_help_v0,
+		.x6_parse      = owner_mt_parse_v0,
+		.x6_fcheck     = owner_mt_check,
+		.print         = owner_mt_print_v0,
+		.save          = owner_mt_save_v0,
+		.x6_options    = owner_mt_opts_v0,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "owner",
+		.revision      = 0,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct ip6t_owner_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct ip6t_owner_info)),
+		.help          = owner_mt6_help_v0,
+		.x6_parse      = owner_mt6_parse_v0,
+		.x6_fcheck     = owner_mt_check,
+		.print         = owner_mt6_print_v0,
+		.save          = owner_mt6_save_v0,
+		.x6_options    = owner_mt6_opts_v0,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "owner",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.size          = XT_ALIGN(sizeof(struct xt_owner_match_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_owner_match_info)),
+		.help          = owner_mt_help,
+		.x6_parse      = owner_mt_parse,
+		.x6_fcheck     = owner_mt_check,
+		.print         = owner_mt_print,
+		.save          = owner_mt_save,
+		.x6_options    = owner_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
+}
diff --git a/extensions/libxt_owner.man b/extensions/libxt_owner.man
new file mode 100644
index 0000000..49b58ce
--- /dev/null
+++ b/extensions/libxt_owner.man
@@ -0,0 +1,19 @@
+This module attempts to match various characteristics of the packet creator,
+for locally generated packets. This match is only valid in the OUTPUT and
+POSTROUTING chains. Forwarded packets do not have any socket associated with
+them. Packets from kernel threads do have a socket, but usually no owner.
+.TP
+[\fB!\fP] \fB\-\-uid\-owner\fP \fIusername\fP
+.TP
+[\fB!\fP] \fB\-\-uid\-owner\fP \fIuserid\fP[\fB\-\fP\fIuserid\fP]
+Matches if the packet socket's file structure (if it has one) is owned by the
+given user. You may also specify a numerical UID, or an UID range.
+.TP
+[\fB!\fP] \fB\-\-gid\-owner\fP \fIgroupname\fP
+.TP
+[\fB!\fP] \fB\-\-gid\-owner\fP \fIgroupid\fP[\fB\-\fP\fIgroupid\fP]
+Matches if the packet socket's file structure is owned by the given group.
+You may also specify a numerical GID, or a GID range.
+.TP
+[\fB!\fP] \fB\-\-socket\-exists\fP
+Matches if the packet is associated with a socket.
diff --git a/extensions/libxt_physdev.c b/extensions/libxt_physdev.c
new file mode 100644
index 0000000..a11faf4
--- /dev/null
+++ b/extensions/libxt_physdev.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_physdev.h>
+
+enum {
+	O_PHYSDEV_IN = 0,
+	O_PHYSDEV_OUT,
+	O_PHYSDEV_IS_IN,
+	O_PHYSDEV_IS_OUT,
+	O_PHYSDEV_IS_BRIDGED,
+};
+
+static void physdev_help(void)
+{
+	printf(
+"physdev match options:\n"
+" [!] --physdev-in inputname[+]		bridge port name ([+] for wildcard)\n"
+" [!] --physdev-out outputname[+]	bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in			arrived on a bridge device\n"
+" [!] --physdev-is-out			will leave on a bridge device\n"
+" [!] --physdev-is-bridged		it's a bridged packet\n");
+}
+
+#define s struct xt_physdev_info
+static const struct xt_option_entry physdev_opts[] = {
+	{.name = "physdev-in", .id = O_PHYSDEV_IN, .type = XTTYPE_STRING,
+	 .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,
+	 .flags = XTOPT_INVERT},
+	{.name = "physdev-is-out", .id = O_PHYSDEV_IS_OUT,
+	 .type = XTTYPE_NONE, .flags = XTOPT_INVERT},
+	{.name = "physdev-is-bridged", .id = O_PHYSDEV_IS_BRIDGED,
+	 .type = XTTYPE_NONE, .flags = XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void physdev_parse(struct xt_option_call *cb)
+{
+	struct xt_physdev_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_PHYSDEV_IN:
+		xtables_parse_interface(cb->arg, info->physindev,
+				(unsigned char *)info->in_mask);
+		if (cb->invert)
+			info->invert |= XT_PHYSDEV_OP_IN;
+		info->bitmask |= XT_PHYSDEV_OP_IN;
+		break;
+	case O_PHYSDEV_OUT:
+		xtables_parse_interface(cb->arg, info->physoutdev,
+				(unsigned char *)info->out_mask);
+		if (cb->invert)
+			info->invert |= XT_PHYSDEV_OP_OUT;
+		info->bitmask |= XT_PHYSDEV_OP_OUT;
+		break;
+	case O_PHYSDEV_IS_IN:
+		info->bitmask |= XT_PHYSDEV_OP_ISIN;
+		if (cb->invert)
+			info->invert |= XT_PHYSDEV_OP_ISIN;
+		break;
+	case O_PHYSDEV_IS_OUT:
+		info->bitmask |= XT_PHYSDEV_OP_ISOUT;
+		if (cb->invert)
+			info->invert |= XT_PHYSDEV_OP_ISOUT;
+		break;
+	case O_PHYSDEV_IS_BRIDGED:
+		if (cb->invert)
+			info->invert |= XT_PHYSDEV_OP_BRIDGED;
+		info->bitmask |= XT_PHYSDEV_OP_BRIDGED;
+		break;
+	}
+}
+
+static void physdev_check(struct xt_fcheck_call *cb)
+{
+	if (cb->xflags == 0)
+		xtables_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
+}
+
+static void
+physdev_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_physdev_info *info = (const void *)match->data;
+
+	printf(" PHYSDEV match");
+	if (info->bitmask & XT_PHYSDEV_OP_ISIN)
+		printf("%s --physdev-is-in",
+		       info->invert & XT_PHYSDEV_OP_ISIN ? " !":"");
+	if (info->bitmask & XT_PHYSDEV_OP_IN)
+		printf("%s --physdev-in %s",
+		(info->invert & XT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+	if (info->bitmask & XT_PHYSDEV_OP_ISOUT)
+		printf("%s --physdev-is-out",
+		       info->invert & XT_PHYSDEV_OP_ISOUT ? " !":"");
+	if (info->bitmask & XT_PHYSDEV_OP_OUT)
+		printf("%s --physdev-out %s",
+		(info->invert & XT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+	if (info->bitmask & XT_PHYSDEV_OP_BRIDGED)
+		printf("%s --physdev-is-bridged",
+		       info->invert & XT_PHYSDEV_OP_BRIDGED ? " !":"");
+}
+
+static void physdev_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_physdev_info *info = (const void *)match->data;
+
+	if (info->bitmask & XT_PHYSDEV_OP_ISIN)
+		printf("%s --physdev-is-in",
+		       (info->invert & XT_PHYSDEV_OP_ISIN) ? " !" : "");
+	if (info->bitmask & XT_PHYSDEV_OP_IN)
+		printf("%s --physdev-in %s",
+		       (info->invert & XT_PHYSDEV_OP_IN) ? " !" : "",
+		       info->physindev);
+
+	if (info->bitmask & XT_PHYSDEV_OP_ISOUT)
+		printf("%s --physdev-is-out",
+		       (info->invert & XT_PHYSDEV_OP_ISOUT) ? " !" : "");
+	if (info->bitmask & XT_PHYSDEV_OP_OUT)
+		printf("%s --physdev-out %s",
+		       (info->invert & XT_PHYSDEV_OP_OUT) ? " !" : "",
+		       info->physoutdev);
+	if (info->bitmask & XT_PHYSDEV_OP_BRIDGED)
+		printf("%s --physdev-is-bridged",
+		       (info->invert & XT_PHYSDEV_OP_BRIDGED) ? " !" : "");
+}
+
+static struct xtables_match physdev_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "physdev",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_physdev_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_physdev_info)),
+	.help		= physdev_help,
+	.print		= physdev_print,
+	.save		= physdev_save,
+	.x6_parse	= physdev_parse,
+	.x6_fcheck	= physdev_check,
+	.x6_options	= physdev_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&physdev_match);
+}
diff --git a/extensions/libxt_physdev.man b/extensions/libxt_physdev.man
new file mode 100644
index 0000000..53beb2e
--- /dev/null
+++ b/extensions/libxt_physdev.man
@@ -0,0 +1,42 @@
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+[\fB!\fP] \fB\-\-physdev\-in\fP \fIname\fP
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+[\fB!\fP] \fB\-\-physdev\-out\fP \fIname\fP
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains).  If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or if it is yet unknown what
+the output device will be, then the packet won't match this option,
+unless '!' is used.
+.TP
+[\fB!\fP] \fB\-\-physdev\-is\-in\fP
+Matches if the packet has entered through a bridge interface.
+.TP
+[\fB!\fP] \fB\-\-physdev\-is\-out\fP
+Matches if the packet will leave through a bridge interface.
+.TP
+[\fB!\fP] \fB\-\-physdev\-is\-bridged\fP
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
diff --git a/extensions/libxt_pkttype.c b/extensions/libxt_pkttype.c
new file mode 100644
index 0000000..1ed3b44
--- /dev/null
+++ b/extensions/libxt_pkttype.c
@@ -0,0 +1,134 @@
+/* 
+ * Shared library add-on to iptables to match 
+ * packets by their type (BROADCAST, UNICAST, MULTICAST). 
+ *
+ * Michal Ludvig <michal@logix.cz>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/if_packet.h>
+#include <linux/netfilter/xt_pkttype.h>
+
+enum {
+	O_PKTTYPE = 0,
+};
+
+struct pkttypes {
+	const char *name;
+	unsigned char pkttype;
+	unsigned char printhelp;
+	const char *help;
+};
+
+static const struct pkttypes supported_types[] = {
+	{"unicast", PACKET_HOST, 1, "to us"},
+	{"broadcast", PACKET_BROADCAST, 1, "to all"},
+	{"multicast", PACKET_MULTICAST, 1, "to group"},
+/*
+	{"otherhost", PACKET_OTHERHOST, 1, "to someone else"},
+	{"outgoing", PACKET_OUTGOING, 1, "outgoing of any type"},
+*/
+	/* aliases */
+	{"bcast", PACKET_BROADCAST, 0, NULL},
+	{"mcast", PACKET_MULTICAST, 0, NULL},
+	{"host", PACKET_HOST, 0, NULL}
+};
+
+static void print_types(void)
+{
+	unsigned int	i;
+	
+	printf("Valid packet types:\n");
+	for (i = 0; i < ARRAY_SIZE(supported_types); ++i)
+		if(supported_types[i].printhelp == 1)
+			printf("\t%-14s\t\t%s\n", supported_types[i].name, supported_types[i].help);
+	printf("\n");
+}
+
+static void pkttype_help(void)
+{
+	printf(
+"pkttype match options:\n"
+"[!] --pkt-type packettype    match packet type\n");
+	print_types();
+}
+
+static const struct xt_option_entry pkttype_opts[] = {
+	{.name = "pkt-type", .id = O_PKTTYPE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void parse_pkttype(const char *pkttype, struct xt_pkttype_info *info)
+{
+	unsigned int	i;
+	
+	for (i = 0; i < ARRAY_SIZE(supported_types); ++i)
+		if(strcasecmp(pkttype, supported_types[i].name)==0)
+		{
+			info->pkttype=supported_types[i].pkttype;
+			return;
+		}
+	
+	xtables_error(PARAMETER_PROBLEM, "Bad packet type '%s'", pkttype);
+}
+
+static void pkttype_parse(struct xt_option_call *cb)
+{
+	struct xt_pkttype_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	parse_pkttype(cb->arg, info);
+	if (cb->invert)
+		info->invert = 1;
+}
+
+static void print_pkttype(const struct xt_pkttype_info *info)
+{
+	unsigned int	i;
+	
+	for (i = 0; i < ARRAY_SIZE(supported_types); ++i)
+		if(supported_types[i].pkttype==info->pkttype)
+		{
+			printf("%s", supported_types[i].name);
+			return;
+		}
+
+	printf("%d", info->pkttype);	/* in case we didn't find an entry in named-packtes */
+}
+
+static void pkttype_print(const void *ip, const struct xt_entry_match *match,
+                          int numeric)
+{
+	const struct xt_pkttype_info *info = (const void *)match->data;
+	
+	printf(" PKTTYPE %s= ", info->invert ? "!" : "");
+	print_pkttype(info);
+}
+
+static void pkttype_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_pkttype_info *info = (const void *)match->data;
+	
+	printf("%s --pkt-type ", info->invert ? " !" : "");
+	print_pkttype(info);
+}
+
+static struct xtables_match pkttype_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "pkttype",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_pkttype_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_pkttype_info)),
+	.help		= pkttype_help,
+	.print		= pkttype_print,
+	.save		= pkttype_save,
+	.x6_parse	= pkttype_parse,
+	.x6_options	= pkttype_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&pkttype_match);
+}
diff --git a/extensions/libxt_pkttype.man b/extensions/libxt_pkttype.man
new file mode 100644
index 0000000..4560c76
--- /dev/null
+++ b/extensions/libxt_pkttype.man
@@ -0,0 +1,3 @@
+This module matches the link-layer packet type.
+.TP
+[\fB!\fP] \fB\-\-pkt\-type\fP {\fBunicast\fP|\fBbroadcast\fP|\fBmulticast\fP}
diff --git a/extensions/libxt_policy.c b/extensions/libxt_policy.c
new file mode 100644
index 0000000..0a64a80
--- /dev/null
+++ b/extensions/libxt_policy.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2005-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_policy.h>
+
+enum {
+	O_DIRECTION = 0,
+	O_POLICY,
+	O_STRICT,
+	O_REQID,
+	O_SPI,
+	O_PROTO,
+	O_MODE,
+	O_TUNNELSRC,
+	O_TUNNELDST,
+	O_NEXT,
+	F_STRICT = 1 << O_STRICT,
+};
+
+static void policy_help(void)
+{
+	printf(
+"policy match options:\n"
+"  --dir in|out			match policy applied during decapsulation/\n"
+"				policy to be applied during encapsulation\n"
+"  --pol none|ipsec		match policy\n"
+"  --strict 			match entire policy instead of single element\n"
+"				at any position\n"
+"These options may be used repeatedly, to describe policy elements:\n"
+"[!] --reqid reqid		match reqid\n"
+"[!] --spi spi			match SPI\n"
+"[!] --proto proto		match protocol (ah/esp/ipcomp)\n"
+"[!] --mode mode 		match mode (transport/tunnel)\n"
+"[!] --tunnel-src addr/mask	match tunnel source\n"
+"[!] --tunnel-dst addr/mask	match tunnel destination\n"
+"  --next 			begin next element in policy\n");
+}
+
+static const struct xt_option_entry policy_opts[] = {
+	{.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,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "spi", .id = O_SPI, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "tunnel-src", .id = O_TUNNELSRC, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "tunnel-dst", .id = O_TUNNELDST, .type = XTTYPE_HOSTMASK,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "proto", .id = O_PROTO, .type = XTTYPE_PROTOCOL,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "mode", .id = O_MODE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MULTI | XTOPT_INVERT},
+	{.name = "next", .id = O_NEXT, .type = XTTYPE_NONE,
+	 .flags = XTOPT_MULTI, .also = F_STRICT},
+	XTOPT_TABLEEND,
+};
+
+static int parse_direction(const char *s)
+{
+	if (strcmp(s, "in") == 0)
+		return XT_POLICY_MATCH_IN;
+	if (strcmp(s, "out") == 0)
+		return XT_POLICY_MATCH_OUT;
+	xtables_error(PARAMETER_PROBLEM, "policy_match: invalid dir \"%s\"", s);
+}
+
+static int parse_policy(const char *s)
+{
+	if (strcmp(s, "none") == 0)
+		return XT_POLICY_MATCH_NONE;
+	if (strcmp(s, "ipsec") == 0)
+		return 0;
+	xtables_error(PARAMETER_PROBLEM, "policy match: invalid policy \"%s\"", s);
+}
+
+static int parse_mode(const char *s)
+{
+	if (strcmp(s, "transport") == 0)
+		return XT_POLICY_MODE_TRANSPORT;
+	if (strcmp(s, "tunnel") == 0)
+		return XT_POLICY_MODE_TUNNEL;
+	xtables_error(PARAMETER_PROBLEM, "policy match: invalid mode \"%s\"", s);
+}
+
+static void policy_parse(struct xt_option_call *cb)
+{
+	struct xt_policy_info *info = cb->data;
+	struct xt_policy_elem *e = &info->pol[info->len];
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_DIRECTION:
+		info->flags |= parse_direction(cb->arg);
+		break;
+	case O_POLICY:
+		info->flags |= parse_policy(cb->arg);
+		break;
+	case O_STRICT:
+		info->flags |= XT_POLICY_MATCH_STRICT;
+		break;
+	case O_REQID:
+		if (e->match.reqid)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --reqid option");
+		e->match.reqid = 1;
+		e->invert.reqid = cb->invert;
+		e->reqid = cb->val.u32;
+		break;
+	case O_SPI:
+		if (e->match.spi)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --spi option");
+		e->match.spi = 1;
+		e->invert.spi = cb->invert;
+		e->spi = cb->val.u32;
+		break;
+	case O_TUNNELSRC:
+		if (e->match.saddr)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --tunnel-src option");
+
+		e->match.saddr = 1;
+		e->invert.saddr = cb->invert;
+		memcpy(&e->saddr, &cb->val.haddr, sizeof(cb->val.haddr));
+		memcpy(&e->smask, &cb->val.hmask, sizeof(cb->val.hmask));
+                break;
+	case O_TUNNELDST:
+		if (e->match.daddr)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --tunnel-dst option");
+		e->match.daddr = 1;
+		e->invert.daddr = cb->invert;
+		memcpy(&e->daddr, &cb->val.haddr, sizeof(cb->val.haddr));
+		memcpy(&e->dmask, &cb->val.hmask, sizeof(cb->val.hmask));
+		break;
+	case O_PROTO:
+		if (e->match.proto)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --proto option");
+		e->proto = cb->val.protocol;
+		if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
+		    e->proto != IPPROTO_COMP)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: protocol must be ah/esp/ipcomp");
+		e->match.proto = 1;
+		e->invert.proto = cb->invert;
+		break;
+	case O_MODE:
+		if (e->match.mode)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: double --mode option");
+		e->match.mode = 1;
+		e->invert.mode = cb->invert;
+		e->mode = parse_mode(cb->arg);
+		break;
+	case O_NEXT:
+		if (++info->len == XT_POLICY_MAX_ELEM)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: maximum policy depth reached");
+		break;
+	}
+}
+
+static void policy_check(struct xt_fcheck_call *cb)
+{
+	struct xt_policy_info *info = cb->data;
+	const struct xt_policy_elem *e;
+	int i;
+
+	/*
+	 * The old "no parameters given" check is carried out
+	 * by testing for --dir.
+	 */
+	if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT)))
+		xtables_error(PARAMETER_PROBLEM,
+		           "policy match: neither --dir in nor --dir out specified");
+
+	if (info->flags & XT_POLICY_MATCH_NONE) {
+		if (info->flags & XT_POLICY_MATCH_STRICT)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: policy none but --strict given");
+
+		if (info->len != 0)
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: policy none but policy given");
+	} else
+		info->len++;	/* increase len by 1, no --next after last element */
+
+	/*
+	 * This is already represented with O_NEXT requiring F_STRICT in the
+	 * options table, but will keep this code as a comment for reference.
+	 *
+	if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1)
+		xtables_error(PARAMETER_PROBLEM,
+		           "policy match: multiple elements but no --strict");
+	 */
+
+	for (i = 0; i < info->len; i++) {
+		e = &info->pol[i];
+
+		if (info->flags & XT_POLICY_MATCH_STRICT &&
+		    !(e->match.reqid || e->match.spi || e->match.saddr ||
+		      e->match.daddr || e->match.proto || e->match.mode))
+			xtables_error(PARAMETER_PROBLEM,
+				"policy match: empty policy element %u. "
+				"--strict is in effect, but at least one of "
+				"reqid, spi, tunnel-src, tunnel-dst, proto or "
+				"mode is required.", i);
+
+		if ((e->match.saddr || e->match.daddr)
+		    && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) ||
+		        (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode)))
+			xtables_error(PARAMETER_PROBLEM,
+			           "policy match: --tunnel-src/--tunnel-dst "
+			           "is only valid in tunnel mode");
+	}
+}
+
+static void print_mode(const char *prefix, uint8_t mode, int numeric)
+{
+	printf(" %smode ", prefix);
+
+	switch (mode) {
+	case XT_POLICY_MODE_TRANSPORT:
+		printf("transport");
+		break;
+	case XT_POLICY_MODE_TUNNEL:
+		printf("tunnel");
+		break;
+	default:
+		printf("???");
+		break;
+	}
+}
+
+static void print_proto(const char *prefix, uint8_t proto, int numeric)
+{
+	const struct protoent *p = NULL;
+
+	printf(" %sproto ", prefix);
+	if (!numeric)
+		p = getprotobynumber(proto);
+	if (p != NULL)
+		printf("%s", p->p_name);
+	else
+		printf("%u", proto);
+}
+
+#define PRINT_INVERT(x)		\
+do {				\
+	if (x)			\
+		printf(" !");	\
+} while(0)
+
+static void print_entry(const char *prefix, const struct xt_policy_elem *e,
+                        bool numeric, uint8_t family)
+{
+	if (e->match.reqid) {
+		PRINT_INVERT(e->invert.reqid);
+		printf(" %sreqid %u", prefix, e->reqid);
+	}
+	if (e->match.spi) {
+		PRINT_INVERT(e->invert.spi);
+		printf(" %sspi 0x%x", prefix, e->spi);
+	}
+	if (e->match.proto) {
+		PRINT_INVERT(e->invert.proto);
+		print_proto(prefix, e->proto, numeric);
+	}
+	if (e->match.mode) {
+		PRINT_INVERT(e->invert.mode);
+		print_mode(prefix, e->mode, numeric);
+	}
+	if (e->match.daddr) {
+		PRINT_INVERT(e->invert.daddr);
+		if (family == NFPROTO_IPV6)
+			printf(" %stunnel-dst %s%s", prefix,
+			       xtables_ip6addr_to_numeric(&e->daddr.a6),
+			       xtables_ip6mask_to_numeric(&e->dmask.a6));
+		else
+			printf(" %stunnel-dst %s%s", prefix,
+			       xtables_ipaddr_to_numeric(&e->daddr.a4),
+			       xtables_ipmask_to_numeric(&e->dmask.a4));
+	}
+	if (e->match.saddr) {
+		PRINT_INVERT(e->invert.saddr);
+		if (family == NFPROTO_IPV6)
+			printf(" %stunnel-src %s%s", prefix,
+			       xtables_ip6addr_to_numeric(&e->saddr.a6),
+			       xtables_ip6mask_to_numeric(&e->smask.a6));
+		else
+			printf(" %stunnel-src %s%s", prefix,
+			       xtables_ipaddr_to_numeric(&e->saddr.a4),
+			       xtables_ipmask_to_numeric(&e->smask.a4));
+	}
+}
+
+static void print_flags(const char *prefix, const struct xt_policy_info *info)
+{
+	if (info->flags & XT_POLICY_MATCH_IN)
+		printf(" %sdir in", prefix);
+	else
+		printf(" %sdir out", prefix);
+
+	if (info->flags & XT_POLICY_MATCH_NONE)
+		printf(" %spol none", prefix);
+	else
+		printf(" %spol ipsec", prefix);
+
+	if (info->flags & XT_POLICY_MATCH_STRICT)
+		printf(" %sstrict", prefix);
+}
+
+static void policy4_print(const void *ip, const struct xt_entry_match *match,
+                          int numeric)
+{
+	const struct xt_policy_info *info = (void *)match->data;
+	unsigned int i;
+
+	printf(" policy match");
+	print_flags("", info);
+	for (i = 0; i < info->len; i++) {
+		if (info->len > 1)
+			printf(" [%u]", i);
+		print_entry("", &info->pol[i], numeric, NFPROTO_IPV4);
+	}
+}
+
+static void policy6_print(const void *ip, const struct xt_entry_match *match,
+                          int numeric)
+{
+	const struct xt_policy_info *info = (void *)match->data;
+	unsigned int i;
+
+	printf(" policy match");
+	print_flags("", info);
+	for (i = 0; i < info->len; i++) {
+		if (info->len > 1)
+			printf(" [%u]", i);
+		print_entry("", &info->pol[i], numeric, NFPROTO_IPV6);
+	}
+}
+
+static void policy4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_policy_info *info = (void *)match->data;
+	unsigned int i;
+
+	print_flags("--", info);
+	for (i = 0; i < info->len; i++) {
+		print_entry("--", &info->pol[i], false, NFPROTO_IPV4);
+		if (i + 1 < info->len)
+			printf(" --next");
+	}
+}
+
+static void policy6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_policy_info *info = (void *)match->data;
+	unsigned int i;
+
+	print_flags("--", info);
+	for (i = 0; i < info->len; i++) {
+		print_entry("--", &info->pol[i], false, NFPROTO_IPV6);
+		if (i + 1 < info->len)
+			printf(" --next");
+	}
+}
+
+static struct xtables_match policy_mt_reg[] = {
+	{
+		.name          = "policy",
+		.version       = XTABLES_VERSION,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_policy_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
+		.help          = policy_help,
+		.x6_parse      = policy_parse,
+		.x6_fcheck     = policy_check,
+		.print         = policy4_print,
+		.save          = policy4_save,
+		.x6_options    = policy_opts,
+	},
+	{
+		.name          = "policy",
+		.version       = XTABLES_VERSION,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_policy_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)),
+		.help          = policy_help,
+		.x6_parse      = policy_parse,
+		.x6_fcheck     = policy_check,
+		.print         = policy6_print,
+		.save          = policy6_save,
+		.x6_options    = policy_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg));
+}
diff --git a/extensions/libxt_policy.man b/extensions/libxt_policy.man
new file mode 100644
index 0000000..1b834fa
--- /dev/null
+++ b/extensions/libxt_policy.man
@@ -0,0 +1,53 @@
+This modules matches the policy used by IPsec for handling a packet.
+.TP
+\fB\-\-dir\fP {\fBin\fP|\fBout\fP}
+Used to select whether to match the policy used for decapsulation or the
+policy that will be used for encapsulation.
+.B in
+is valid in the
+.B PREROUTING, INPUT and FORWARD
+chains,
+.B out
+is valid in the
+.B POSTROUTING, OUTPUT and FORWARD
+chains.
+.TP
+\fB\-\-pol\fP {\fBnone\fP|\fBipsec\fP}
+Matches if the packet is subject to IPsec processing. \fB\-\-pol none\fP
+cannot be combined with \fB\-\-strict\fP.
+.TP
+\fB\-\-strict\fP
+Selects whether to match the exact policy or match if any rule of
+the policy matches the given policy.
+.PP
+For each policy element that is to be described, one can use one or more of
+the following options. When \fB\-\-strict\fP is in effect, at least one must be
+used per element.
+.TP
+[\fB!\fP] \fB\-\-reqid\fP \fIid\fP
+Matches the reqid of the policy rule. The reqid can be specified with
+.B setkey(8)
+using
+.B unique:id
+as level.
+.TP
+[\fB!\fP] \fB\-\-spi\fP \fIspi\fP
+Matches the SPI of the SA.
+.TP
+[\fB!\fP] \fB\-\-proto\fP {\fBah\fP|\fBesp\fP|\fBipcomp\fP}
+Matches the encapsulation protocol.
+.TP
+[\fB!\fP] \fB\-\-mode\fP {\fBtunnel\fP|\fBtransport\fP}
+Matches the encapsulation mode.
+.TP
+[\fB!\fP] \fB\-\-tunnel\-src\fP \fIaddr\fP[\fB/\fP\fImask\fP]
+Matches the source end-point address of a tunnel mode SA.
+Only valid with \fB\-\-mode tunnel\fP.
+.TP
+[\fB!\fP] \fB\-\-tunnel\-dst\fP \fIaddr\fP[\fB/\fP\fImask\fP]
+Matches the destination end-point address of a tunnel mode SA.
+Only valid with \fB\-\-mode tunnel\fP.
+.TP
+\fB\-\-next\fP
+Start the next element in the policy specification. Can only be used with
+\fB\-\-strict\fP.
diff --git a/extensions/libxt_quota.c b/extensions/libxt_quota.c
new file mode 100644
index 0000000..ff498da
--- /dev/null
+++ b/extensions/libxt_quota.c
@@ -0,0 +1,70 @@
+/*
+ * Shared library add-on to iptables to add quota support
+ *
+ * Sam Johnston <samj@samj.net>
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_quota.h>
+
+enum {
+	O_QUOTA = 0,
+};
+
+static const struct xt_option_entry quota_opts[] = {
+	{.name = "quota", .id = O_QUOTA, .type = XTTYPE_UINT64,
+	 .flags = XTOPT_MAND | XTOPT_INVERT | XTOPT_PUT,
+	 XTOPT_POINTER(struct xt_quota_info, quota)},
+	XTOPT_TABLEEND,
+};
+
+static void quota_help(void)
+{
+	printf("quota match options:\n"
+	       "[!] --quota quota		quota (bytes)\n");
+}
+
+static void
+quota_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_quota_info *q = (const void *)match->data;
+	printf(" quota: %llu bytes", (unsigned long long)q->quota);
+}
+
+static void
+quota_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_quota_info *q = (const void *)match->data;
+
+	if (q->flags & XT_QUOTA_INVERT)
+		printf("! ");
+	printf(" --quota %llu", (unsigned long long) q->quota);
+}
+
+static void quota_parse(struct xt_option_call *cb)
+{
+	struct xt_quota_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->invert)
+		info->flags |= XT_QUOTA_INVERT;
+}
+
+static struct xtables_match quota_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "quota",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof (struct xt_quota_info)),
+	.userspacesize	= offsetof(struct xt_quota_info, master),
+	.help		= quota_help,
+	.print		= quota_print,
+	.save		= quota_save,
+	.x6_parse	= quota_parse,
+	.x6_options	= quota_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&quota_match);
+}
diff --git a/extensions/libxt_quota.man b/extensions/libxt_quota.man
new file mode 100644
index 0000000..fbecf37
--- /dev/null
+++ b/extensions/libxt_quota.man
@@ -0,0 +1,7 @@
+Implements network quotas by decrementing a byte counter with each
+packet. The condition matches until the byte counter reaches zero. Behavior
+is reversed with negation (i.e. the condition does not match until the
+byte counter reaches zero).
+.TP
+[\fB!\fP] \fB\-\-quota\fP \fIbytes\fP
+The quota in bytes.
diff --git a/extensions/libxt_rateest.c b/extensions/libxt_rateest.c
new file mode 100644
index 0000000..fb24412
--- /dev/null
+++ b/extensions/libxt_rateest.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2008-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+
+#include <xtables.h>
+#include <linux/netfilter/xt_rateest.h>
+
+static void rateest_help(void)
+{
+	printf(
+"rateest match options:\n"
+" --rateest1 name		Rate estimator name\n"
+" --rateest2 name		Rate estimator name\n"
+" --rateest-delta		Compare difference(s) to given rate(s)\n"
+" --rateest-bps1 [bps]		Compare bps\n"
+" --rateest-pps1 [pps]		Compare pps\n"
+" --rateest-bps2 [bps]		Compare bps\n"
+" --rateest-pps2 [pps]		Compare pps\n"
+" [!] --rateest-lt		Match if rate is less than given rate/estimator\n"
+" [!] --rateest-gt		Match if rate is greater than given rate/estimator\n"
+" [!] --rateest-eq		Match if rate is equal to given rate/estimator\n");
+}
+
+enum rateest_options {
+	OPT_RATEEST1,
+	OPT_RATEEST2,
+	OPT_RATEEST_BPS1,
+	OPT_RATEEST_PPS1,
+	OPT_RATEEST_BPS2,
+	OPT_RATEEST_PPS2,
+	OPT_RATEEST_DELTA,
+	OPT_RATEEST_LT,
+	OPT_RATEEST_GT,
+	OPT_RATEEST_EQ,
+};
+
+static const struct option rateest_opts[] = {
+	{.name = "rateest1",      .has_arg = true,  .val = OPT_RATEEST1},
+	{.name = "rateest",       .has_arg = true,  .val = OPT_RATEEST1}, /* alias for absolute mode */
+	{.name = "rateest2",      .has_arg = true,  .val = OPT_RATEEST2},
+	{.name = "rateest-bps1",  .has_arg = false, .val = OPT_RATEEST_BPS1},
+	{.name = "rateest-pps1",  .has_arg = false, .val = OPT_RATEEST_PPS1},
+	{.name = "rateest-bps2",  .has_arg = false, .val = OPT_RATEEST_BPS2},
+	{.name = "rateest-pps2",  .has_arg = false, .val = OPT_RATEEST_PPS2},
+	{.name = "rateest-bps",   .has_arg = false, .val = OPT_RATEEST_BPS2}, /* alias for absolute mode */
+	{.name = "rateest-pps",   .has_arg = false, .val = OPT_RATEEST_PPS2}, /* alias for absolute mode */
+	{.name = "rateest-delta", .has_arg = false, .val = OPT_RATEEST_DELTA},
+	{.name = "rateest-lt",    .has_arg = false, .val = OPT_RATEEST_LT},
+	{.name = "rateest-gt",    .has_arg = false, .val = OPT_RATEEST_GT},
+	{.name = "rateest-eq",    .has_arg = false, .val = OPT_RATEEST_EQ},
+	XT_GETOPT_TABLEEND,
+};
+
+/* Copied from iproute. See http://physics.nist.gov/cuu/Units/binary.html */
+static const struct rate_suffix {
+	const char *name;
+	double scale;
+} suffixes[] = {
+	{ "bit",	1. },
+	{ "Kibit",	1024. },
+	{ "kbit",	1000. },
+	{ "Mibit",	1024.*1024. },
+	{ "mbit",	1000000. },
+	{ "Gibit",	1024.*1024.*1024. },
+	{ "gbit",	1000000000. },
+	{ "Tibit",	1024.*1024.*1024.*1024. },
+	{ "tbit",	1000000000000. },
+	{ "Bps",	8. },
+	{ "KiBps",	8.*1024. },
+	{ "KBps",	8000. },
+	{ "MiBps",	8.*1024*1024. },
+	{ "MBps",	8000000. },
+	{ "GiBps",	8.*1024.*1024.*1024. },
+	{ "GBps",	8000000000. },
+	{ "TiBps",	8.*1024.*1024.*1024.*1024. },
+	{ "TBps",	8000000000000. },
+	{NULL},
+};
+
+static int
+rateest_get_rate(uint32_t *rate, const char *str)
+{
+	char *p;
+	double bps = strtod(str, &p);
+	const struct rate_suffix *s;
+
+	if (p == str)
+		return -1;
+
+	if (*p == '\0') {
+		*rate = bps / 8.;	/* assume bytes/sec */
+		return 0;
+	}
+
+	for (s = suffixes; s->name; ++s) {
+		if (strcasecmp(s->name, p) == 0) {
+			*rate = (bps * s->scale) / 8.;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int
+rateest_parse(int c, char **argv, int invert, unsigned int *flags,
+	      const void *entry, struct xt_entry_match **match)
+{
+	struct xt_rateest_match_info *info = (void *)(*match)->data;
+	unsigned int val;
+
+	switch (c) {
+	case OPT_RATEEST1:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest1 twice");
+		*flags |= 1 << c;
+
+		strncpy(info->name1, optarg, sizeof(info->name1) - 1);
+		break;
+
+	case OPT_RATEEST2:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest2 twice");
+		*flags |= 1 << c;
+
+		strncpy(info->name2, optarg, sizeof(info->name2) - 1);
+		info->flags |= XT_RATEEST_MATCH_REL;
+		break;
+
+	case OPT_RATEEST_BPS1:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest-bps can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest-bps1 twice");
+		*flags |= 1 << c;
+
+		info->flags |= XT_RATEEST_MATCH_BPS;
+
+		/* The rate is optional and only required in absolute mode */
+		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
+			break;
+
+		if (rateest_get_rate(&info->bps1, argv[optind]) < 0)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: could not parse rate `%s'",
+				   argv[optind]);
+		optind++;
+		break;
+
+	case OPT_RATEEST_PPS1:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest-pps can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest-pps1 twice");
+		*flags |= 1 << c;
+
+		info->flags |= XT_RATEEST_MATCH_PPS;
+
+		/* The rate is optional and only required in absolute mode */
+		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
+			break;
+
+		if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: could not parse pps `%s'",
+				   argv[optind]);
+		info->pps1 = val;
+		optind++;
+		break;
+
+	case OPT_RATEEST_BPS2:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest-bps can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest-bps2 twice");
+		*flags |= 1 << c;
+
+		info->flags |= XT_RATEEST_MATCH_BPS;
+
+		/* The rate is optional and only required in absolute mode */
+		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
+			break;
+
+		if (rateest_get_rate(&info->bps2, argv[optind]) < 0)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: could not parse rate `%s'",
+				   argv[optind]);
+		optind++;
+		break;
+
+	case OPT_RATEEST_PPS2:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest-pps can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest-pps2 twice");
+		*flags |= 1 << c;
+
+		info->flags |= XT_RATEEST_MATCH_PPS;
+
+		/* The rate is optional and only required in absolute mode */
+		if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!')
+			break;
+
+		if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: could not parse pps `%s'",
+				   argv[optind]);
+		info->pps2 = val;
+		optind++;
+		break;
+
+	case OPT_RATEEST_DELTA:
+		if (invert)
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: rateest-delta can't be inverted");
+
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify --rateest-delta twice");
+		*flags |= 1 << c;
+
+		info->flags |= XT_RATEEST_MATCH_DELTA;
+		break;
+
+	case OPT_RATEEST_EQ:
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify lt/gt/eq twice");
+		*flags |= 1 << c;
+
+		info->mode = XT_RATEEST_MATCH_EQ;
+		if (invert)
+			info->flags |= XT_RATEEST_MATCH_INVERT;
+		break;
+
+	case OPT_RATEEST_LT:
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify lt/gt/eq twice");
+		*flags |= 1 << c;
+
+		info->mode = XT_RATEEST_MATCH_LT;
+		if (invert)
+			info->flags |= XT_RATEEST_MATCH_INVERT;
+		break;
+
+	case OPT_RATEEST_GT:
+		if (*flags & (1 << c))
+			xtables_error(PARAMETER_PROBLEM,
+				   "rateest: can't specify lt/gt/eq twice");
+		*flags |= 1 << c;
+
+		info->mode = XT_RATEEST_MATCH_GT;
+		if (invert)
+			info->flags |= XT_RATEEST_MATCH_INVERT;
+		break;
+	}
+
+	return 1;
+}
+
+static void rateest_final_check(struct xt_fcheck_call *cb)
+{
+	struct xt_rateest_match_info *info = cb->data;
+
+	if (info == NULL)
+		xtables_error(PARAMETER_PROBLEM, "rateest match: "
+		           "you need to specify some flags");
+	if (!(info->flags & XT_RATEEST_MATCH_REL))
+		info->flags |= XT_RATEEST_MATCH_ABS;
+}
+
+static void
+rateest_print_rate(uint32_t rate, int numeric)
+{
+	double tmp = (double)rate*8;
+
+	if (numeric)
+		printf(" %u", rate);
+	else if (tmp >= 1000.0*1000000.0)
+		printf(" %.0fMbit", tmp/1000000.0);
+	else if (tmp >= 1000.0 * 1000.0)
+		printf(" %.0fKbit", tmp/1000.0);
+	else
+		printf(" %.0fbit", tmp);
+}
+
+static void
+rateest_print_mode(const struct xt_rateest_match_info *info,
+                   const char *prefix)
+{
+	if (info->flags & XT_RATEEST_MATCH_INVERT)
+		printf(" !");
+
+	switch (info->mode) {
+	case XT_RATEEST_MATCH_EQ:
+		printf(" %seq", prefix);
+		break;
+	case XT_RATEEST_MATCH_LT:
+		printf(" %slt", prefix);
+		break;
+	case XT_RATEEST_MATCH_GT:
+		printf(" %sgt", prefix);
+		break;
+	default:
+		exit(1);
+	}
+}
+
+static void
+rateest_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_rateest_match_info *info = (const void *)match->data;
+
+	printf(" rateest match ");
+
+	printf("%s", info->name1);
+	if (info->flags & XT_RATEEST_MATCH_DELTA)
+		printf(" delta");
+
+	if (info->flags & XT_RATEEST_MATCH_BPS) {
+		printf(" bps");
+		if (info->flags & XT_RATEEST_MATCH_DELTA)
+			rateest_print_rate(info->bps1, numeric);
+		if (info->flags & XT_RATEEST_MATCH_ABS) {
+			rateest_print_rate(info->bps2, numeric);
+			rateest_print_mode(info, "");
+		}
+	}
+	if (info->flags & XT_RATEEST_MATCH_PPS) {
+		printf(" pps");
+		if (info->flags & XT_RATEEST_MATCH_DELTA)
+			printf(" %u", info->pps1);
+		if (info->flags & XT_RATEEST_MATCH_ABS) {
+			rateest_print_mode(info, "");
+			printf(" %u", info->pps2);
+		}
+	}
+
+	if (info->flags & XT_RATEEST_MATCH_REL) {
+		rateest_print_mode(info, "");
+
+		printf(" %s", info->name2);
+
+		if (info->flags & XT_RATEEST_MATCH_BPS) {
+			printf(" bps");
+			if (info->flags & XT_RATEEST_MATCH_DELTA)
+				rateest_print_rate(info->bps2, numeric);
+		}
+		if (info->flags & XT_RATEEST_MATCH_PPS) {
+			printf(" pps");
+			if (info->flags & XT_RATEEST_MATCH_DELTA)
+				printf(" %u", info->pps2);
+		}
+	}
+}
+
+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);
+		rateest_save_rates(info);
+		printf(" --rateest2 %s", info->name2);
+	} else { /* XT_RATEEST_MATCH_ABS */
+		printf(" --rateest %s", info->name1);
+		rateest_save_rates(info);
+	}
+}
+
+static struct xtables_match rateest_mt_reg = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "rateest",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_rateest_match_info)),
+	.userspacesize	= XT_ALIGN(offsetof(struct xt_rateest_match_info, est1)),
+	.help		= rateest_help,
+	.parse		= rateest_parse,
+	.x6_fcheck	= rateest_final_check,
+	.print		= rateest_print,
+	.save		= rateest_save,
+	.extra_opts	= rateest_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&rateest_mt_reg);
+}
diff --git a/extensions/libxt_rateest.man b/extensions/libxt_rateest.man
new file mode 100644
index 0000000..42a82f3
--- /dev/null
+++ b/extensions/libxt_rateest.man
@@ -0,0 +1,96 @@
+The rate estimator can match on estimated rates as collected by the RATEEST
+target. It supports matching on absolute bps/pps values, comparing two rate
+estimators and matching on the difference between two rate estimators.
+.PP
+For a better understanding of the available options, these are all possible
+combinations:
+.\" * Absolute:
+.IP \(bu 4
+\fBrateest\fP \fIoperator\fP \fBrateest-bps\fP
+.IP \(bu 4
+\fBrateest\fP \fIoperator\fP \fBrateest-pps\fP
+.\" * Absolute + Delta:
+.IP \(bu 4
+(\fBrateest\fP minus \fBrateest-bps1\fP) \fIoperator\fP \fBrateest-bps2\fP
+.IP \(bu 4
+(\fBrateest\fP minus \fBrateest-pps1\fP) \fIoperator\fP \fBrateest-pps2\fP
+.\" * Relative:
+.IP \(bu 4
+\fBrateest1\fP \fIoperator\fP \fBrateest2\fP \fBrateest-bps\fP(without rate!)
+.IP \(bu 4
+\fBrateest1\fP \fIoperator\fP \fBrateest2\fP \fBrateest-pps\fP(without rate!)
+.\" * Relative + Delta:
+.IP \(bu 4
+(\fBrateest1\fP minus \fBrateest-bps1\fP) \fIoperator\fP
+(\fBrateest2\fP minus \fBrateest-bps2\fP)
+.IP \(bu 4
+(\fBrateest1\fP minus \fBrateest-pps1\fP) \fIoperator\fP
+(\fBrateest2\fP minus \fBrateest-pps2\fP)
+.TP
+\fB\-\-rateest\-delta\fP
+For each estimator (either absolute or relative mode), calculate the difference
+between the estimator-determined flow rate and the static value chosen with the
+BPS/PPS options. If the flow rate is higher than the specified BPS/PPS, 0 will
+be used instead of a negative value. In other words, "max(0, rateest#_rate -
+rateest#_bps)" is used.
+.TP
+[\fB!\fP] \fB\-\-rateest\-lt\fP
+Match if rate is less than given rate/estimator.
+.TP
+[\fB!\fP] \fB\-\-rateest\-gt\fP
+Match if rate is greater than given rate/estimator.
+.TP
+[\fB!\fP] \fB\-\-rateest\-eq\fP
+Match if rate is equal to given rate/estimator.
+.PP
+In the so-called "absolute mode", only one rate estimator is used and compared
+against a static value, while in "relative mode", two rate estimators are
+compared against another.
+.TP
+\fB\-\-rateest\fP \fIname\fP
+Name of the one rate estimator for absolute mode.
+.TP
+\fB\-\-rateest1\fP \fIname\fP
+.TP
+\fB\-\-rateest2\fP \fIname\fP
+The names of the two rate estimators for relative mode.
+.TP
+\fB\-\-rateest\-bps\fP [\fIvalue\fP]
+.TP
+\fB\-\-rateest\-pps\fP [\fIvalue\fP]
+.TP
+\fB\-\-rateest\-bps1\fP [\fIvalue\fP]
+.TP
+\fB\-\-rateest\-bps2\fP [\fIvalue\fP]
+.TP
+\fB\-\-rateest\-pps1\fP [\fIvalue\fP]
+.TP
+\fB\-\-rateest\-pps2\fP [\fIvalue\fP]
+Compare the estimator(s) by bytes or packets per second, and compare against
+the chosen value. See the above bullet list for which option is to be used in
+which case. A unit suffix may be used - available ones are: bit, [kmgt]bit,
+[KMGT]ibit, Bps, [KMGT]Bps, [KMGT]iBps.
+.PP
+Example: This is what can be used to route outgoing data connections from an
+FTP server over two lines based on the available bandwidth at the time the data
+connection was started:
+.PP
+# Estimate outgoing rates
+.PP
+iptables \-t mangle \-A POSTROUTING \-o eth0 \-j RATEEST \-\-rateest\-name eth0
+\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s
+.PP
+iptables \-t mangle \-A POSTROUTING \-o ppp0 \-j RATEEST \-\-rateest\-name ppp0
+\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s
+.PP
+# Mark based on available bandwidth
+.PP
+iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp
+\-m rateest \-\-rateest\-delta \-\-rateest1 eth0 \-\-rateest\-bps1 2.5mbit \-\-rateest\-gt
+\-\-rateest2 ppp0 \-\-rateest\-bps2 2mbit \-j CONNMARK \-\-set\-mark 1
+.PP
+iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp
+\-m rateest \-\-rateest\-delta \-\-rateest1 ppp0 \-\-rateest\-bps1 2mbit \-\-rateest\-gt
+\-\-rateest2 eth0 \-\-rateest\-bps2 2.5mbit \-j CONNMARK \-\-set\-mark 2
+.PP
+iptables \-t mangle \-A balance \-j CONNMARK \-\-restore\-mark
diff --git a/extensions/libxt_recent.c b/extensions/libxt_recent.c
new file mode 100644
index 0000000..e1801f1
--- /dev/null
+++ b/extensions/libxt_recent.c
@@ -0,0 +1,355 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_recent.h>
+
+enum {
+	O_SET = 0,
+	O_RCHECK,
+	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_v0[] = {
+	{.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},
+	XTOPT_TABLEEND,
+};
+#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(
+"recent match options:\n"
+"[!] --set                       Add source address to list, always matches.\n"
+"[!] --rcheck                    Match if source address in list.\n"
+"[!] --update                    Match if source address in list, also update last-seen time.\n"
+"[!] --remove                    Match if source address in list, also removes that address from list.\n"
+"    --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"
+"    --rttl                      For check and update commands above.\n"
+"                                Specifies that the match will only occur if the source address and the TTL\n"
+"                                match between this packet and the one which was set.\n"
+"                                Useful if you have problems with people spoofing their source address in order\n"
+"                                to DoS you via this module.\n"
+"    --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>.\n");
+}
+
+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 = (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)
+{
+	struct xt_recent_mtinfo *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET:
+		info->check_set |= XT_RECENT_SET;
+		if (cb->invert)
+			info->invert = true;
+		break;
+	case O_RCHECK:
+		info->check_set |= XT_RECENT_CHECK;
+		if (cb->invert)
+			info->invert = true;
+		break;
+	case O_UPDATE:
+		info->check_set |= XT_RECENT_UPDATE;
+		if (cb->invert)
+			info->invert = true;
+		break;
+	case O_REMOVE:
+		info->check_set |= XT_RECENT_REMOVE;
+		if (cb->invert)
+			info->invert = true;
+		break;
+	case O_RTTL:
+		info->check_set |= XT_RECENT_TTL;
+		break;
+	case O_RSOURCE:
+		info->side = XT_RECENT_SOURCE;
+		break;
+	case O_RDEST:
+		info->side = XT_RECENT_DEST;
+		break;
+	case O_REAP:
+		info->check_set |= XT_RECENT_REAP;
+		break;
+	}
+}
+
+static void recent_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_ANY_OP))
+		xtables_error(PARAMETER_PROBLEM,
+			"recent: you must specify one of `--set', `--rcheck' "
+			"`--update' or `--remove'");
+}
+
+static void recent_print(const void *ip, const struct xt_entry_match *match,
+                         unsigned int family)
+{
+	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	printf(" recent:");
+	if (info->check_set & XT_RECENT_SET)
+		printf(" SET");
+	if (info->check_set & XT_RECENT_CHECK)
+		printf(" CHECK");
+	if (info->check_set & XT_RECENT_UPDATE)
+		printf(" UPDATE");
+	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");
+	if(info->name) printf(" name: %s", info->name);
+	if (info->side == XT_RECENT_SOURCE)
+		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,
+			unsigned int family)
+{
+	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+
+	if (info->check_set & XT_RECENT_SET)
+		printf(" --set");
+	if (info->check_set & XT_RECENT_CHECK)
+		printf(" --rcheck");
+	if (info->check_set & XT_RECENT_UPDATE)
+		printf(" --update");
+	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 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_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
+}
diff --git a/extensions/libxt_recent.man b/extensions/libxt_recent.man
new file mode 100644
index 0000000..419be25
--- /dev/null
+++ b/extensions/libxt_recent.man
@@ -0,0 +1,109 @@
+Allows you to dynamically create a list of IP addresses and then match against
+that list in a few different ways.
+.PP
+For example, you can create a "badguy" list out of people attempting to connect
+to port 139 on your firewall and then DROP all future packets from them without
+considering them.
+.PP
+\fB\-\-set\fP, \fB\-\-rcheck\fP, \fB\-\-update\fP and \fB\-\-remove\fP are
+mutually exclusive.
+.TP
+\fB\-\-name\fP \fIname\fP
+Specify the list to use for the commands. If no name is given then
+\fBDEFAULT\fP will be used.
+.TP
+[\fB!\fP] \fB\-\-set\fP
+This will add the source address of the packet to the list. If the source
+address is already in the list, this will update the existing entry. This will
+always return success (or failure if \fB!\fP is passed in).
+.TP
+\fB\-\-rsource\fP
+Match/save the source address of each packet in the recent list table. This
+is the default.
+.TP
+\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
+[\fB!\fP] \fB\-\-update\fP
+Like \fB\-\-rcheck\fP, except it will update the "last seen" timestamp if it
+matches.
+.TP
+[\fB!\fP] \fB\-\-remove\fP
+Check if the source address of the packet is currently in the list and if so
+that address will be removed from the list and the rule will return true. If
+the address is not found, false is returned.
+.TP
+\fB\-\-seconds\fP \fIseconds\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
+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
+address is in the list and packets had been received greater than or equal to
+the given value. This option may be used along with \fB\-\-seconds\fP to create
+an even narrower match requiring a certain number of hits within a specific
+time frame. The maximum value for the hitcount parameter is given by the
+"ip_pkt_list_tot" parameter of the xt_recent kernel module. Exceeding this
+value on the command line will cause the rule to be rejected.
+.TP
+\fB\-\-rttl\fP
+This option may only 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
+address is in the list and the TTL of the current packet matches that of the
+packet which hit the \fB\-\-set\fP rule. This may be useful if you have problems
+with people faking their source address in order to DoS you via this module by
+disallowing others access to your site by sending bogus packets to you.
+.PP
+Examples:
+.IP
+iptables \-A FORWARD \-m recent \-\-name badguy \-\-rcheck \-\-seconds 60 \-j DROP
+.IP
+iptables \-A FORWARD \-p tcp \-i eth0 \-\-dport 139 \-m recent \-\-name badguy \-\-set \-j DROP
+.PP
+\fB/proc/net/xt_recent/*\fP are the current lists of addresses and information
+about each entry of each list.
+.PP
+Each file in \fB/proc/net/xt_recent/\fP can be read from to see the current
+list or written two using the following commands to modify the list:
+.TP
+\fBecho +\fP\fIaddr\fP\fB >/proc/net/xt_recent/DEFAULT\fP
+to add \fIaddr\fP to the DEFAULT list
+.TP
+\fBecho \-\fP\fIaddr\fP\fB >/proc/net/xt_recent/DEFAULT\fP
+to remove \fIaddr\fP from the DEFAULT list
+.TP
+\fBecho / >/proc/net/xt_recent/DEFAULT\fP
+to flush the DEFAULT list (remove all entries).
+.PP
+The module itself accepts parameters, defaults shown:
+.TP
+\fBip_list_tot\fP=\fI100\fP
+Number of addresses remembered per table.
+.TP
+\fBip_pkt_list_tot\fP=\fI20\fP
+Number of packets per address remembered.
+.TP
+\fBip_list_hash_size\fP=\fI0\fP
+Hash table size. 0 means to calculate it based on ip_list_tot, default: 512.
+.TP
+\fBip_list_perms\fP=\fI0644\fP
+Permissions for /proc/net/xt_recent/* files.
+.TP
+\fBip_list_uid\fP=\fI0\fP
+Numerical UID for ownership of /proc/net/xt_recent/* files.
+.TP
+\fBip_list_gid\fP=\fI0\fP
+Numerical GID for ownership of /proc/net/xt_recent/* files.
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
new file mode 100644
index 0000000..56a4cdf
--- /dev/null
+++ b/extensions/libxt_sctp.c
@@ -0,0 +1,505 @@
+/* Shared library add-on to iptables for SCTP matching
+ *
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <xtables.h>
+
+#include <linux/netfilter/xt_sctp.h>
+
+#if 0
+#define DEBUGP(format, first...) printf(format, ##first)
+#define static
+#else
+#define DEBUGP(format, fist...) 
+#endif
+
+static void
+print_chunk(uint32_t chunknum, int numeric);
+
+static void sctp_init(struct xt_entry_match *m)
+{
+	int i;
+	struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data;
+
+	for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) {
+		einfo->flag_info[i].chunktype = -1;
+	}
+}
+
+static void sctp_help(void)
+{
+	printf(
+"sctp match options\n"
+"[!] --source-port port[:port]                          match source port(s)\n"
+" --sport ...\n"
+"[!] --destination-port port[:port]                     match destination port(s)\n"
+" --dport ...\n" 
+"[!] --chunk-types (all|any|none) (chunktype[:flags])+	match if all, any or none of\n"
+"						        chunktypes are present\n"
+"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN ALL NONE\n");
+}
+
+static const struct option sctp_opts[] = {
+	{.name = "source-port",      .has_arg = true, .val = '1'},
+	{.name = "sport",            .has_arg = true, .val = '1'},
+	{.name = "destination-port", .has_arg = true, .val = '2'},
+	{.name = "dport",            .has_arg = true, .val = '2'},
+	{.name = "chunk-types",      .has_arg = true, .val = '3'},
+	XT_GETOPT_TABLEEND,
+};
+
+static void
+parse_sctp_ports(const char *portstring, 
+		 uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+
+	buffer = strdup(portstring);
+	DEBUGP("%s\n", portstring);
+	if ((cp = strchr(buffer, ':')) == NULL) {
+		ports[0] = ports[1] = xtables_parse_port(buffer, "sctp");
+	}
+	else {
+		*cp = '\0';
+		cp++;
+
+		ports[0] = buffer[0] ? xtables_parse_port(buffer, "sctp") : 0;
+		ports[1] = cp[0] ? xtables_parse_port(cp, "sctp") : 0xFFFF;
+
+		if (ports[0] > ports[1])
+			xtables_error(PARAMETER_PROBLEM,
+				   "invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+struct sctp_chunk_names {
+	const char *name;
+	unsigned int chunk_type;
+	const char *valid_flags;
+};
+
+/*'ALL' and 'NONE' will be treated specially. */
+static const struct sctp_chunk_names sctp_chunk_names[]
+= { { .name = "DATA", 		.chunk_type = 0,   .valid_flags = "----IUBE"},
+    { .name = "INIT", 		.chunk_type = 1,   .valid_flags = "--------"},
+    { .name = "INIT_ACK", 	.chunk_type = 2,   .valid_flags = "--------"},
+    { .name = "SACK",		.chunk_type = 3,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT",	.chunk_type = 4,   .valid_flags = "--------"},
+    { .name = "HEARTBEAT_ACK",	.chunk_type = 5,   .valid_flags = "--------"},
+    { .name = "ABORT",		.chunk_type = 6,   .valid_flags = "-------T"},
+    { .name = "SHUTDOWN",	.chunk_type = 7,   .valid_flags = "--------"},
+    { .name = "SHUTDOWN_ACK",	.chunk_type = 8,   .valid_flags = "--------"},
+    { .name = "ERROR",		.chunk_type = 9,   .valid_flags = "--------"},
+    { .name = "COOKIE_ECHO",	.chunk_type = 10,  .valid_flags = "--------"},
+    { .name = "COOKIE_ACK",	.chunk_type = 11,  .valid_flags = "--------"},
+    { .name = "ECN_ECNE",	.chunk_type = 12,  .valid_flags = "--------"},
+    { .name = "ECN_CWR",	.chunk_type = 13,  .valid_flags = "--------"},
+    { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14,  .valid_flags = "-------T"},
+    { .name = "ASCONF",		.chunk_type = 193,  .valid_flags = "--------"},
+    { .name = "ASCONF_ACK",	.chunk_type = 128,  .valid_flags = "--------"},
+    { .name = "FORWARD_TSN",	.chunk_type = 192,  .valid_flags = "--------"},
+};
+
+static void
+save_chunk_flag_info(struct xt_sctp_flag_info *flag_info,
+		     int *flag_count,
+		     int chunktype, 
+		     int bit, 
+		     int set)
+{
+	int i;
+
+	for (i = 0; i < *flag_count; i++) {
+		if (flag_info[i].chunktype == chunktype) {
+			DEBUGP("Previous match found\n");
+			flag_info[i].chunktype = chunktype;
+			flag_info[i].flag_mask |= (1 << bit);
+			if (set) {
+				flag_info[i].flag |= (1 << bit);
+			}
+
+			return;
+		}
+	}
+	
+	if (*flag_count == XT_NUM_SCTP_FLAGS) {
+		xtables_error (PARAMETER_PROBLEM,
+			"Number of chunk types with flags exceeds currently allowed limit."
+			"Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and"
+			"recompiling both the kernel space and user space modules\n");
+	}
+
+	flag_info[*flag_count].chunktype = chunktype;
+	flag_info[*flag_count].flag_mask |= (1 << bit);
+	if (set) {
+		flag_info[*flag_count].flag |= (1 << bit);
+	}
+	(*flag_count)++;
+}
+
+static void
+parse_sctp_chunk(struct xt_sctp_info *einfo, 
+		 const char *chunks)
+{
+	char *ptr;
+	char *buffer;
+	unsigned int i, j;
+	int found = 0;
+	char *chunk_flags;
+
+	buffer = strdup(chunks);
+	DEBUGP("Buffer: %s\n", buffer);
+
+	SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+
+	if (!strcasecmp(buffer, "ALL")) {
+		SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap);
+		goto out;
+	}
+	
+	if (!strcasecmp(buffer, "NONE")) {
+		SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+		goto out;
+	}
+
+	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+		found = 0;
+		DEBUGP("Next Chunk type %s\n", ptr);
+		
+		if ((chunk_flags = strchr(ptr, ':')) != NULL) {
+			*chunk_flags++ = 0;
+		}
+		
+		for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
+			if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
+				DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type);
+				SCTP_CHUNKMAP_SET(einfo->chunkmap, 
+					sctp_chunk_names[i].chunk_type);
+				found = 1;
+				break;
+			}
+		if (!found)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Unknown sctp chunk `%s'", ptr);
+
+		if (chunk_flags) {
+			DEBUGP("Chunk flags %s\n", chunk_flags);
+			for (j = 0; j < strlen(chunk_flags); j++) {
+				char *p;
+				int bit;
+
+				if ((p = strchr(sctp_chunk_names[i].valid_flags, 
+						toupper(chunk_flags[j]))) != NULL) {
+					bit = p - sctp_chunk_names[i].valid_flags;
+					bit = 7 - bit;
+
+					save_chunk_flag_info(einfo->flag_info, 
+						&(einfo->flag_count), i, bit, 
+						isupper(chunk_flags[j]));
+				} else {
+					xtables_error(PARAMETER_PROBLEM,
+						"Invalid flags for chunk type %d\n", i);
+				}
+			}
+		}
+	}
+out:
+	free(buffer);
+}
+
+static void
+parse_sctp_chunks(struct xt_sctp_info *einfo,
+		  const char *match_type,
+		  const char *chunks)
+{
+	DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks);
+	if (!strcasecmp(match_type, "ANY")) {
+		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY;
+	} else 	if (!strcasecmp(match_type, "ALL")) {
+		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL;
+	} else 	if (!strcasecmp(match_type, "ONLY")) {
+		einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY;
+	} else {
+		xtables_error (PARAMETER_PROBLEM,
+			"Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\"");
+	}
+
+	SCTP_CHUNKMAP_RESET(einfo->chunkmap);
+	parse_sctp_chunk(einfo, chunks);
+}
+
+static int
+sctp_parse(int c, char **argv, int invert, unsigned int *flags,
+           const void *entry, struct xt_entry_match **match)
+{
+	struct xt_sctp_info *einfo
+		= (struct xt_sctp_info *)(*match)->data;
+
+	switch (c) {
+	case '1':
+		if (*flags & XT_SCTP_SRC_PORTS)
+			xtables_error(PARAMETER_PROBLEM,
+			           "Only one `--source-port' allowed");
+		einfo->flags |= XT_SCTP_SRC_PORTS;
+		parse_sctp_ports(optarg, einfo->spts);
+		if (invert)
+			einfo->invflags |= XT_SCTP_SRC_PORTS;
+		*flags |= XT_SCTP_SRC_PORTS;
+		break;
+
+	case '2':
+		if (*flags & XT_SCTP_DEST_PORTS)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one `--destination-port' allowed");
+		einfo->flags |= XT_SCTP_DEST_PORTS;
+		parse_sctp_ports(optarg, einfo->dpts);
+		if (invert)
+			einfo->invflags |= XT_SCTP_DEST_PORTS;
+		*flags |= XT_SCTP_DEST_PORTS;
+		break;
+
+	case '3':
+		if (*flags & XT_SCTP_CHUNK_TYPES)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one `--chunk-types' allowed");
+		if (!argv[optind] 
+		    || argv[optind][0] == '-' || argv[optind][0] == '!')
+			xtables_error(PARAMETER_PROBLEM,
+				   "--chunk-types requires two args");
+
+		einfo->flags |= XT_SCTP_CHUNK_TYPES;
+		parse_sctp_chunks(einfo, optarg, argv[optind]);
+		if (invert)
+			einfo->invflags |= XT_SCTP_CHUNK_TYPES;
+		optind++;
+		*flags |= XT_SCTP_CHUNK_TYPES;
+		break;
+	}
+	return 1;
+}
+
+static const char *
+port_to_service(int port)
+{
+	const struct servent *service;
+
+	if ((service = getservbyport(htons(port), "sctp")))
+		return service->s_name;
+
+	return NULL;
+}
+
+static void
+print_port(uint16_t port, int numeric)
+{
+	const char *service;
+
+	if (numeric || (service = port_to_service(port)) == NULL)
+		printf("%u", port);
+	else
+		printf("%s", service);
+}
+
+static void
+print_ports(const char *name, uint16_t min, uint16_t max,
+	    int invert, int numeric)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFF || invert) {
+		printf(" %s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			print_port(min, numeric);
+		} else {
+			printf("s:%s", inv);
+			print_port(min, numeric);
+			printf(":");
+			print_port(max, numeric);
+		}
+	}
+}
+
+static void
+print_chunk_flags(uint32_t chunknum, uint8_t chunk_flags, uint8_t chunk_flags_mask)
+{
+	int i;
+
+	DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, 
+			chunk_flags_mask);
+
+	if (chunk_flags_mask) {
+		printf(":");
+	}
+
+	for (i = 7; i >= 0; i--) {
+		if (chunk_flags_mask & (1 << i)) {
+			if (chunk_flags & (1 << i)) {
+				printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]);
+			} else {
+				printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i]));
+			}
+		}
+	}
+}
+
+static void
+print_chunk(uint32_t chunknum, int numeric)
+{
+	if (numeric) {
+		printf("0x%04X", chunknum);
+	}
+	else {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i)
+			if (sctp_chunk_names[i].chunk_type == chunknum)
+				printf("%s", sctp_chunk_names[chunknum].name);
+	}
+}
+
+static void
+print_chunks(const struct xt_sctp_info *einfo, int numeric)
+{
+	uint32_t chunk_match_type = einfo->chunk_match_type;
+	const struct xt_sctp_flag_info *flag_info = einfo->flag_info;
+	int flag_count = einfo->flag_count;
+	int i, j;
+	int flag;
+
+	switch (chunk_match_type) {
+		case SCTP_CHUNK_MATCH_ANY:	printf(" any"); break;
+		case SCTP_CHUNK_MATCH_ALL:	printf(" all"); break;
+		case SCTP_CHUNK_MATCH_ONLY:	printf(" only"); break;
+		default:	printf("Never reach here\n"); break;
+	}
+
+	if (SCTP_CHUNKMAP_IS_CLEAR(einfo->chunkmap)) {
+		printf(" NONE");
+		goto out;
+	}
+	
+	if (SCTP_CHUNKMAP_IS_ALL_SET(einfo->chunkmap)) {
+		printf(" ALL");
+		goto out;
+	}
+	
+	flag = 0;
+	for (i = 0; i < 256; i++) {
+		if (SCTP_CHUNKMAP_IS_SET(einfo->chunkmap, i)) {
+			if (flag)
+				printf(",");
+			else
+				putchar(' ');
+			flag = 1;
+			print_chunk(i, numeric);
+			for (j = 0; j < flag_count; j++) {
+				if (flag_info[j].chunktype == i) {
+					print_chunk_flags(i, flag_info[j].flag,
+						flag_info[j].flag_mask);
+				}
+			}
+		}
+	}
+out:
+	return;
+}
+
+static void
+sctp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_sctp_info *einfo =
+		(const struct xt_sctp_info *)match->data;
+
+	printf(" sctp");
+
+	if (einfo->flags & XT_SCTP_SRC_PORTS) {
+		print_ports("spt", einfo->spts[0], einfo->spts[1],
+			einfo->invflags & XT_SCTP_SRC_PORTS,
+			numeric);
+	}
+
+	if (einfo->flags & XT_SCTP_DEST_PORTS) {
+		print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+			einfo->invflags & XT_SCTP_DEST_PORTS,
+			numeric);
+	}
+
+	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
+		/* FIXME: print_chunks() is used in save() where the printing of '!'
+		s taken care of, so we need to do that here as well */
+		if (einfo->invflags & XT_SCTP_CHUNK_TYPES) {
+			printf(" !");
+		}
+		print_chunks(einfo, numeric);
+	}
+}
+
+static void sctp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_sctp_info *einfo =
+		(const struct xt_sctp_info *)match->data;
+
+	if (einfo->flags & XT_SCTP_SRC_PORTS) {
+		if (einfo->invflags & XT_SCTP_SRC_PORTS)
+			printf(" !");
+		if (einfo->spts[0] != einfo->spts[1])
+			printf(" --sport %u:%u",
+			       einfo->spts[0], einfo->spts[1]);
+		else
+			printf(" --sport %u", einfo->spts[0]);
+	}
+
+	if (einfo->flags & XT_SCTP_DEST_PORTS) {
+		if (einfo->invflags & XT_SCTP_DEST_PORTS)
+			printf(" !");
+		if (einfo->dpts[0] != einfo->dpts[1])
+			printf(" --dport %u:%u",
+			       einfo->dpts[0], einfo->dpts[1]);
+		else
+			printf(" --dport %u", einfo->dpts[0]);
+	}
+
+	if (einfo->flags & XT_SCTP_CHUNK_TYPES) {
+		if (einfo->invflags & XT_SCTP_CHUNK_TYPES)
+			printf(" !");
+		printf(" --chunk-types");
+
+		print_chunks(einfo, 0);
+	}
+}
+
+static struct xtables_match sctp_match = {
+	.name		= "sctp",
+	.family		= NFPROTO_UNSPEC,
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_sctp_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_sctp_info)),
+	.help		= sctp_help,
+	.init		= sctp_init,
+	.parse		= sctp_parse,
+	.print		= sctp_print,
+	.save		= sctp_save,
+	.extra_opts	= sctp_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&sctp_match);
+}
diff --git a/extensions/libxt_sctp.man b/extensions/libxt_sctp.man
new file mode 100644
index 0000000..9c0bd8c
--- /dev/null
+++ b/extensions/libxt_sctp.man
@@ -0,0 +1,28 @@
+.TP
+[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP]
+.TP
+[\fB!\fP] \fB\-\-chunk\-types\fP {\fBall\fP|\fBany\fP|\fBonly\fP} \fIchunktype\fP[\fB:\fP\fIflags\fP] [...]
+The flag letter in upper case indicates that the flag is to match if set,
+in the lower case indicates to match if unset.
+
+Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN
+
+chunk type            available flags      
+.br
+DATA                  I U B E i u b e
+.br
+ABORT                 T t                 
+.br
+SHUTDOWN_COMPLETE     T t                 
+
+(lowercase means flag should be "off", uppercase means "on")
+.P
+Examples:
+
+iptables \-A INPUT \-p sctp \-\-dport 80 \-j DROP
+
+iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA,INIT \-j DROP
+
+iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA:Be \-j ACCEPT
diff --git a/extensions/libxt_set.c b/extensions/libxt_set.c
new file mode 100644
index 0000000..2cb9e78
--- /dev/null
+++ b/extensions/libxt_set.c
@@ -0,0 +1,562 @@
+/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
+ *                         Patrick Schaaf <bof@bof.de>
+ *                         Martin Josefsson <gandalf@wlug.westbo.se>
+ * Copyright (C) 2003-2010 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.  
+ */
+
+/* Shared library add-on to iptables to add IP set matching. */
+#include <stdbool.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <xtables.h>
+#include <linux/netfilter/xt_set.h>
+#include "libxt_set.h"
+
+/* Revision 0 */
+
+static void
+set_help_v0(void)
+{
+	printf("set match options:\n"
+	       " [!] --match-set name flags\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_v0[] = {
+	{.name = "match-set", .has_arg = true, .val = '1'},
+	{.name = "set",       .has_arg = true, .val = '2'},
+	XT_GETOPT_TABLEEND,
+};
+
+static void
+set_check_v0(unsigned int flags)
+{
+	if (!flags)
+		xtables_error(PARAMETER_PROBLEM,
+			"You must specify `--match-set' with proper arguments");
+}
+
+static int
+set_parse_v0(int c, char **argv, int invert, unsigned int *flags,
+	     const void *entry, struct xt_entry_match **match)
+{
+	struct xt_set_info_match_v0 *myinfo = 
+		(struct xt_set_info_match_v0 *) (*match)->data;
+	struct xt_set_info_v0 *info = &myinfo->match_set;
+
+	switch (c) {
+	case '2':
+		fprintf(stderr,
+			"--set option deprecated, please use --match-set\n");
+	case '1':		/* --match-set <set> <flag>[,<flag> */
+		if (info->u.flags[0])
+			xtables_error(PARAMETER_PROBLEM,
+				      "--match-set can be specified only once");
+		if (invert)
+			info->u.flags[0] |= IPSET_MATCH_INV;
+
+		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, (struct xt_set_info *)info);
+		parse_dirs_v0(argv[optind], info);
+		DEBUGP("parse: set index %u\n", info->index);
+		optind++;
+		
+		*flags = 1;
+		break;
+	}
+
+	return 1;
+}
+
+static void
+print_match_v0(const char *prefix, const struct xt_set_info_v0 *info)
+{
+	int i;
+	char setname[IPSET_MAXNAMELEN];
+
+	get_set_byid(setname, info->index);
+	printf("%s %s %s",
+	       (info->u.flags[0] & IPSET_MATCH_INV) ? " !" : "",
+	       prefix,
+	       setname); 
+	for (i = 0; i < IPSET_DIM_MAX; i++) {
+		if (!info->u.flags[i])
+			break;		
+		printf("%s%s",
+		       i == 0 ? " " : ",",
+		       info->u.flags[i] & IPSET_SRC ? "src" : "dst");
+	}
+}
+
+/* Prints out the matchinfo. */
+static void
+set_print_v0(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_set_info_match_v0 *info = (const void *)match->data;
+
+	print_match_v0("match-set", &info->match_set);
+}
+
+static void
+set_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_set_info_match_v0 *info = (const void *)match->data;
+
+	print_match_v0("--match-set", &info->match_set);
+}
+
+/* Revision 1 */
+static int
+set_parse_v1(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 '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;
+}
+
+static void
+print_match(const char *prefix, const struct xt_set_info *info)
+{
+	int i;
+	char setname[IPSET_MAXNAMELEN];
+
+	get_set_byid(setname, info->index);
+	printf("%s %s %s",
+	       (info->flags & IPSET_INV_MATCH) ? " !" : "",
+	       prefix,
+	       setname); 
+	for (i = 1; i <= info->dim; i++) {		
+		printf("%s%s",
+		       i == 1 ? " " : ",",
+		       info->flags & (1 << i) ? "src" : "dst");
+	}
+}
+
+/* Prints out the matchinfo. */
+static void
+set_print_v1(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);
+}
+
+static void
+set_save_v1(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);
+}
+
+/* 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",
+		.revision	= 0,
+		.version	= XTABLES_VERSION,
+		.family		= NFPROTO_IPV4,
+		.size		= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
+		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_match_v0)),
+		.help		= set_help_v0,
+		.parse		= set_parse_v0,
+		.final_check	= set_check_v0,
+		.print		= set_print_v0,
+		.save		= set_save_v0,
+		.extra_opts	= set_opts_v0,
+	},
+	{
+		.name		= "set",
+		.revision	= 1,
+		.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_v0,
+		.parse		= set_parse_v1,
+		.final_check	= set_check_v0,
+		.print		= set_print_v1,
+		.save		= set_save_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,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg));
+}
diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h
new file mode 100644
index 0000000..5a1bdcf
--- /dev/null
+++ b/extensions/libxt_set.h
@@ -0,0 +1,197 @@
+#ifndef _LIBXT_SET_H
+#define _LIBXT_SET_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include "../iptables/xshared.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x , ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int
+get_version(unsigned *version)
+{
+	int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	struct ip_set_req_version req_version;
+	socklen_t size = sizeof(req_version);
+	
+	if (sockfd < 0)
+		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)
+		xtables_error(OTHER_PROBLEM,
+			      "Kernel module xt_set is not loaded in.\n");
+
+	*version = req_version.version;
+	
+	return sockfd;
+}
+
+static void
+get_set_byid(char *setname, ip_set_id_t idx)
+{
+	struct ip_set_req_get_set req;
+	socklen_t size = sizeof(struct ip_set_req_get_set);
+	int res, sockfd;
+
+	sockfd = get_version(&req.version);
+	req.op = IP_SET_OP_GET_BYINDEX;
+	req.set.index = idx;
+	res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
+	close(sockfd);
+
+	if (res != 0)
+		xtables_error(OTHER_PROBLEM,
+			"Problem when communicating with ipset, errno=%d.\n",
+			errno);
+	if (size != sizeof(struct ip_set_req_get_set))
+		xtables_error(OTHER_PROBLEM,
+			"Incorrect return size from kernel during ipset lookup, "
+			"(want %zu, got %zu)\n",
+			sizeof(struct ip_set_req_get_set), (size_t)size);
+	if (req.set.name[0] == '\0')
+		xtables_error(PARAMETER_PROBLEM,
+			"Set with index %i in kernel doesn't exist.\n", idx);
+
+	strncpy(setname, req.set.name, IPSET_MAXNAMELEN);
+}
+
+static void
+get_set_byname_only(const char *setname, struct xt_set_info *info,
+		    int sockfd, unsigned int version)
+{
+	struct ip_set_req_get_set req = { .version = version };
+	socklen_t size = sizeof(struct ip_set_req_get_set);
+	int res;
+
+	req.op = IP_SET_OP_GET_BYNAME;
+	strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
+	req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
+	res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
+	close(sockfd);
+
+	if (res != 0)
+		xtables_error(OTHER_PROBLEM,
+			"Problem when communicating with ipset, errno=%d.\n",
+			errno);
+	if (size != sizeof(struct ip_set_req_get_set))
+		xtables_error(OTHER_PROBLEM,
+			"Incorrect return size from kernel during ipset lookup, "
+			"(want %zu, got %zu)\n",
+			sizeof(struct ip_set_req_get_set), (size_t)size);
+	if (req.set.index == IPSET_INVALID_ID)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Set %s doesn't exist.\n", setname);
+
+	info->index = req.set.index;
+}
+
+static void
+get_set_byname(const char *setname, struct xt_set_info *info)
+{
+	struct ip_set_req_get_set_family req;
+	socklen_t size = sizeof(struct ip_set_req_get_set_family);
+	int res, sockfd, version;
+
+	sockfd = get_version(&req.version);
+	version = req.version;
+	req.op = IP_SET_OP_GET_FNAME;
+	strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
+	req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
+	res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
+
+	if (res != 0 && errno == EBADMSG)
+		/* Backward compatibility */
+		return get_set_byname_only(setname, info, sockfd, version);
+
+	close(sockfd);
+	if (res != 0)
+		xtables_error(OTHER_PROBLEM,
+			"Problem when communicating with ipset, errno=%d.\n",
+			errno);
+	if (size != sizeof(struct ip_set_req_get_set_family))
+		xtables_error(OTHER_PROBLEM,
+			"Incorrect return size from kernel during ipset lookup, "
+			"(want %zu, got %zu)\n",
+			sizeof(struct ip_set_req_get_set_family),
+			(size_t)size);
+	if (req.set.index == IPSET_INVALID_ID)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Set %s doesn't exist.\n", setname);
+	if (!(req.family == afinfo->family ||
+	      req.family == NFPROTO_UNSPEC))
+		xtables_error(PARAMETER_PROBLEM,
+			      "The protocol family of set %s is %s, "
+			      "which is not applicable.\n",
+			      setname,
+			      req.family == NFPROTO_IPV4 ? "IPv4" : "IPv6");
+
+	info->index = req.set.index;
+}
+
+static void
+parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info)
+{
+	char *saved = strdup(opt_arg);
+	char *ptr, *tmp = saved;
+	int i = 0;
+	
+	while (i < (IPSET_DIM_MAX - 1) && tmp != NULL) {
+		ptr = strsep(&tmp, ",");
+		if (strncmp(ptr, "src", 3) == 0)
+			info->u.flags[i++] |= IPSET_SRC;
+		else if (strncmp(ptr, "dst", 3) == 0)
+			info->u.flags[i++] |= IPSET_DST;
+		else
+			xtables_error(PARAMETER_PROBLEM,
+				"You must spefify (the comma separated list of) 'src' or 'dst'.");
+	}
+
+	if (tmp)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Can't be more src/dst options than %i.", 
+			      IPSET_DIM_MAX);
+
+	free(saved);
+}
+
+static void
+parse_dirs(const char *opt_arg, struct xt_set_info *info)
+{
+	char *saved = strdup(opt_arg);
+	char *ptr, *tmp = saved;
+	
+	while (info->dim < IPSET_DIM_MAX && tmp != NULL) {
+		info->dim++;
+		ptr = strsep(&tmp, ",");
+		if (strncmp(ptr, "src", 3) == 0)
+			info->flags |= (1 << info->dim);
+		else if (strncmp(ptr, "dst", 3) != 0)
+			xtables_error(PARAMETER_PROBLEM,
+				"You must spefify (the comma separated list of) 'src' or 'dst'.");
+	}
+
+	if (tmp)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Can't be more src/dst options than %i.", 
+			      IPSET_DIM_MAX);
+
+	free(saved);
+}
+
+#endif /*_LIBXT_SET_H*/
diff --git a/extensions/libxt_set.man b/extensions/libxt_set.man
new file mode 100644
index 0000000..7012ef2
--- /dev/null
+++ b/extensions/libxt_set.man
@@ -0,0 +1,65 @@
+This module matches IP sets which can be defined by ipset(8).
+.TP
+[\fB!\fP] \fB\-\-match\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP]...
+where flags are the comma separated list of
+.BR "src"
+and/or
+.BR "dst" 
+specifications and there can be no more than six of them. Hence the command
+.IP
+ iptables \-A FORWARD \-m set \-\-match\-set test src,dst
+.IP
+will match packets, for which (if the set type is ipportmap) the source
+address and destination port pair can be found in the specified set. If
+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, which, for
+standard kernels, is the case since Linux 2.6.39.
diff --git a/extensions/libxt_socket.c b/extensions/libxt_socket.c
new file mode 100644
index 0000000..f19c280
--- /dev/null
+++ b/extensions/libxt_socket.c
@@ -0,0 +1,144 @@
+/*
+ * Shared library add-on to iptables to add early socket matching support.
+ *
+ * Copyright (C) 2007 BalaBit IT Ltd.
+ */
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_socket.h>
+
+enum {
+	O_TRANSPARENT = 0,
+	O_NOWILDCARD = 1,
+};
+
+static const struct xt_option_entry socket_mt_opts[] = {
+	{.name = "transparent", .id = O_TRANSPARENT, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry socket_mt_opts_v2[] = {
+	{.name = "transparent", .id = O_TRANSPARENT, .type = XTTYPE_NONE},
+	{.name = "nowildcard", .id = O_NOWILDCARD, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+
+static void socket_mt_help(void)
+{
+	printf(
+		"socket match options:\n"
+		"  --transparent    Ignore non-transparent sockets\n\n");
+}
+
+static void socket_mt_help_v2(void)
+{
+	printf(
+		"socket match options:\n"
+		"  --nowildcard     Do not ignore LISTEN sockets bound on INADDR_ANY\n"
+		"  --transparent    Ignore non-transparent sockets\n\n");
+}
+
+static void socket_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_socket_mtinfo1 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TRANSPARENT:
+		info->flags |= XT_SOCKET_TRANSPARENT;
+		break;
+	}
+}
+
+static void socket_mt_parse_v2(struct xt_option_call *cb)
+{
+	struct xt_socket_mtinfo2 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_TRANSPARENT:
+		info->flags |= XT_SOCKET_TRANSPARENT;
+		break;
+	case O_NOWILDCARD:
+		info->flags |= XT_SOCKET_NOWILDCARD;
+		break;
+	}
+}
+
+static void
+socket_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_socket_mtinfo1 *info = (const void *)match->data;
+
+	if (info->flags & XT_SOCKET_TRANSPARENT)
+		printf(" --transparent");
+}
+
+static void
+socket_mt_print(const void *ip, const struct xt_entry_match *match,
+		int numeric)
+{
+	printf(" socket");
+	socket_mt_save(ip, match);
+}
+
+static void
+socket_mt_save_v2(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_socket_mtinfo2 *info = (const void *)match->data;
+
+	if (info->flags & XT_SOCKET_TRANSPARENT)
+		printf(" --transparent");
+	if (info->flags & XT_SOCKET_NOWILDCARD)
+		printf(" --nowildcard");
+}
+
+static void
+socket_mt_print_v2(const void *ip, const struct xt_entry_match *match,
+		   int numeric)
+{
+	printf(" socket");
+	socket_mt_save_v2(ip, match);
+}
+
+static struct xtables_match socket_mt_reg[] = {
+	{
+		.name          = "socket",
+		.revision      = 0,
+		.family        = NFPROTO_IPV4,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(0),
+		.userspacesize = XT_ALIGN(0),
+	},
+	{
+		.name          = "socket",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
+		.help          = socket_mt_help,
+		.print         = socket_mt_print,
+		.save          = socket_mt_save,
+		.x6_parse      = socket_mt_parse,
+		.x6_options    = socket_mt_opts,
+	},
+	{
+		.name          = "socket",
+		.revision      = 2,
+		.family        = NFPROTO_UNSPEC,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_socket_mtinfo2)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_socket_mtinfo2)),
+		.help          = socket_mt_help_v2,
+		.print         = socket_mt_print_v2,
+		.save          = socket_mt_save_v2,
+		.x6_parse      = socket_mt_parse_v2,
+		.x6_options    = socket_mt_opts_v2,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
+}
diff --git a/extensions/libxt_socket.man b/extensions/libxt_socket.man
new file mode 100644
index 0000000..2ef32ce
--- /dev/null
+++ b/extensions/libxt_socket.man
@@ -0,0 +1,22 @@
+This matches if an open TCP/UDP socket can be found by doing a socket lookup on the
+packet. It matches if there is an established or non\-zero bound listening
+socket (possibly with a non\-local address). The lookup is performed using
+the \fBpacket\fP tuple of TCP/UDP packets, or the original TCP/UDP header
+\fBembedded\fP in an ICMP/ICPMv6 error packet.
+.TP
+\fB\-\-transparent\fP
+Ignore non-transparent sockets.
+.TP
+\fB\-\-nowildcard\fP
+Do not ignore sockets bound to 'any' address.
+The socket match won't accept zero\-bound listeners by default, since
+then local services could intercept traffic that would otherwise be forwarded.
+This option therefore has security implications when used to match traffic being
+forwarded to redirect such packets to local machine with policy routing.
+When using the socket match to implement fully transparent
+proxies bound to non\-local addresses it is recommended to use the \-\-transparent
+option instead.
+.PP
+Example (assuming packets with mark 1 are delivered locally):
+.IP
+\-t mangle \-A PREROUTING \-m socket \-\-transparent \-j MARK \-\-set\-mark 1
diff --git a/extensions/libxt_standard.c b/extensions/libxt_standard.c
new file mode 100644
index 0000000..c64ba29
--- /dev/null
+++ b/extensions/libxt_standard.c
@@ -0,0 +1,24 @@
+/* Shared library add-on to iptables for standard target support. */
+#include <stdio.h>
+#include <xtables.h>
+
+static void standard_help(void)
+{
+	printf(
+"standard match options:\n"
+"(If target is DROP, ACCEPT, RETURN or nothing)\n");
+}
+
+static struct xtables_target standard_target = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "standard",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(int)),
+	.userspacesize	= XT_ALIGN(sizeof(int)),
+	.help		= standard_help,
+};
+
+void _init(void)
+{
+	xtables_register_target(&standard_target);
+}
diff --git a/extensions/libxt_state.man b/extensions/libxt_state.man
new file mode 100644
index 0000000..ec096ca
--- /dev/null
+++ b/extensions/libxt_state.man
@@ -0,0 +1,8 @@
+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. 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
new file mode 100644
index 0000000..b6ae5f5
--- /dev/null
+++ b/extensions/libxt_statistic.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2006-2013 Patrick McHardy <kaber@trash.net>
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_statistic.h>
+
+enum {
+	O_MODE = 0,
+	O_PROBABILITY,
+	O_EVERY,
+	O_PACKET,
+	F_PROBABILITY = 1 << O_PROBABILITY,
+	F_EVERY       = 1 << O_EVERY,
+	F_PACKET      = 1 << O_PACKET,
+};
+
+static void statistic_help(void)
+{
+	printf(
+"statistic match options:\n"
+" --mode mode                    Match mode (random, nth)\n"
+" random mode:\n"
+"[!] --probability p		 Probability\n"
+" nth mode:\n"
+"[!] --every n			 Match every nth packet\n"
+" --packet p			 Initial counter value (0 <= p <= n-1, default 0)\n");
+}
+
+#define s struct xt_statistic_info
+static const struct xt_option_entry statistic_opts[] = {
+	{.name = "mode", .id = O_MODE, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND},
+	{.name = "probability", .id = O_PROBABILITY, .type = XTTYPE_DOUBLE,
+	 .flags = XTOPT_INVERT, .min = 0, .max = 1,
+	 .excl = F_EVERY | F_PACKET},
+	{.name = "every", .id = O_EVERY, .type = XTTYPE_UINT32, .min = 1,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, u.nth.every),
+	 .excl = F_PROBABILITY, .also = F_PACKET},
+	{.name = "packet", .id = O_PACKET, .type = XTTYPE_UINT32,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, u.nth.packet),
+	 .excl = F_PROBABILITY, .also = F_EVERY},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void statistic_parse(struct xt_option_call *cb)
+{
+	struct xt_statistic_info *info = cb->data;
+
+	if (cb->invert)
+		info->flags |= XT_STATISTIC_INVERT;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_MODE:
+		if (strcmp(cb->arg, "random") == 0)
+			info->mode = XT_STATISTIC_MODE_RANDOM;
+		else if (strcmp(cb->arg, "nth") == 0)
+			info->mode = XT_STATISTIC_MODE_NTH;
+		else
+			xtables_error(PARAMETER_PROBLEM, "Bad mode \"%s\"",
+				cb->arg);
+		break;
+	case O_PROBABILITY:
+		info->u.random.probability = lround(0x80000000 * cb->val.dbl);
+		break;
+	case O_EVERY:
+		--info->u.nth.every;
+		break;
+	}
+}
+
+static void statistic_check(struct xt_fcheck_call *cb)
+{
+	struct xt_statistic_info *info = cb->data;
+
+	if (info->mode == XT_STATISTIC_MODE_RANDOM &&
+	    !(cb->xflags & F_PROBABILITY))
+		xtables_error(PARAMETER_PROBLEM,
+			"--probability must be specified when using "
+			"random mode");
+	if (info->mode == XT_STATISTIC_MODE_NTH &&
+	    !(cb->xflags & (F_EVERY | F_PACKET)))
+		xtables_error(PARAMETER_PROBLEM,
+			"--every and --packet must be specified when "
+			"using nth mode");
+
+	/* at this point, info->u.nth.every have been decreased. */
+	if (info->u.nth.packet > info->u.nth.every)
+		xtables_error(PARAMETER_PROBLEM,
+			  "the --packet p must be 0 <= p <= n-1");
+
+	info->u.nth.count = info->u.nth.every - info->u.nth.packet;
+}
+
+static void print_match(const struct xt_statistic_info *info, char *prefix)
+{
+	switch (info->mode) {
+	case XT_STATISTIC_MODE_RANDOM:
+		printf(" %smode random%s %sprobability %.11f", prefix,
+		       (info->flags & XT_STATISTIC_INVERT) ? " !" : "",
+		       prefix,
+		       1.0 * info->u.random.probability / 0x80000000);
+		break;
+	case XT_STATISTIC_MODE_NTH:
+		printf(" %smode nth%s %severy %u", prefix,
+		       (info->flags & XT_STATISTIC_INVERT) ? " !" : "",
+		       prefix,
+		       info->u.nth.every + 1);
+		if (info->u.nth.packet || *prefix)
+			printf(" %spacket %u", prefix, info->u.nth.packet);
+		break;
+	}
+}
+
+static void
+statistic_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_statistic_info *info = (const void *)match->data;
+
+	printf(" statistic");
+	print_match(info, "");
+}
+
+static void statistic_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_statistic_info *info = (const void *)match->data;
+
+	print_match(info, "--");
+}
+
+static struct xtables_match statistic_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "statistic",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_statistic_info)),
+	.userspacesize	= offsetof(struct xt_statistic_info, u.nth.count),
+	.help		= statistic_help,
+	.x6_parse	= statistic_parse,
+	.x6_fcheck	= statistic_check,
+	.print		= statistic_print,
+	.save		= statistic_save,
+	.x6_options	= statistic_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&statistic_match);
+}
diff --git a/extensions/libxt_statistic.man b/extensions/libxt_statistic.man
new file mode 100644
index 0000000..47182bf
--- /dev/null
+++ b/extensions/libxt_statistic.man
@@ -0,0 +1,29 @@
+This module matches packets based on some statistic condition.
+It supports two distinct modes settable with the 
+\fB\-\-mode\fP
+option.
+.PP
+Supported options:
+.TP
+\fB\-\-mode\fP \fImode\fP
+Set the matching mode of the matching rule, supported modes are
+.B random
+and
+.B nth. 
+.TP
+[\fB!\fP] \fB\-\-probability\fP \fIp\fP
+Set the probability for a packet to be randomly matched. It only works with the
+\fBrandom\fP mode. \fIp\fP must be within 0.0 and 1.0. The supported
+granularity is in 1/2147483648th increments.
+.TP
+[\fB!\fP] \fB\-\-every\fP \fIn\fP
+Match one packet every nth packet. It works only with the
+.B nth
+mode (see also the 
+\fB\-\-packet\fP
+option).
+.TP
+\fB\-\-packet\fP \fIp\fP
+Set the initial counter value (0 <= p <= n\-1, default 0) for the
+.B nth 
+mode.
diff --git a/extensions/libxt_string.c b/extensions/libxt_string.c
new file mode 100644
index 0000000..fb15980
--- /dev/null
+++ b/extensions/libxt_string.c
@@ -0,0 +1,338 @@
+/* Shared library add-on to iptables to add string matching support. 
+ * 
+ * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
+ *
+ * 2005-08-05 Pablo Neira Ayuso <pablo@eurodev.net>
+ * 	- reimplemented to use new string matching iptables match
+ * 	- add functionality to match packets by using window offsets
+ * 	- add functionality to select the string matching algorithm
+ *
+ * ChangeLog
+ *     29.12.2003: Michael Rash <mbr@cipherdyne.org>
+ *             Fixed iptables save/restore for ascii strings
+ *             that contain space chars, and hex strings that
+ *             contain embedded NULL chars.  Updated to print
+ *             strings in hex mode if any non-printable char
+ *             is contained within the string.
+ *
+ *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
+ *             Changed --tos to --string in save(). Also
+ *             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>
+#include <ctype.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_string.h>
+
+enum {
+	O_FROM = 0,
+	O_TO,
+	O_ALGO,
+	O_ICASE,
+	O_STRING,
+	O_HEX_STRING,
+	F_STRING     = 1 << O_STRING,
+	F_HEX_STRING = 1 << O_HEX_STRING,
+	F_OP_ANY     = F_STRING | F_HEX_STRING,
+};
+
+static void string_help(void)
+{
+	printf(
+"string match options:\n"
+"--from                       Offset to start searching from\n"
+"--to                         Offset to stop searching\n"
+"--algo                       Algorithm\n"
+"--icase                      Ignore case (default: 0)\n"
+"[!] --string string          Match a string in a packet\n"
+"[!] --hex-string string      Match a hex string in a packet\n");
+}
+
+#define s struct xt_string_info
+static const struct xt_option_entry string_opts[] = {
+	{.name = "from", .id = O_FROM, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, from_offset)},
+	{.name = "to", .id = O_TO, .type = XTTYPE_UINT16,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, to_offset)},
+	{.name = "algo", .id = O_ALGO, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, algo)},
+	{.name = "string", .id = O_STRING, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT, .excl = F_HEX_STRING},
+	{.name = "hex-string", .id = O_HEX_STRING, .type = XTTYPE_STRING,
+	 .flags = XTOPT_INVERT, .excl = F_STRING},
+	{.name = "icase", .id = O_ICASE, .type = XTTYPE_NONE},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void string_init(struct xt_entry_match *m)
+{
+	struct xt_string_info *i = (struct xt_string_info *) m->data;
+
+	i->to_offset = UINT16_MAX;
+}
+
+static void
+parse_string(const char *s, struct xt_string_info *info)
+{	
+	/* xt_string does not need \0 at the end of the pattern */
+	if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
+		strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
+		info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE);
+		return;
+	}
+	xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
+}
+
+static void
+parse_hex_string(const char *s, struct xt_string_info *info)
+{
+	int i=0, slen, sindex=0, schar;
+	short hex_f = 0, literal_f = 0;
+	char hextmp[3];
+
+	slen = strlen(s);
+
+	if (slen == 0) {
+		xtables_error(PARAMETER_PROBLEM,
+			"STRING must contain at least one char");
+	}
+
+	while (i < slen) {
+		if (s[i] == '\\' && !hex_f) {
+			literal_f = 1;
+		} else if (s[i] == '\\') {
+			xtables_error(PARAMETER_PROBLEM,
+				"Cannot include literals in hex data");
+		} else if (s[i] == '|') {
+			if (hex_f)
+				hex_f = 0;
+			else {
+				hex_f = 1;
+				/* get past any initial whitespace just after the '|' */
+				while (s[i+1] == ' ')
+					i++;
+			}
+			if (i+1 >= slen)
+				break;
+			else
+				i++;  /* advance to the next character */
+		}
+
+		if (literal_f) {
+			if (i+1 >= slen) {
+				xtables_error(PARAMETER_PROBLEM,
+					"Bad literal placement at end of string");
+			}
+			info->pattern[sindex] = s[i+1];
+			i += 2;  /* skip over literal char */
+			literal_f = 0;
+		} else if (hex_f) {
+			if (i+1 >= slen) {
+				xtables_error(PARAMETER_PROBLEM,
+					"Odd number of hex digits");
+			}
+			if (i+2 >= slen) {
+				/* must end with a "|" */
+				xtables_error(PARAMETER_PROBLEM, "Invalid hex block");
+			}
+			if (! isxdigit(s[i])) /* check for valid hex char */
+				xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]);
+			if (! isxdigit(s[i+1])) /* check for valid hex char */
+				xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]);
+			hextmp[0] = s[i];
+			hextmp[1] = s[i+1];
+			hextmp[2] = '\0';
+			if (! sscanf(hextmp, "%x", &schar))
+				xtables_error(PARAMETER_PROBLEM,
+					"Invalid hex char `%c'", s[i]);
+			info->pattern[sindex] = (char) schar;
+			if (s[i+2] == ' ')
+				i += 3;  /* spaces included in the hex block */
+			else
+				i += 2;
+		} else {  /* the char is not part of hex data, so just copy */
+			info->pattern[sindex] = s[i];
+			i++;
+		}
+		if (sindex > XT_STRING_MAX_PATTERN_SIZE)
+			xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
+		sindex++;
+	}
+	info->patlen = sindex;
+}
+
+static void string_parse(struct xt_option_call *cb)
+{
+	struct xt_string_info *stringinfo = cb->data;
+	const unsigned int revision = (*cb->match)->u.user.revision;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_STRING:
+		parse_string(cb->arg, stringinfo);
+		if (cb->invert) {
+			if (revision == 0)
+				stringinfo->u.v0.invert = 1;
+			else
+				stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
+		}
+		break;
+	case O_HEX_STRING:
+		parse_hex_string(cb->arg, stringinfo);  /* sets length */
+		if (cb->invert) {
+			if (revision == 0)
+				stringinfo->u.v0.invert = 1;
+			else
+				stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
+		}
+		break;
+	case O_ICASE:
+		if (revision == 0)
+			xtables_error(VERSION_PROBLEM,
+				   "Kernel doesn't support --icase");
+
+		stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE;
+		break;
+	}
+}
+
+static void string_check(struct xt_fcheck_call *cb)
+{
+	if (!(cb->xflags & F_OP_ANY))
+		xtables_error(PARAMETER_PROBLEM,
+			   "STRING match: You must specify `--string' or "
+			   "`--hex-string'");
+}
+
+/* Test to see if the string contains non-printable chars or quotes */
+static unsigned short int
+is_hex_string(const char *str, const unsigned short int len)
+{
+	unsigned int i;
+	for (i=0; i < len; i++)
+		if (! isprint(str[i]))
+			return 1;  /* string contains at least one non-printable char */
+	/* use hex output if the last char is a "\" */
+	if (str[len-1] == '\\')
+		return 1;
+	return 0;
+}
+
+/* Print string with "|" chars included as one would pass to --hex-string */
+static void
+print_hex_string(const char *str, const unsigned short int len)
+{
+	unsigned int i;
+	/* start hex block */
+	printf(" \"|");
+	for (i=0; i < len; i++)
+		printf("%02x", (unsigned char)str[i]);
+	/* close hex block */
+	printf("|\"");
+}
+
+static void
+print_string(const char *str, const unsigned short int len)
+{
+	unsigned int i;
+	printf(" \"");
+	for (i=0; i < len; i++) {
+		if (str[i] == '\"' || str[i] == '\\')
+			putchar('\\');
+		printf("%c", (unsigned char) str[i]);
+	}
+	printf("\"");  /* closing quote */
+}
+
+static void
+string_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_string_info *info =
+	    (const struct xt_string_info*) match->data;
+	const int revision = match->u.user.revision;
+	int invert = (revision == 0 ? info->u.v0.invert :
+				    info->u.v1.flags & XT_STRING_FLAG_INVERT);
+
+	if (is_hex_string(info->pattern, info->patlen)) {
+		printf(" STRING match %s", invert ? "!" : "");
+		print_hex_string(info->pattern, info->patlen);
+	} else {
+		printf(" STRING match %s", invert ? "!" : "");
+		print_string(info->pattern, info->patlen);
+	}
+	printf(" ALGO name %s", info->algo);
+	if (info->from_offset != 0)
+		printf(" FROM %u", info->from_offset);
+	if (info->to_offset != 0)
+		printf(" TO %u", info->to_offset);
+	if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
+		printf(" ICASE");
+}
+
+static void string_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_string_info *info =
+	    (const struct xt_string_info*) match->data;
+	const int revision = match->u.user.revision;
+	int invert = (revision == 0 ? info->u.v0.invert :
+				    info->u.v1.flags & XT_STRING_FLAG_INVERT);
+
+	if (is_hex_string(info->pattern, info->patlen)) {
+		printf("%s --hex-string", (invert) ? " !" : "");
+		print_hex_string(info->pattern, info->patlen);
+	} else {
+		printf("%s --string", (invert) ? " !": "");
+		print_string(info->pattern, info->patlen);
+	}
+	printf(" --algo %s", info->algo);
+	if (info->from_offset != 0)
+		printf(" --from %u", info->from_offset);
+	if (info->to_offset != 0)
+		printf(" --to %u", info->to_offset);
+	if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
+		printf(" --icase");
+}
+
+
+static struct xtables_match string_mt_reg[] = {
+	{
+		.name          = "string",
+		.revision      = 0,
+		.family        = NFPROTO_UNSPEC,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_string_info)),
+		.userspacesize = offsetof(struct xt_string_info, config),
+		.help          = string_help,
+		.init          = string_init,
+		.print         = string_print,
+		.save          = string_save,
+		.x6_parse      = string_parse,
+		.x6_fcheck     = string_check,
+		.x6_options    = string_opts,
+	},
+	{
+		.name          = "string",
+		.revision      = 1,
+		.family        = NFPROTO_UNSPEC,
+		.version       = XTABLES_VERSION,
+		.size          = XT_ALIGN(sizeof(struct xt_string_info)),
+		.userspacesize = offsetof(struct xt_string_info, config),
+		.help          = string_help,
+		.init          = string_init,
+		.print         = string_print,
+		.save          = string_save,
+		.x6_parse      = string_parse,
+		.x6_fcheck     = string_check,
+		.x6_options    = string_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg));
+}
diff --git a/extensions/libxt_string.man b/extensions/libxt_string.man
new file mode 100644
index 0000000..adc9c18
--- /dev/null
+++ b/extensions/libxt_string.man
@@ -0,0 +1,28 @@
+This modules matches a given string by using some pattern matching strategy. It requires a linux kernel >= 2.6.14.
+.TP
+\fB\-\-algo\fP {\fBbm\fP|\fBkmp\fP}
+Select the pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)
+.TP
+\fB\-\-from\fP \fIoffset\fP
+Set the offset from which it starts looking for any matching. If not passed, default is 0.
+.TP
+\fB\-\-to\fP \fIoffset\fP
+Set the offset up to which should be scanned. That is, byte \fIoffset\fP-1
+(counting from 0) is the last one that is scanned.
+If not passed, default is the packet size.
+.TP
+[\fB!\fP] \fB\-\-string\fP \fIpattern\fP
+Matches the given pattern.
+.TP
+[\fB!\fP] \fB\-\-hex\-string\fP \fIpattern\fP
+Matches the given pattern in hex notation.
+.TP
+Examples:
+.IP
+# The string pattern can be used for simple text characters.
+.br
+iptables \-A INPUT \-p tcp \-\-dport 80 \-m string \-\-algo bm \-\-string 'GET /index.html' \-j LOG
+.IP
+# The hex string pattern can be used for non-printable characters, like |0D 0A| or |0D0A|.
+.br
+iptables \-p udp \-\-dport 53 \-m string \-\-algo bm \-\-from 40 \-\-to 57 \-\-hex\-string '|03|www|09|netfilter|03|org|00|'
diff --git a/extensions/libxt_tcp.c b/extensions/libxt_tcp.c
new file mode 100644
index 0000000..bbdec45
--- /dev/null
+++ b/extensions/libxt_tcp.c
@@ -0,0 +1,383 @@
+/* Shared library add-on to iptables to add TCP support. */
+#include <stdbool.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_tcpudp.h>
+
+static void tcp_help(void)
+{
+	printf(
+"tcp match options:\n"
+"[!] --tcp-flags mask comp	match when TCP flags & mask == comp\n"
+"				(Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
+"[!] --syn			match when only SYN flag set\n"
+"				(equivalent to --tcp-flags SYN,RST,ACK,FIN SYN)\n"
+"[!] --source-port port[:port]\n"
+" --sport ...\n"
+"				match source port(s)\n"
+"[!] --destination-port port[:port]\n"
+" --dport ...\n"
+"				match destination port(s)\n"
+"[!] --tcp-option number        match if TCP option set\n");
+}
+
+static const struct option tcp_opts[] = {
+	{.name = "source-port",      .has_arg = true,  .val = '1'},
+	{.name = "sport",            .has_arg = true,  .val = '1'}, /* synonym */
+	{.name = "destination-port", .has_arg = true,  .val = '2'},
+	{.name = "dport",            .has_arg = true,  .val = '2'}, /* synonym */
+	{.name = "syn",              .has_arg = false, .val = '3'},
+	{.name = "tcp-flags",        .has_arg = true,  .val = '4'},
+	{.name = "tcp-option",       .has_arg = true,  .val = '5'},
+	XT_GETOPT_TABLEEND,
+};
+
+static void
+parse_tcp_ports(const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = xtables_parse_port(buffer, "tcp");
+	else {
+		*cp = '\0';
+		cp++;
+
+		ports[0] = buffer[0] ? xtables_parse_port(buffer, "tcp") : 0;
+		ports[1] = cp[0] ? xtables_parse_port(cp, "tcp") : 0xFFFF;
+
+		if (ports[0] > ports[1])
+			xtables_error(PARAMETER_PROBLEM,
+				   "invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+struct tcp_flag_names {
+	const char *name;
+	unsigned int flag;
+};
+
+static const struct tcp_flag_names tcp_flag_names[]
+= { { "FIN", 0x01 },
+    { "SYN", 0x02 },
+    { "RST", 0x04 },
+    { "PSH", 0x08 },
+    { "ACK", 0x10 },
+    { "URG", 0x20 },
+    { "ALL", 0x3F },
+    { "NONE", 0 },
+};
+
+static unsigned int
+parse_tcp_flag(const char *flags)
+{
+	unsigned int ret = 0;
+	char *ptr;
+	char *buffer;
+
+	buffer = strdup(flags);
+
+	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+		unsigned int i;
+		for (i = 0; i < ARRAY_SIZE(tcp_flag_names); ++i)
+			if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
+				ret |= tcp_flag_names[i].flag;
+				break;
+			}
+		if (i == ARRAY_SIZE(tcp_flag_names))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Unknown TCP flag `%s'", ptr);
+	}
+
+	free(buffer);
+	return ret;
+}
+
+static void
+parse_tcp_flags(struct xt_tcp *tcpinfo,
+		const char *mask,
+		const char *cmp,
+		int invert)
+{
+	tcpinfo->flg_mask = parse_tcp_flag(mask);
+	tcpinfo->flg_cmp = parse_tcp_flag(cmp);
+
+	if (invert)
+		tcpinfo->invflags |= XT_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, uint8_t *result)
+{
+	unsigned int ret;
+
+	if (!xtables_strtoui(option, NULL, &ret, 1, UINT8_MAX))
+		xtables_error(PARAMETER_PROBLEM, "Bad TCP option \"%s\"", option);
+
+	*result = ret;
+}
+
+static void tcp_init(struct xt_entry_match *m)
+{
+	struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data;
+
+	tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
+}
+
+#define TCP_SRC_PORTS 0x01
+#define TCP_DST_PORTS 0x02
+#define TCP_FLAGS 0x04
+#define TCP_OPTION	0x08
+
+static int
+tcp_parse(int c, char **argv, int invert, unsigned int *flags,
+          const void *entry, struct xt_entry_match **match)
+{
+	struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data;
+
+	switch (c) {
+	case '1':
+		if (*flags & TCP_SRC_PORTS)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one `--source-port' allowed");
+		parse_tcp_ports(optarg, tcpinfo->spts);
+		if (invert)
+			tcpinfo->invflags |= XT_TCP_INV_SRCPT;
+		*flags |= TCP_SRC_PORTS;
+		break;
+
+	case '2':
+		if (*flags & TCP_DST_PORTS)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one `--destination-port' allowed");
+		parse_tcp_ports(optarg, tcpinfo->dpts);
+		if (invert)
+			tcpinfo->invflags |= XT_TCP_INV_DSTPT;
+		*flags |= TCP_DST_PORTS;
+		break;
+
+	case '3':
+		if (*flags & TCP_FLAGS)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one of `--syn' or `--tcp-flags' "
+				   " allowed");
+		parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
+		*flags |= TCP_FLAGS;
+		break;
+
+	case '4':
+		if (*flags & TCP_FLAGS)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one of `--syn' or `--tcp-flags' "
+				   " allowed");
+		if (!argv[optind]
+		    || argv[optind][0] == '-' || argv[optind][0] == '!')
+			xtables_error(PARAMETER_PROBLEM,
+				   "--tcp-flags requires two args.");
+
+		parse_tcp_flags(tcpinfo, optarg, argv[optind],
+				invert);
+		optind++;
+		*flags |= TCP_FLAGS;
+		break;
+
+	case '5':
+		if (*flags & TCP_OPTION)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Only one `--tcp-option' allowed");
+		parse_tcp_option(optarg, &tcpinfo->option);
+		if (invert)
+			tcpinfo->invflags |= XT_TCP_INV_OPTION;
+		*flags |= TCP_OPTION;
+		break;
+	}
+
+	return 1;
+}
+
+static const char *
+port_to_service(int port)
+{
+	const struct servent *service;
+
+	if ((service = getservbyport(htons(port), "tcp")))
+		return service->s_name;
+
+	return NULL;
+}
+
+static void
+print_port(uint16_t port, int numeric)
+{
+	const char *service;
+
+	if (numeric || (service = port_to_service(port)) == NULL)
+		printf("%u", port);
+	else
+		printf("%s", service);
+}
+
+static void
+print_ports(const char *name, uint16_t min, uint16_t max,
+	    int invert, int numeric)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFF || invert) {
+		printf(" %s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			print_port(min, numeric);
+		} else {
+			printf("s:%s", inv);
+			print_port(min, numeric);
+			printf(":");
+			print_port(max, numeric);
+		}
+	}
+}
+
+static void
+print_option(uint8_t option, int invert, int numeric)
+{
+	if (option || invert)
+		printf(" option=%s%u", invert ? "!" : "", option);
+}
+
+static void
+print_tcpf(uint8_t flags)
+{
+	int have_flag = 0;
+
+	while (flags) {
+		unsigned int i;
+
+		for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+		if (have_flag)
+			printf(",");
+		printf("%s", tcp_flag_names[i].name);
+		have_flag = 1;
+
+		flags &= ~tcp_flag_names[i].flag;
+	}
+
+	if (!have_flag)
+		printf("NONE");
+}
+
+static void
+print_flags(uint8_t mask, uint8_t cmp, int invert, int numeric)
+{
+	if (mask || invert) {
+		printf(" flags:%s", invert ? "!" : "");
+		if (numeric)
+			printf("0x%02X/0x%02X", mask, cmp);
+		else {
+			print_tcpf(mask);
+			printf("/");
+			print_tcpf(cmp);
+		}
+	}
+}
+
+static void
+tcp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_tcp *tcp = (struct xt_tcp *)match->data;
+
+	printf(" tcp");
+	print_ports("spt", tcp->spts[0], tcp->spts[1],
+		    tcp->invflags & XT_TCP_INV_SRCPT,
+		    numeric);
+	print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+		    tcp->invflags & XT_TCP_INV_DSTPT,
+		    numeric);
+	print_option(tcp->option,
+		     tcp->invflags & XT_TCP_INV_OPTION,
+		     numeric);
+	print_flags(tcp->flg_mask, tcp->flg_cmp,
+		    tcp->invflags & XT_TCP_INV_FLAGS,
+		    numeric);
+	if (tcp->invflags & ~XT_TCP_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       tcp->invflags & ~XT_TCP_INV_MASK);
+}
+
+static void tcp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data;
+
+	if (tcpinfo->spts[0] != 0
+	    || tcpinfo->spts[1] != 0xFFFF) {
+		if (tcpinfo->invflags & XT_TCP_INV_SRCPT)
+			printf(" !");
+		if (tcpinfo->spts[0]
+		    != tcpinfo->spts[1])
+			printf(" --sport %u:%u",
+			       tcpinfo->spts[0],
+			       tcpinfo->spts[1]);
+		else
+			printf(" --sport %u",
+			       tcpinfo->spts[0]);
+	}
+
+	if (tcpinfo->dpts[0] != 0
+	    || tcpinfo->dpts[1] != 0xFFFF) {
+		if (tcpinfo->invflags & XT_TCP_INV_DSTPT)
+			printf(" !");
+		if (tcpinfo->dpts[0]
+		    != tcpinfo->dpts[1])
+			printf(" --dport %u:%u",
+			       tcpinfo->dpts[0],
+			       tcpinfo->dpts[1]);
+		else
+			printf(" --dport %u",
+			       tcpinfo->dpts[0]);
+	}
+
+	if (tcpinfo->option
+	    || (tcpinfo->invflags & XT_TCP_INV_OPTION)) {
+		if (tcpinfo->invflags & XT_TCP_INV_OPTION)
+			printf(" !");
+		printf(" --tcp-option %u", tcpinfo->option);
+	}
+
+	if (tcpinfo->flg_mask
+	    || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) {
+		if (tcpinfo->invflags & XT_TCP_INV_FLAGS)
+			printf(" !");
+		printf(" --tcp-flags ");
+		print_tcpf(tcpinfo->flg_mask);
+		printf(" ");
+		print_tcpf(tcpinfo->flg_cmp);
+	}
+}
+
+static struct xtables_match tcp_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "tcp",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_tcp)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_tcp)),
+	.help		= tcp_help,
+	.init		= tcp_init,
+	.parse		= tcp_parse,
+	.print		= tcp_print,
+	.save		= tcp_save,
+	.extra_opts	= tcp_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&tcp_match);
+}
diff --git a/extensions/libxt_tcp.man b/extensions/libxt_tcp.man
new file mode 100644
index 0000000..7a16118
--- /dev/null
+++ b/extensions/libxt_tcp.man
@@ -0,0 +1,44 @@
+These extensions can be used if `\-\-protocol tcp' is specified. It
+provides the following options:
+.TP
+[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP]
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format \fIfirst\fP\fB:\fP\fIlast\fP.
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the first port is greater than the second one they will be swapped.
+The flag
+\fB\-\-sport\fP
+is a convenient alias for this option.
+.TP
+[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP]
+Destination port or port range specification.  The flag
+\fB\-\-dport\fP
+is a convenient alias for this option.
+.TP
+[\fB!\fP] \fB\-\-tcp\-flags\fP \fImask\fP \fIcomp\fP
+Match when the TCP flags are as specified.  The first argument \fImask\fP is the
+flags which we should examine, written as a comma-separated list, and
+the second argument \fIcomp\fP is a comma-separated list of flags which must be
+set.  Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ iptables \-A FORWARD \-p tcp \-\-tcp\-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+[\fB!\fP] \fB\-\-syn\fP
+Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits
+cleared.  Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB\-\-tcp\-flags SYN,RST,ACK,FIN SYN\fP.
+If the "!" flag precedes the "\-\-syn", the sense of the
+option is inverted.
+.TP
+[\fB!\fP] \fB\-\-tcp\-option\fP \fInumber\fP
+Match if TCP option set.
diff --git a/extensions/libxt_tcpmss.c b/extensions/libxt_tcpmss.c
new file mode 100644
index 0000000..c7c5971
--- /dev/null
+++ b/extensions/libxt_tcpmss.c
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_tcpmss.h>
+
+enum {
+	O_TCPMSS = 0,
+};
+
+static void tcpmss_help(void)
+{
+	printf(
+"tcpmss match options:\n"
+"[!] --mss value[:value]	Match TCP MSS range.\n"
+"				(only valid for TCP SYN or SYN/ACK packets)\n");
+}
+
+static const struct xt_option_entry tcpmss_opts[] = {
+	{.name = "mss", .id = O_TCPMSS, .type = XTTYPE_UINT16RC,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void tcpmss_parse(struct xt_option_call *cb)
+{
+	struct xt_tcpmss_match_info *mssinfo = cb->data;
+
+	xtables_option_parse(cb);
+	mssinfo->mss_min = cb->val.u16_range[0];
+	mssinfo->mss_max = mssinfo->mss_min;
+	if (cb->nvals == 2)
+		mssinfo->mss_max = cb->val.u16_range[1];
+	if (cb->invert)
+		mssinfo->invert = 1;
+}
+
+static void
+tcpmss_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_tcpmss_match_info *info = (void *)match->data;
+
+	printf(" tcpmss match %s", info->invert ? "!" : "");
+	if (info->mss_min == info->mss_max)
+		printf("%u", info->mss_min);
+	else
+		printf("%u:%u", info->mss_min, info->mss_max);
+}
+
+static void tcpmss_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_tcpmss_match_info *info = (void *)match->data;
+
+	printf("%s --mss ", info->invert ? " !" : "");
+	if (info->mss_min == info->mss_max)
+		printf("%u", info->mss_min);
+	else
+		printf("%u:%u", info->mss_min, info->mss_max);
+}
+
+static struct xtables_match tcpmss_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "tcpmss",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_tcpmss_match_info)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_tcpmss_match_info)),
+	.help		= tcpmss_help,
+	.print		= tcpmss_print,
+	.save		= tcpmss_save,
+	.x6_parse	= tcpmss_parse,
+	.x6_options	= tcpmss_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&tcpmss_match);
+}
diff --git a/extensions/libxt_tcpmss.man b/extensions/libxt_tcpmss.man
new file mode 100644
index 0000000..8ee715c
--- /dev/null
+++ b/extensions/libxt_tcpmss.man
@@ -0,0 +1,4 @@
+This matches the TCP MSS (maximum segment size) field of the TCP header.  You can only use this on TCP SYN or SYN/ACK packets, since the MSS is only negotiated during the TCP handshake at connection startup time.
+.TP
+[\fB!\fP] \fB\-\-mss\fP \fIvalue\fP[\fB:\fP\fIvalue\fP]
+Match a given TCP MSS value or range.
diff --git a/extensions/libxt_time.c b/extensions/libxt_time.c
new file mode 100644
index 0000000..9c5bda8
--- /dev/null
+++ b/extensions/libxt_time.c
@@ -0,0 +1,471 @@
+/*
+ *	libxt_time - iptables part for xt_time
+ *	Copyright © CC Computer Consultants GmbH, 2007
+ *	Contact: <jengelh@computergmbh.de>
+ *
+ *	libxt_time.c is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 or 3 of the License.
+ *
+ *	Based on libipt_time.c.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <linux/types.h>
+#include <linux/netfilter/xt_time.h>
+#include <xtables.h>
+
+enum {
+	O_DATE_START = 0,
+	O_DATE_STOP,
+	O_TIME_START,
+	O_TIME_STOP,
+	O_TIME_CONTIGUOUS,
+	O_MONTHDAYS,
+	O_WEEKDAYS,
+	O_LOCAL_TZ,
+	O_UTC,
+	O_KERNEL_TZ,
+	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[] = {
+	NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+};
+
+static const struct xt_option_entry time_opts[] = {
+	{.name = "datestart", .id = O_DATE_START, .type = XTTYPE_STRING},
+	{.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,
+	 .flags = XTOPT_INVERT},
+	{.name = "localtz", .id = O_LOCAL_TZ, .type = XTTYPE_NONE,
+	 .excl = F_UTC},
+	{.name = "utc", .id = O_UTC, .type = XTTYPE_NONE,
+	 .excl = F_LOCAL_TZ | F_KERNEL_TZ},
+	{.name = "kerneltz", .id = O_KERNEL_TZ, .type = XTTYPE_NONE,
+	 .excl = F_UTC},
+	XTOPT_TABLEEND,
+};
+
+static void time_help(void)
+{
+	printf(
+"time match options:\n"
+"    --datestart time     Start and stop time, to be given in ISO 8601\n"
+"    --datestop time      (YYYY[-MM[-DD[Thh[:mm[:ss]]]]])\n"
+"    --timestart time     Start and stop daytime (hh:mm[:ss])\n"
+"    --timestop time      (between 00:00:00 and 23:59:59)\n"
+"[!] --monthdays value    List of days on which to match, separated by comma\n"
+"                         (Possible days: 1 to 31; defaults to all)\n"
+"[!] --weekdays value     List of weekdays on which to match, sep. by comma\n"
+"                         (Possible days: Mon,Tue,Wed,Thu,Fri,Sat,Sun or 1 to 7\n"
+"                         Defaults to all weekdays.)\n"
+"    --kerneltz           Work with the kernel timezone instead of UTC\n");
+}
+
+static void time_init(struct xt_entry_match *m)
+{
+	struct xt_time_info *info = (void *)m->data;
+
+	/* By default, we match on every day, every daytime */
+	info->monthdays_match = XT_TIME_ALL_MONTHDAYS;
+	info->weekdays_match  = XT_TIME_ALL_WEEKDAYS;
+	info->daytime_start   = XT_TIME_MIN_DAYTIME;
+	info->daytime_stop    = XT_TIME_MAX_DAYTIME;
+
+	/* ...and have no date-begin or date-end boundary */
+	info->date_start = 0;
+	info->date_stop  = INT_MAX;
+}
+
+static time_t time_parse_date(const char *s, bool end)
+{
+	unsigned int month = 1, day = 1, hour = 0, minute = 0, second = 0;
+	unsigned int year  = end ? 2038 : 1970;
+	const char *os = s;
+	struct tm tm;
+	time_t ret;
+	char *e;
+
+	year = strtoul(s, &e, 10);
+	if ((*e != '-' && *e != '\0') || year < 1970 || year > 2038)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	month = strtoul(s, &e, 10);
+	if ((*e != '-' && *e != '\0') || month > 12)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	day = strtoul(s, &e, 10);
+	if ((*e != 'T' && *e != '\0') || day > 31)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	hour = strtoul(s, &e, 10);
+	if ((*e != ':' && *e != '\0') || hour > 23)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	minute = strtoul(s, &e, 10);
+	if ((*e != ':' && *e != '\0') || minute > 59)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	second = strtoul(s, &e, 10);
+	if (*e != '\0' || second > 59)
+		goto out;
+
+ eval:
+	tm.tm_year = year - 1900;
+	tm.tm_mon  = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min  = minute;
+	tm.tm_sec  = second;
+	tm.tm_isdst = 0;
+	/*
+	 * Offsetting, if any, is done by xt_time.ko,
+	 * so we have to disable it here in userspace.
+	 */
+	setenv("TZ", "UTC", true);
+	tzset();
+	ret = mktime(&tm);
+	if (ret >= 0)
+		return ret;
+	perror("mktime");
+	xtables_error(OTHER_PROBLEM, "mktime returned an error");
+
+ out:
+	xtables_error(PARAMETER_PROBLEM, "Invalid date \"%s\" specified. Should "
+	           "be YYYY[-MM[-DD[Thh[:mm[:ss]]]]]", os);
+	return -1;
+}
+
+static unsigned int time_parse_minutes(const char *s)
+{
+	unsigned int hour, minute, second = 0;
+	char *e;
+
+	hour = strtoul(s, &e, 10);
+	if (*e != ':' || hour > 23)
+		goto out;
+
+	s = e + 1;
+	minute = strtoul(s, &e, 10);
+	if ((*e != ':' && *e != '\0') || minute > 59)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	second = strtoul(s, &e, 10);
+	if (*e != '\0' || second > 59)
+		goto out;
+
+ eval:
+	return 60 * 60 * hour + 60 * minute + second;
+
+ out:
+	xtables_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, "
+	           "should be hh:mm[:ss] format and within the boundaries", s);
+	return -1;
+}
+
+static const char *my_strseg(char *buf, unsigned int buflen,
+    const char **arg, char delim)
+{
+	const char *sep;
+
+	if (*arg == NULL || **arg == '\0')
+		return NULL;
+	sep = strchr(*arg, delim);
+	if (sep == NULL) {
+		snprintf(buf, buflen, "%s", *arg);
+		*arg = NULL;
+		return buf;
+	}
+	snprintf(buf, buflen, "%.*s", (unsigned int)(sep - *arg), *arg);
+	*arg = sep + 1;
+	return buf;
+}
+
+static uint32_t time_parse_monthdays(const char *arg)
+{
+	char day[3], *err = NULL;
+	uint32_t ret = 0;
+	unsigned int i;
+
+	while (my_strseg(day, sizeof(day), &arg, ',') != NULL) {
+		i = strtoul(day, &err, 0);
+		if ((*err != ',' && *err != '\0') || i > 31)
+			xtables_error(PARAMETER_PROBLEM,
+			           "%s is not a valid day for --monthdays", day);
+		ret |= 1 << i;
+	}
+
+	return ret;
+}
+
+static unsigned int time_parse_weekdays(const char *arg)
+{
+	char day[4], *err = NULL;
+	unsigned int i, ret = 0;
+	bool valid;
+
+	while (my_strseg(day, sizeof(day), &arg, ',') != NULL) {
+		i = strtoul(day, &err, 0);
+		if (*err == '\0') {
+			if (i == 0)
+				xtables_error(PARAMETER_PROBLEM,
+				           "No, the week does NOT begin with Sunday.");
+			ret |= 1 << i;
+			continue;
+		}
+
+		valid = false;
+		for (i = 1; i < ARRAY_SIZE(week_days); ++i)
+			if (strncmp(day, week_days[i], 2) == 0) {
+				ret |= 1 << i;
+				valid = true;
+			}
+
+		if (!valid)
+			xtables_error(PARAMETER_PROBLEM,
+			           "%s is not a valid day specifier", day);
+	}
+
+	return ret;
+}
+
+static void time_parse(struct xt_option_call *cb)
+{
+	struct xt_time_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_DATE_START:
+		info->date_start = time_parse_date(cb->arg, false);
+		break;
+	case O_DATE_STOP:
+		info->date_stop = time_parse_date(cb->arg, true);
+		break;
+	case O_TIME_START:
+		info->daytime_start = time_parse_minutes(cb->arg);
+		break;
+	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 "
+		        "kernel timezone has caveats - "
+		        "see manpage for details.\n");
+		/* fallthrough */
+	case O_KERNEL_TZ:
+		info->flags |= XT_TIME_LOCAL_TZ;
+		break;
+	case O_MONTHDAYS:
+		info->monthdays_match = time_parse_monthdays(cb->arg);
+		if (cb->invert)
+			info->monthdays_match ^= XT_TIME_ALL_MONTHDAYS;
+		break;
+	case O_WEEKDAYS:
+		info->weekdays_match = time_parse_weekdays(cb->arg);
+		if (cb->invert)
+			info->weekdays_match ^= XT_TIME_ALL_WEEKDAYS;
+		break;
+	}
+}
+
+static void time_print_date(time_t date, const char *command)
+{
+	struct tm *t;
+
+	/* If it is the default value, do not print it. */
+	if (date == 0 || date == LONG_MAX)
+		return;
+
+	t = gmtime(&date);
+	if (command != NULL)
+		/*
+		 * Need a contiguous string (no whitespaces), hence using
+		 * the ISO 8601 "T" variant.
+		 */
+		printf(" %s %04u-%02u-%02uT%02u:%02u:%02u",
+		       command, t->tm_year + 1900, t->tm_mon + 1,
+		       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	else
+		printf(" %04u-%02u-%02u %02u:%02u:%02u",
+		       t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+		       t->tm_hour, t->tm_min, t->tm_sec);
+}
+
+static void time_print_monthdays(uint32_t mask, bool human_readable)
+{
+	unsigned int i, nbdays = 0;
+
+	printf(" ");
+	for (i = 1; i <= 31; ++i)
+		if (mask & (1 << i)) {
+			if (nbdays++ > 0)
+				printf(",");
+			printf("%u", i);
+			if (human_readable)
+				switch (i % 10) {
+					case 1:
+						printf("st");
+						break;
+					case 2:
+						printf("nd");
+						break;
+					case 3:
+						printf("rd");
+						break;
+					default:
+						printf("th");
+						break;
+				}
+		}
+}
+
+static void time_print_weekdays(unsigned int mask)
+{
+	unsigned int i, nbdays = 0;
+
+	printf(" ");
+	for (i = 1; i <= 7; ++i)
+		if (mask & (1 << i)) {
+			if (nbdays > 0)
+				printf(",%s", week_days[i]);
+			else
+				printf("%s", week_days[i]);
+			++nbdays;
+		}
+}
+
+static inline void divide_time(unsigned int fulltime, unsigned int *hours,
+    unsigned int *minutes, unsigned int *seconds)
+{
+	*seconds  = fulltime % 60;
+	fulltime /= 60;
+	*minutes  = fulltime % 60;
+	*hours    = fulltime / 60;
+}
+
+static void time_print(const void *ip, const struct xt_entry_match *match,
+                       int numeric)
+{
+	const struct xt_time_info *info = (const void *)match->data;
+	unsigned int h, m, s;
+
+	printf(" TIME");
+
+	if (info->daytime_start != XT_TIME_MIN_DAYTIME ||
+	    info->daytime_stop != XT_TIME_MAX_DAYTIME) {
+		divide_time(info->daytime_start, &h, &m, &s);
+		printf(" from %02u:%02u:%02u", h, m, s);
+		divide_time(info->daytime_stop, &h, &m, &s);
+		printf(" to %02u:%02u:%02u", h, m, s);
+	}
+	if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) {
+		printf(" on");
+		time_print_weekdays(info->weekdays_match);
+	}
+	if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) {
+		printf(" on");
+		time_print_monthdays(info->monthdays_match, true);
+	}
+	if (info->date_start != 0) {
+		printf(" starting from");
+		time_print_date(info->date_start, NULL);
+	}
+	if (info->date_stop != INT_MAX) {
+		printf(" until date");
+		time_print_date(info->date_stop, NULL);
+	}
+	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)
+{
+	const struct xt_time_info *info = (const void *)match->data;
+	unsigned int h, m, s;
+
+	if (info->daytime_start != XT_TIME_MIN_DAYTIME ||
+	    info->daytime_stop != XT_TIME_MAX_DAYTIME) {
+		divide_time(info->daytime_start, &h, &m, &s);
+		printf(" --timestart %02u:%02u:%02u", h, m, s);
+		divide_time(info->daytime_stop, &h, &m, &s);
+		printf(" --timestop %02u:%02u:%02u", h, m, s);
+	}
+	if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) {
+		printf(" --monthdays");
+		time_print_monthdays(info->monthdays_match, false);
+	}
+	if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) {
+		printf(" --weekdays");
+		time_print_weekdays(info->weekdays_match);
+	}
+	time_print_date(info->date_start, "--datestart");
+	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 = {
+	.name          = "time",
+	.family        = NFPROTO_UNSPEC,
+	.version       = XTABLES_VERSION,
+	.size          = XT_ALIGN(sizeof(struct xt_time_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_time_info)),
+	.help          = time_help,
+	.init          = time_init,
+	.print         = time_print,
+	.save          = time_save,
+	.x6_parse      = time_parse,
+	.x6_fcheck     = time_check,
+	.x6_options    = time_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&time_match);
+}
diff --git a/extensions/libxt_time.man b/extensions/libxt_time.man
new file mode 100644
index 0000000..4c0cae0
--- /dev/null
+++ b/extensions/libxt_time.man
@@ -0,0 +1,98 @@
+This matches if the packet arrival time/date is within a given range. All
+options are optional, but are ANDed when specified. All times are interpreted
+as UTC by default.
+.TP
+\fB\-\-datestart\fP \fIYYYY\fP[\fB\-\fP\fIMM\fP[\fB\-\fP\fIDD\fP[\fBT\fP\fIhh\fP[\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]]]]]
+.TP
+\fB\-\-datestop\fP \fIYYYY\fP[\fB\-\fP\fIMM\fP[\fB\-\fP\fIDD\fP[\fBT\fP\fIhh\fP[\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]]]]]
+Only match during the given time, which must be in ISO 8601 "T" notation.
+The possible time range is 1970-01-01T00:00:00 to 2038-01-19T04:17:07.
+.IP
+If \-\-datestart or \-\-datestop are not specified, it will default to 1970-01-01
+and 2038-01-19, respectively.
+.TP
+\fB\-\-timestart\fP \fIhh\fP\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]
+.TP
+\fB\-\-timestop\fP \fIhh\fP\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]
+Only match during the given daytime. The possible time range is 00:00:00 to
+23:59:59. Leading zeroes are allowed (e.g. "06:03") and correctly interpreted
+as base-10.
+.TP
+[\fB!\fP] \fB\-\-monthdays\fP \fIday\fP[\fB,\fP\fIday\fP...]
+Only match on the given days of the month. Possible values are \fB1\fP
+to \fB31\fP. Note that specifying \fB31\fP will of course not match
+on months which do not have a 31st day; the same goes for 28- or 29-day
+February.
+.TP
+[\fB!\fP] \fB\-\-weekdays\fP \fIday\fP[\fB,\fP\fIday\fP...]
+Only match on the given weekdays. Possible values are \fBMon\fP, \fBTue\fP,
+\fBWed\fP, \fBThu\fP, \fBFri\fP, \fBSat\fP, \fBSun\fP, or values from \fB1\fP
+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.
+.PP
+About kernel timezones: Linux keeps the system time in UTC, and always does so.
+On boot, system time is initialized from a referential time source. Where this
+time source has no timezone information, such as the x86 CMOS RTC, UTC will be
+assumed. If the time source is however not in UTC, userspace should provide the
+correct system time and timezone to the kernel once it has the information.
+.PP
+Local time is a feature on top of the (timezone independent) system time. Each
+process has its own idea of local time, specified via the TZ environment
+variable. The kernel also has its own timezone offset variable. The TZ
+userspace environment variable specifies how the UTC-based system time is
+displayed, e.g. when you run date(1), or what you see on your desktop clock.
+The TZ string may resolve to different offsets at different dates, which is
+what enables the automatic time-jumping in userspace. when DST changes. The
+kernel's timezone offset variable is used when it has to convert between
+non-UTC sources, such as FAT filesystems, to UTC (since the latter is what the
+rest of the system uses).
+.PP
+The caveat with the kernel timezone is that Linux distributions may ignore to
+set the kernel timezone, and instead only set the system time. Even if a
+particular distribution does set the timezone at boot, it is usually does not
+keep the kernel timezone offset - which is what changes on DST - up to date.
+ntpd will not touch the kernel timezone, so running it will not resolve the
+issue. As such, one may encounter a timezone that is always +0000, or one that
+is wrong half of the time of the year. As such, \fBusing \-\-kerneltz is highly
+discouraged.\fP
+.PP
+EXAMPLES. To match on weekends, use:
+.IP
+\-m time \-\-weekdays Sa,Su
+.PP
+Or, to match (once) on a national holiday block:
+.IP
+\-m time \-\-datestart 2007\-12\-24 \-\-datestop 2007\-12\-27
+.PP
+Since the stop time is actually inclusive, you would need the following stop
+time to not match the first second of the new day:
+.IP
+\-m time \-\-datestart 2007\-01\-01T17:00 \-\-datestop 2007\-01\-01T23:59:59
+.PP
+During lunch hour:
+.IP
+\-m time \-\-timestart 12:30 \-\-timestop 13:30
+.PP
+The fourth Friday in the month:
+.IP
+\-m time \-\-weekdays Fr \-\-monthdays 22,23,24,25,26,27,28
+.PP
+(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_tos.c b/extensions/libxt_tos.c
new file mode 100644
index 0000000..81c096f
--- /dev/null
+++ b/extensions/libxt_tos.c
@@ -0,0 +1,156 @@
+/*
+ * Shared library add-on to iptables to add tos match support
+ *
+ * Copyright © CC Computer Consultants GmbH, 2007
+ * Contact: Jan Engelhardt <jengelh@computergmbh.de>
+ */
+#include <getopt.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xtables.h>
+#include <linux/netfilter/xt_dscp.h>
+#include "tos_values.c"
+
+struct ipt_tos_info {
+	uint8_t tos;
+	uint8_t invert;
+};
+
+enum {
+	O_TOS = 1 << 0,
+};
+
+static const struct xt_option_entry tos_mt_opts_v0[] = {
+	{.name = "tos", .id = O_TOS, .type = XTTYPE_TOSMASK,
+	 .flags = XTOPT_INVERT | XTOPT_MAND, .max = 0xFF},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry tos_mt_opts[] = {
+	{.name = "tos", .id = O_TOS, .type = XTTYPE_TOSMASK,
+	 .flags = XTOPT_INVERT | XTOPT_MAND, .max = 0x3F},
+	XTOPT_TABLEEND,
+};
+
+static void tos_mt_help(void)
+{
+	const struct tos_symbol_info *symbol;
+
+	printf(
+"tos match options:\n"
+"[!] --tos value[/mask]    Match Type of Service/Priority field value\n"
+"[!] --tos symbol          Match TOS field (IPv4 only) by symbol\n"
+"                          Accepted symbolic names for value are:\n");
+
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		printf("                          (0x%02x) %2u %s\n",
+		       symbol->value, symbol->value, symbol->name);
+
+	printf("\n");
+}
+
+static void tos_mt_parse_v0(struct xt_option_call *cb)
+{
+	struct ipt_tos_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	if (cb->val.tos_mask != 0xFF)
+		xtables_error(PARAMETER_PROBLEM, "tos: Your kernel is "
+		           "too old to support anything besides /0xFF "
+			   "as a mask.");
+	info->tos = cb->val.tos_value;
+	if (cb->invert)
+		info->invert = true;
+}
+
+static void tos_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_tos_match_info *info = cb->data;
+
+	xtables_option_parse(cb);
+	info->tos_value = cb->val.tos_value;
+	info->tos_mask  = cb->val.tos_mask;
+	if (cb->invert)
+		info->invert = true;
+}
+
+static void tos_mt_print_v0(const void *ip, const struct xt_entry_match *match,
+                            int numeric)
+{
+	const struct ipt_tos_info *info = (const void *)match->data;
+
+	printf(" tos match ");
+	if (info->invert)
+		printf("!");
+	if (numeric || !tos_try_print_symbolic("", info->tos, 0x3F))
+		printf("0x%02x", info->tos);
+}
+
+static void tos_mt_print(const void *ip, const struct xt_entry_match *match,
+                         int numeric)
+{
+	const struct xt_tos_match_info *info = (const void *)match->data;
+
+	printf(" tos match");
+	if (info->invert)
+		printf("!");
+	if (numeric ||
+	    !tos_try_print_symbolic("", info->tos_value, info->tos_mask))
+		printf("0x%02x/0x%02x", info->tos_value, info->tos_mask);
+}
+
+static void tos_mt_save_v0(const void *ip, const struct xt_entry_match *match)
+{
+	const struct ipt_tos_info *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+	printf(" --tos 0x%02x", info->tos);
+}
+
+static void tos_mt_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_tos_match_info *info = (const void *)match->data;
+
+	if (info->invert)
+		printf(" !");
+	printf(" --tos 0x%02x/0x%02x", info->tos_value, info->tos_mask);
+}
+
+static struct xtables_match tos_mt_reg[] = {
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "tos",
+		.family        = NFPROTO_IPV4,
+		.revision      = 0,
+		.size          = XT_ALIGN(sizeof(struct ipt_tos_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct ipt_tos_info)),
+		.help          = tos_mt_help,
+		.print         = tos_mt_print_v0,
+		.save          = tos_mt_save_v0,
+		.x6_parse      = tos_mt_parse_v0,
+		.x6_options    = tos_mt_opts_v0,
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "tos",
+		.family        = NFPROTO_UNSPEC,
+		.revision      = 1,
+		.size          = XT_ALIGN(sizeof(struct xt_tos_match_info)),
+		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_match_info)),
+		.help          = tos_mt_help,
+		.print         = tos_mt_print,
+		.save          = tos_mt_save,
+		.x6_parse      = tos_mt_parse,
+		.x6_options    = tos_mt_opts,
+	},
+};
+
+void _init(void)
+{
+	xtables_register_matches(tos_mt_reg, ARRAY_SIZE(tos_mt_reg));
+}
diff --git a/extensions/libxt_tos.man b/extensions/libxt_tos.man
new file mode 100644
index 0000000..ae73e63
--- /dev/null
+++ b/extensions/libxt_tos.man
@@ -0,0 +1,12 @@
+This module matches the 8-bit Type of Service field in the IPv4 header (i.e.
+including the "Precedence" bits) or the (also 8-bit) Priority field in the IPv6
+header.
+.TP
+[\fB!\fP] \fB\-\-tos\fP \fIvalue\fP[\fB/\fP\fImask\fP]
+Matches packets with the given TOS mark value. If a mask is specified, it is
+logically ANDed with the TOS mark before the comparison.
+.TP
+[\fB!\fP] \fB\-\-tos\fP \fIsymbol\fP
+You can specify a symbolic name when using the tos match for IPv4. The list of
+recognized TOS names can be obtained by calling iptables with \fB\-m tos \-h\fP.
+Note that this implies a mask of 0x3F, i.e. all but the ECN bits.
diff --git a/extensions/libxt_u32.c b/extensions/libxt_u32.c
new file mode 100644
index 0000000..2a7f5d8
--- /dev/null
+++ b/extensions/libxt_u32.c
@@ -0,0 +1,279 @@
+/* Shared library add-on to iptables to add u32 matching,
+ * generalized matching on values found at packet offsets
+ *
+ * Detailed doc is in the kernel module source
+ * net/netfilter/xt_u32.c
+ *
+ * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com>
+ * Released under the terms of GNU GPL v2
+ *
+ * Copyright © CC Computer Consultants GmbH, 2007
+ * Contact: <jengelh@computergmbh.de>
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_u32.h>
+
+enum {
+	O_U32 = 0,
+};
+
+static const struct xt_option_entry u32_opts[] = {
+	{.name = "u32", .id = O_U32, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_INVERT},
+	XTOPT_TABLEEND,
+};
+
+static void u32_help(void)
+{
+	printf(
+		"u32 match options:\n"
+		"[!] --u32 tests\n"
+		"\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n"
+		"\t\t""value := range | value \",\" range\n"
+		"\t\t""range := number | number \":\" number\n"
+		"\t\t""location := number | location operator number\n"
+		"\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n");
+}
+
+static void u32_dump(const struct xt_u32 *data)
+{
+	const struct xt_u32_test *ct;
+	unsigned int testind, i;
+
+	printf(" \"");
+	for (testind = 0; testind < data->ntests; ++testind) {
+		ct = &data->tests[testind];
+
+		if (testind > 0)
+			printf("&&");
+
+		printf("0x%x", ct->location[0].number);
+		for (i = 1; i < ct->nnums; ++i) {
+			switch (ct->location[i].nextop) {
+			case XT_U32_AND:
+				printf("&");
+				break;
+			case XT_U32_LEFTSH:
+				printf("<<");
+				break;
+			case XT_U32_RIGHTSH:
+				printf(">>");
+				break;
+			case XT_U32_AT:
+				printf("@");
+				break;
+			}
+			printf("0x%x", ct->location[i].number);
+		}
+
+		printf("=");
+		for (i = 0; i < ct->nvalues; ++i) {
+			if (i > 0)
+				printf(",");
+			if (ct->value[i].min == ct->value[i].max)
+				printf("0x%x", ct->value[i].min);
+			else
+				printf("0x%x:0x%x", ct->value[i].min,
+				       ct->value[i].max);
+		}
+	}
+	putchar('\"');
+}
+
+/* string_to_number() is not quite what we need here ... */
+static uint32_t parse_number(const char **s, int pos)
+{
+	unsigned int number;
+	char *end;
+
+	if (!xtables_strtoui(*s, &end, &number, 0, UINT32_MAX) ||
+	    end == *s)
+		xtables_error(PARAMETER_PROBLEM,
+			"u32: at char %d: not a number or out of range", pos);
+	*s = end;
+	return number;
+}
+
+static void u32_parse(struct xt_option_call *cb)
+{
+	struct xt_u32 *data = cb->data;
+	unsigned int testind = 0, locind = 0, valind = 0;
+	struct xt_u32_test *ct = &data->tests[testind]; /* current test */
+	const char *arg = cb->arg; /* the argument string */
+	const char *start = cb->arg;
+	int state = 0;
+
+	xtables_option_parse(cb);
+	data->invert = cb->invert;
+
+	/*
+	 * states:
+	 * 0 = looking for numbers and operations,
+	 * 1 = looking for ranges
+	 */
+	while (1) {
+		/* read next operand/number or range */
+		while (isspace(*arg))
+			++arg;
+
+		if (*arg == '\0') {
+			/* end of argument found */
+			if (state == 0)
+				xtables_error(PARAMETER_PROBLEM,
+					   "u32: abrupt end of input after location specifier");
+			if (valind == 0)
+				xtables_error(PARAMETER_PROBLEM,
+					   "u32: test ended with no value specified");
+
+			ct->nnums    = locind;
+			ct->nvalues  = valind;
+			data->ntests = ++testind;
+
+			if (testind > XT_U32_MAXSIZE)
+				xtables_error(PARAMETER_PROBLEM,
+				           "u32: at char %u: too many \"&&\"s",
+				           (unsigned int)(arg - start));
+			return;
+		}
+
+		if (state == 0) {
+			/*
+			 * reading location: read a number if nothing read yet,
+			 * otherwise either op number or = to end location spec
+			 */
+			if (*arg == '=') {
+				if (locind == 0) {
+					xtables_error(PARAMETER_PROBLEM,
+					           "u32: at char %u: "
+					           "location spec missing",
+					           (unsigned int)(arg - start));
+				} else {
+					++arg;
+					state = 1;
+				}
+			} else {
+				if (locind != 0) {
+					/* need op before number */
+					if (*arg == '&') {
+						ct->location[locind].nextop = XT_U32_AND;
+					} else if (*arg == '<') {
+						if (*++arg != '<')
+							xtables_error(PARAMETER_PROBLEM,
+								   "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start));
+						ct->location[locind].nextop = XT_U32_LEFTSH;
+					} else if (*arg == '>') {
+						if (*++arg != '>')
+							xtables_error(PARAMETER_PROBLEM,
+								   "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start));
+						ct->location[locind].nextop = XT_U32_RIGHTSH;
+					} else if (*arg == '@') {
+						ct->location[locind].nextop = XT_U32_AT;
+					} else {
+						xtables_error(PARAMETER_PROBLEM,
+							"u32: at char %u: operator expected", (unsigned int)(arg - start));
+					}
+					++arg;
+				}
+				/* now a number; string_to_number skips white space? */
+				ct->location[locind].number =
+					parse_number(&arg, arg - start);
+				if (++locind > XT_U32_MAXSIZE)
+					xtables_error(PARAMETER_PROBLEM,
+						   "u32: at char %u: too many operators", (unsigned int)(arg - start));
+			}
+		} else {
+			/*
+			 * state 1 - reading values: read a range if nothing
+			 * read yet, otherwise either ,range or && to end
+			 * test spec
+			 */
+			if (*arg == '&') {
+				if (*++arg != '&')
+					xtables_error(PARAMETER_PROBLEM,
+						   "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start));
+				if (valind == 0) {
+					xtables_error(PARAMETER_PROBLEM,
+						   "u32: at char %u: value spec missing", (unsigned int)(arg - start));
+				} else {
+					ct->nnums   = locind;
+					ct->nvalues = valind;
+					ct = &data->tests[++testind];
+					if (testind > XT_U32_MAXSIZE)
+						xtables_error(PARAMETER_PROBLEM,
+							   "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start));
+					++arg;
+					state  = 0;
+					locind = 0;
+					valind = 0;
+				}
+			} else { /* read value range */
+				if (valind > 0) { /* need , before number */
+					if (*arg != ',')
+						xtables_error(PARAMETER_PROBLEM,
+							   "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start));
+					++arg;
+				}
+				ct->value[valind].min =
+					parse_number(&arg, arg - start);
+
+				while (isspace(*arg))
+					++arg;
+
+				if (*arg == ':') {
+					++arg;
+					ct->value[valind].max =
+						parse_number(&arg, arg-start);
+				} else {
+					ct->value[valind].max =
+						ct->value[valind].min;
+				}
+
+				if (++valind > XT_U32_MAXSIZE)
+					xtables_error(PARAMETER_PROBLEM,
+						   "u32: at char %u: too many \",\"s", (unsigned int)(arg - start));
+			}
+		}
+	}
+}
+
+static void u32_print(const void *ip, const struct xt_entry_match *match,
+                      int numeric)
+{
+	const struct xt_u32 *data = (const void *)match->data;
+	printf(" u32");
+	if (data->invert)
+		printf(" !");
+	u32_dump(data);
+}
+
+static void u32_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_u32 *data = (const void *)match->data;
+	if (data->invert)
+		printf(" !");
+	printf(" --u32");
+	u32_dump(data);
+}
+
+static struct xtables_match u32_match = {
+	.name          = "u32",
+	.family        = NFPROTO_UNSPEC,
+	.version       = XTABLES_VERSION,
+	.size          = XT_ALIGN(sizeof(struct xt_u32)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
+	.help          = u32_help,
+	.print         = u32_print,
+	.save          = u32_save,
+	.x6_parse      = u32_parse,
+	.x6_options    = u32_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&u32_match);
+}
diff --git a/extensions/libxt_u32.man b/extensions/libxt_u32.man
new file mode 100644
index 0000000..7c8615d
--- /dev/null
+++ b/extensions/libxt_u32.man
@@ -0,0 +1,129 @@
+U32 tests whether quantities of up to 4 bytes extracted from a packet have
+specified values. The specification of what to extract is general enough to
+find data at given offsets from tcp headers or payloads.
+.TP
+[\fB!\fP] \fB\-\-u32\fP \fItests\fP
+The argument amounts to a program in a small language described below.
+.IP
+tests := location "=" value | tests "&&" location "=" value
+.IP
+value := range | value "," range
+.IP
+range := number | number ":" number
+.PP
+a single number, \fIn\fP, is interpreted the same as \fIn:n\fP. \fIn:m\fP is
+interpreted as the range of numbers \fB>=n\fP and \fB<=m\fP.
+.IP "" 4
+location := number | location operator number
+.IP "" 4
+operator := "&" | "<<" | ">>" | "@"
+.PP
+The operators \fB&\fP, \fB<<\fP, \fB>>\fP and \fB&&\fP mean the same as in C.
+The \fB=\fP is really a set membership operator and the value syntax describes
+a set. The \fB@\fP operator is what allows moving to the next header and is
+described further below.
+.PP
+There are currently some artificial implementation limits on the size of the
+tests:
+.IP "    *"
+no more than 10 of "\fB=\fP" (and 9 "\fB&&\fP"s) in the u32 argument
+.IP "    *"
+no more than 10 ranges (and 9 commas) per value
+.IP "    *"
+no more than 10 numbers (and 9 operators) per location
+.PP
+To describe the meaning of location, imagine the following machine that
+interprets it. There are three registers:
+.IP
+A is of type \fBchar *\fP, initially the address of the IP header
+.IP
+B and C are unsigned 32 bit integers, initially zero
+.PP
+The instructions are:
+.IP
+number B = number;
+.IP
+C = (*(A+B)<<24) + (*(A+B+1)<<16) + (*(A+B+2)<<8) + *(A+B+3)
+.IP
+&number C = C & number
+.IP
+<< number C = C << number
+.IP
+>> number C = C >> number
+.IP
+@number A = A + C; then do the instruction number
+.PP
+Any access of memory outside [skb\->data,skb\->end] causes the match to fail.
+Otherwise the result of the computation is the final value of C.
+.PP
+Whitespace is allowed but not required in the tests. However, the characters
+that do occur there are likely to require shell quoting, so it is a good idea
+to enclose the arguments in quotes.
+.PP
+Example:
+.IP
+match IP packets with total length >= 256
+.IP
+The IP header contains a total length field in bytes 2-3.
+.IP
+\-\-u32 "\fB0 & 0xFFFF = 0x100:0xFFFF\fP"
+.IP
+read bytes 0-3
+.IP
+AND that with 0xFFFF (giving bytes 2-3), and test whether that is in the range
+[0x100:0xFFFF]
+.PP
+Example: (more realistic, hence more complicated)
+.IP
+match ICMP packets with icmp type 0
+.IP
+First test that it is an ICMP packet, true iff byte 9 (protocol) = 1
+.IP
+\-\-u32 "\fB6 & 0xFF = 1 &&\fP ...
+.IP
+read bytes 6-9, use \fB&\fP to throw away bytes 6-8 and compare the result to
+1. Next test that it is not a fragment. (If so, it might be part of such a
+packet but we cannot always tell.) N.B.: This test is generally needed if you
+want to match anything beyond the IP header. The last 6 bits of byte 6 and all
+of byte 7 are 0 iff this is a complete packet (not a fragment). Alternatively,
+you can allow first fragments by only testing the last 5 bits of byte 6.
+.IP
+ ... \fB4 & 0x3FFF = 0 &&\fP ...
+.IP
+Last test: the first byte past the IP header (the type) is 0. This is where we
+have to use the @syntax. The length of the IP header (IHL) in 32 bit words is
+stored in the right half of byte 0 of the IP header itself.
+.IP
+ ... \fB0 >> 22 & 0x3C @ 0 >> 24 = 0\fP"
+.IP
+The first 0 means read bytes 0-3, \fB>>22\fP means shift that 22 bits to the
+right. Shifting 24 bits would give the first byte, so only 22 bits is four
+times that plus a few more bits. \fB&3C\fP then eliminates the two extra bits
+on the right and the first four bits of the first byte. For instance, if IHL=5,
+then the IP header is 20 (4 x 5) bytes long. In this case, bytes 0-1 are (in
+binary) xxxx0101 yyzzzzzz, \fB>>22\fP gives the 10 bit value xxxx0101yy and
+\fB&3C\fP gives 010100. \fB@\fP means to use this number as a new offset into
+the packet, and read four bytes starting from there. This is the first 4 bytes
+of the ICMP payload, of which byte 0 is the ICMP type. Therefore, we simply
+shift the value 24 to the right to throw out all but the first byte and compare
+the result with 0.
+.PP
+Example:
+.IP
+TCP payload bytes 8-12 is any of 1, 2, 5 or 8
+.IP
+First we test that the packet is a tcp packet (similar to ICMP).
+.IP
+\-\-u32 "\fB6 & 0xFF = 6 &&\fP ...
+.IP
+Next, test that it is not a fragment (same as above).
+.IP
+ ... \fB0 >> 22 & 0x3C @ 12 >> 26 & 0x3C @ 8 = 1,2,5,8\fP"
+.IP
+\fB0>>22&3C\fP as above computes the number of bytes in the IP header. \fB@\fP
+makes this the new offset into the packet, which is the start of the TCP
+header. The length of the TCP header (again in 32 bit words) is the left half
+of byte 12 of the TCP header. The \fB12>>26&3C\fP computes this length in bytes
+(similar to the IP header before). "@" makes this the new offset, which is the
+start of the TCP payload. Finally, 8 reads bytes 8-12 of the payload and
+\fB=\fP checks whether the result is any of 1, 2, 5 or 8.
diff --git a/extensions/libxt_udp.c b/extensions/libxt_udp.c
new file mode 100644
index 0000000..b9f39ee
--- /dev/null
+++ b/extensions/libxt_udp.c
@@ -0,0 +1,173 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <xtables.h>
+#include <linux/netfilter/xt_tcpudp.h>
+
+enum {
+	O_SOURCE_PORT = 0,
+	O_DEST_PORT,
+};
+
+static void udp_help(void)
+{
+	printf(
+"udp match options:\n"
+"[!] --source-port port[:port]\n"
+" --sport ...\n"
+"				match source port(s)\n"
+"[!] --destination-port port[:port]\n"
+" --dport ...\n"
+"				match destination port(s)\n");
+}
+
+#define s struct xt_udp
+static const struct xt_option_entry udp_opts[] = {
+	{.name = "source-port", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "sport", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "destination-port", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .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)},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static void udp_init(struct xt_entry_match *m)
+{
+	struct xt_udp *udpinfo = (struct xt_udp *)m->data;
+
+	udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
+}
+
+static void udp_parse(struct xt_option_call *cb)
+{
+	struct xt_udp *udpinfo = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORT:
+		if (cb->invert)
+			udpinfo->invflags |= XT_UDP_INV_SRCPT;
+		break;
+	case O_DEST_PORT:
+		if (cb->invert)
+			udpinfo->invflags |= XT_UDP_INV_DSTPT;
+		break;
+	}
+}
+
+static const char *
+port_to_service(int port)
+{
+	const struct servent *service;
+
+	if ((service = getservbyport(htons(port), "udp")))
+		return service->s_name;
+
+	return NULL;
+}
+
+static void
+print_port(uint16_t port, int numeric)
+{
+	const char *service;
+
+	if (numeric || (service = port_to_service(port)) == NULL)
+		printf("%u", port);
+	else
+		printf("%s", service);
+}
+
+static void
+print_ports(const char *name, uint16_t min, uint16_t max,
+	    int invert, int numeric)
+{
+	const char *inv = invert ? "!" : "";
+
+	if (min != 0 || max != 0xFFFF || invert) {
+		printf(" %s", name);
+		if (min == max) {
+			printf(":%s", inv);
+			print_port(min, numeric);
+		} else {
+			printf("s:%s", inv);
+			print_port(min, numeric);
+			printf(":");
+			print_port(max, numeric);
+		}
+	}
+}
+
+static void
+udp_print(const void *ip, const struct xt_entry_match *match, int numeric)
+{
+	const struct xt_udp *udp = (struct xt_udp *)match->data;
+
+	printf(" udp");
+	print_ports("spt", udp->spts[0], udp->spts[1],
+		    udp->invflags & XT_UDP_INV_SRCPT,
+		    numeric);
+	print_ports("dpt", udp->dpts[0], udp->dpts[1],
+		    udp->invflags & XT_UDP_INV_DSTPT,
+		    numeric);
+	if (udp->invflags & ~XT_UDP_INV_MASK)
+		printf(" Unknown invflags: 0x%X",
+		       udp->invflags & ~XT_UDP_INV_MASK);
+}
+
+static void udp_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_udp *udpinfo = (struct xt_udp *)match->data;
+
+	if (udpinfo->spts[0] != 0
+	    || udpinfo->spts[1] != 0xFFFF) {
+		if (udpinfo->invflags & XT_UDP_INV_SRCPT)
+			printf(" !");
+		if (udpinfo->spts[0]
+		    != udpinfo->spts[1])
+			printf(" --sport %u:%u",
+			       udpinfo->spts[0],
+			       udpinfo->spts[1]);
+		else
+			printf(" --sport %u",
+			       udpinfo->spts[0]);
+	}
+
+	if (udpinfo->dpts[0] != 0
+	    || udpinfo->dpts[1] != 0xFFFF) {
+		if (udpinfo->invflags & XT_UDP_INV_DSTPT)
+			printf(" !");
+		if (udpinfo->dpts[0]
+		    != udpinfo->dpts[1])
+			printf(" --dport %u:%u",
+			       udpinfo->dpts[0],
+			       udpinfo->dpts[1]);
+		else
+			printf(" --dport %u",
+			       udpinfo->dpts[0]);
+	}
+}
+
+static struct xtables_match udp_match = {
+	.family		= NFPROTO_UNSPEC,
+	.name		= "udp",
+	.version	= XTABLES_VERSION,
+	.size		= XT_ALIGN(sizeof(struct xt_udp)),
+	.userspacesize	= XT_ALIGN(sizeof(struct xt_udp)),
+	.help		= udp_help,
+	.init		= udp_init,
+	.print		= udp_print,
+	.save		= udp_save,
+	.x6_parse	= udp_parse,
+	.x6_options	= udp_opts,
+};
+
+void
+_init(void)
+{
+	xtables_register_match(&udp_match);
+}
diff --git a/extensions/libxt_udp.man b/extensions/libxt_udp.man
new file mode 100644
index 0000000..5339c8e
--- /dev/null
+++ b/extensions/libxt_udp.man
@@ -0,0 +1,14 @@
+These extensions can be used if `\-\-protocol udp' is specified. It
+provides the following options:
+.TP
+[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP]
+Source port or port range specification.
+See the description of the
+\fB\-\-source\-port\fP
+option of the TCP extension for details.
+.TP
+[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP]
+Destination port or port range specification.
+See the description of the
+\fB\-\-destination\-port\fP
+option of the TCP extension for details.
diff --git a/extensions/tos_values.c b/extensions/tos_values.c
new file mode 100644
index 0000000..6dc4743
--- /dev/null
+++ b/extensions/tos_values.c
@@ -0,0 +1,41 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <linux/ip.h>
+
+#ifndef IPTOS_NORMALSVC
+#	define IPTOS_NORMALSVC 0
+#endif
+
+struct tos_value_mask {
+	uint8_t value, mask;
+};
+
+static const struct tos_symbol_info {
+	unsigned char value;
+	const char *name;
+} tos_symbol_names[] = {
+	{IPTOS_LOWDELAY,    "Minimize-Delay"},
+	{IPTOS_THROUGHPUT,  "Maximize-Throughput"},
+	{IPTOS_RELIABILITY, "Maximize-Reliability"},
+	{IPTOS_MINCOST,     "Minimize-Cost"},
+	{IPTOS_NORMALSVC,   "Normal-Service"},
+	{},
+};
+
+static bool tos_try_print_symbolic(const char *prefix,
+    uint8_t value, uint8_t mask)
+{
+	const struct tos_symbol_info *symbol;
+
+	if (mask != 0x3F)
+		return false;
+
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		if (value == symbol->value) {
+			printf(" %s%s", prefix, symbol->name);
+			return true;
+		}
+
+	return false;
+}
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..e695120
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,12 @@
+# -*- Makefile -*-
+
+include_HEADERS =
+nobase_include_HEADERS = xtables.h xtables-version.h
+
+if ENABLE_LIBIPQ
+include_HEADERS += libipq/libipq.h
+endif
+
+nobase_include_HEADERS += \
+	libiptc/ipt_kernel_headers.h libiptc/libiptc.h \
+	libiptc/libip6tc.h libiptc/libxtc.h libiptc/xtcshared.h
diff --git a/include/ip6tables.h b/include/ip6tables.h
new file mode 100644
index 0000000..5f1c5b6
--- /dev/null
+++ b/include/ip6tables.h
@@ -0,0 +1,20 @@
+#ifndef _IP6TABLES_USER_H
+#define _IP6TABLES_USER_H
+
+#include <netinet/ip.h>
+#include <xtables.h>
+#include <libiptc/libip6tc.h>
+#include <iptables/internal.h>
+
+/* Your shared library should call one of these. */
+extern int do_command6(int argc, char *argv[], char **table,
+		       struct xtc_handle **handle, bool restore);
+
+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;
+
+#endif /*_IP6TABLES_USER_H*/
diff --git a/include/iptables.h b/include/iptables.h
new file mode 100644
index 0000000..ac9dc0e
--- /dev/null
+++ b/include/iptables.h
@@ -0,0 +1,23 @@
+#ifndef _IPTABLES_USER_H
+#define _IPTABLES_USER_H
+
+#include <netinet/ip.h>
+#include <xtables.h>
+#include <libiptc/libiptc.h>
+#include <iptables/internal.h>
+
+/* Your shared library should call one of these. */
+extern int do_command4(int argc, char *argv[], char **table,
+		      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 xtc_handle *handle, const char *chain, int counters);
+
+extern struct xtables_globals iptables_globals;
+
+#endif /*_IPTABLES_USER_H*/
diff --git a/include/iptables/internal.h.in b/include/iptables/internal.h.in
new file mode 100644
index 0000000..8568e58
--- /dev/null
+++ b/include/iptables/internal.h.in
@@ -0,0 +1,13 @@
+#ifndef IPTABLES_INTERNAL_H
+#define IPTABLES_INTERNAL_H 1
+
+#define IPTABLES_VERSION "@PACKAGE_VERSION@"
+
+/**
+ * Program's own name and version.
+ */
+extern const char *program_name, *program_version;
+
+extern int line;
+
+#endif /* IPTABLES_INTERNAL_H */
diff --git a/include/libipq/libipq.h b/include/libipq/libipq.h
new file mode 100644
index 0000000..3cd1329
--- /dev/null
+++ b/include/libipq/libipq.h
@@ -0,0 +1,83 @@
+/*
+ * libipq.h
+ *
+ * IPQ library for userspace.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _LIBIPQ_H
+#define _LIBIPQ_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include <linux/netfilter_ipv4/ip_queue.h>
+typedef unsigned long ipq_id_t;
+
+#ifdef DEBUG_LIBIPQ
+#include <stdio.h>
+#define LDEBUG(x...) fprintf(stderr, ## x)
+#else
+#define LDEBUG(x...)
+#endif	/* DEBUG_LIBIPQ */
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0x20
+#endif
+
+struct ipq_handle
+{
+	int fd;
+	u_int8_t blocking;
+	struct sockaddr_nl local;
+	struct sockaddr_nl peer;
+};
+
+struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol);
+
+int ipq_destroy_handle(struct ipq_handle *h);
+
+ssize_t ipq_read(const struct ipq_handle *h,
+                unsigned char *buf, size_t len, int timeout);
+
+int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t len);
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf);
+
+int ipq_message_type(const unsigned char *buf);
+
+int ipq_get_msgerr(const unsigned char *buf);
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf);
+
+int ipq_ctl(const struct ipq_handle *h, int request, ...);
+
+char *ipq_errstr(void);
+void ipq_perror(const char *s);
+
+#endif	/* _LIBIPQ_H */
+
diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h
new file mode 100644
index 0000000..18861fe
--- /dev/null
+++ b/include/libiptc/ipt_kernel_headers.h
@@ -0,0 +1,27 @@
+/* This is the userspace/kernel interface for Generic IP Chains,
+   required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#include <limits.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <sys/types.h>
+#else /* libc5 */
+#include <sys/socket.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/in6.h>
+#endif
+#endif
diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h
new file mode 100644
index 0000000..9aed80a
--- /dev/null
+++ b/include/libiptc/libip6tc.h
@@ -0,0 +1,161 @@
+#ifndef _LIBIP6TC_H
+#define _LIBIP6TC_H
+/* Library which manipulates firewall rules. Version 0.2. */
+
+#include <linux/types.h>
+#include <libiptc/ipt_kernel_headers.h>
+#ifdef __cplusplus
+#	include <climits>
+#else
+#	include <limits.h> /* INT_MAX in ip6_tables.h */
+#endif
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <libiptc/xtcshared.h>
+
+#define ip6tc_handle xtc_handle
+#define ip6t_chainlabel xt_chainlabel
+
+#define IP6TC_LABEL_ACCEPT "ACCEPT"
+#define IP6TC_LABEL_DROP "DROP"
+#define IP6TC_LABEL_QUEUE   "QUEUE"
+#define IP6TC_LABEL_RETURN "RETURN"
+
+/* Does this chain exist? */
+int ip6tc_is_chain(const char *chain, struct xtc_handle *const handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+struct xtc_handle *ip6tc_init(const char *tablename);
+
+/* Cleanup after ip6tc_init(). */
+void ip6tc_free(struct xtc_handle *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+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 xtc_handle *handle);
+
+/* Returns NULL when rules run out. */
+const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
+					 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 xtc_handle *handle);
+
+/* Is this a built-in chain? */
+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 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 xt_chainlabel chain,
+		       const struct ip6t_entry *e,
+		       unsigned int rulenum,
+		       struct xtc_handle *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int ip6tc_replace_entry(const xt_chainlabel chain,
+			const struct ip6t_entry *e,
+			unsigned int rulenum,
+			struct xtc_handle *handle);
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+   rulenum = length of chain. */
+int ip6tc_append_entry(const xt_chainlabel chain,
+		       const struct ip6t_entry *e,
+		       struct xtc_handle *handle);
+
+/* Check whether a matching rule exists */
+int ip6tc_check_entry(const xt_chainlabel chain,
+		       const struct ip6t_entry *origfw,
+		       unsigned char *matchmask,
+		       struct xtc_handle *handle);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int ip6tc_delete_entry(const xt_chainlabel chain,
+		       const struct ip6t_entry *origfw,
+		       unsigned char *matchmask,
+		       struct xtc_handle *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int ip6tc_delete_num_entry(const xt_chainlabel chain,
+			   unsigned int rulenum,
+			   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 xt_chainlabel chain,
+			       struct ip6t_entry *,
+			       struct xtc_handle *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int ip6tc_flush_entries(const xt_chainlabel chain,
+			struct xtc_handle *handle);
+
+/* Zeroes the counters in a chain. */
+int ip6tc_zero_entries(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
+
+/* Creates a new chain. */
+int ip6tc_create_chain(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
+
+/* Deletes a chain. */
+int ip6tc_delete_chain(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
+
+/* Renames a chain. */
+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 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 xt_chainlabel chain,
+			 struct xtc_handle *handle);
+
+/* read packet and byte counters for a specific rule */
+struct xt_counters *ip6tc_read_counter(const xt_chainlabel chain,
+					unsigned int rulenum,
+					struct xtc_handle *handle);
+
+/* zero packet and byte counters for a specific rule */
+int ip6tc_zero_counter(const xt_chainlabel chain,
+		       unsigned int rulenum,
+		       struct xtc_handle *handle);
+
+/* set packet and byte counters for a specific rule */
+int ip6tc_set_counter(const xt_chainlabel chain,
+		      unsigned int rulenum,
+		      struct xt_counters *counters,
+		      struct xtc_handle *handle);
+
+/* Makes the actual changes. */
+int ip6tc_commit(struct xtc_handle *handle);
+
+/* Get raw socket. */
+int ip6tc_get_raw_socket(void);
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *ip6tc_strerror(int err);
+
+extern void dump_entries6(struct xtc_handle *const);
+
+extern const struct xtc_ops ip6tc_ops;
+
+#endif /* _LIBIP6TC_H */
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
new file mode 100644
index 0000000..24cdbdb
--- /dev/null
+++ b/include/libiptc/libiptc.h
@@ -0,0 +1,172 @@
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <linux/types.h>
+#include <libiptc/ipt_kernel_headers.h>
+#ifdef __cplusplus
+#	include <climits>
+#else
+#	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
+
+#define iptc_handle xtc_handle
+#define ipt_chainlabel xt_chainlabel
+
+#define IPTC_LABEL_ACCEPT  "ACCEPT"
+#define IPTC_LABEL_DROP    "DROP"
+#define IPTC_LABEL_QUEUE   "QUEUE"
+#define IPTC_LABEL_RETURN  "RETURN"
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, struct xtc_handle *const handle);
+
+/* Take a snapshot of the rules.  Returns NULL on error. */
+struct xtc_handle *iptc_init(const char *tablename);
+
+/* Cleanup after iptc_init(). */
+void iptc_free(struct xtc_handle *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+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 xtc_handle *handle);
+
+/* Returns NULL when rules run out. */
+const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
+				       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 xtc_handle *handle);
+
+/* Is this a built-in chain? */
+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 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 xt_chainlabel chain,
+		      const struct ipt_entry *e,
+		      unsigned int rulenum,
+		      struct xtc_handle *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int iptc_replace_entry(const xt_chainlabel chain,
+		       const struct ipt_entry *e,
+		       unsigned int rulenum,
+		       struct xtc_handle *handle);
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int iptc_append_entry(const xt_chainlabel chain,
+		      const struct ipt_entry *e,
+		      struct xtc_handle *handle);
+
+/* Check whether a mathching rule exists */
+int iptc_check_entry(const xt_chainlabel chain,
+		      const struct ipt_entry *origfw,
+		      unsigned char *matchmask,
+		      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 xt_chainlabel chain,
+		      const struct ipt_entry *origfw,
+		      unsigned char *matchmask,
+		      struct xtc_handle *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int iptc_delete_num_entry(const xt_chainlabel chain,
+			  unsigned int rulenum,
+			  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 xt_chainlabel chain,
+			      struct ipt_entry *entry,
+			      struct xtc_handle *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int iptc_flush_entries(const xt_chainlabel chain,
+		       struct xtc_handle *handle);
+
+/* Zeroes the counters in a chain. */
+int iptc_zero_entries(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
+
+/* Creates a new chain. */
+int iptc_create_chain(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
+
+/* Deletes a chain. */
+int iptc_delete_chain(const xt_chainlabel chain,
+		      struct xtc_handle *handle);
+
+/* Renames a chain. */
+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 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 xt_chainlabel chain,
+			struct xtc_handle *handle);
+
+/* read packet and byte counters for a specific rule */
+struct xt_counters *iptc_read_counter(const xt_chainlabel chain,
+				       unsigned int rulenum,
+				       struct xtc_handle *handle);
+
+/* zero packet and byte counters for a specific rule */
+int iptc_zero_counter(const xt_chainlabel chain,
+		      unsigned int rulenum,
+		      struct xtc_handle *handle);
+
+/* set packet and byte counters for a specific rule */
+int iptc_set_counter(const xt_chainlabel chain,
+		     unsigned int rulenum,
+		     struct xt_counters *counters,
+		     struct xtc_handle *handle);
+
+/* Makes the actual changes. */
+int iptc_commit(struct xtc_handle *handle);
+
+/* Get raw socket. */
+int iptc_get_raw_socket(void);
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *iptc_strerror(int err);
+
+extern void dump_entries(struct xtc_handle *const);
+
+extern const struct xtc_ops iptc_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBIPTC_H */
diff --git a/include/libiptc/libxtc.h b/include/libiptc/libxtc.h
new file mode 100644
index 0000000..3701018
--- /dev/null
+++ b/include/libiptc/libxtc.h
@@ -0,0 +1,33 @@
+#ifndef _LIBXTC_H
+#define _LIBXTC_H
+/* Library which manipulates filtering rules. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter/x_tables.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XT_MIN_ALIGN
+/* xt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define XT_MIN_ALIGN (__alignof__(struct xt_entry))
+#endif
+
+#ifndef XT_ALIGN
+#define XT_ALIGN(s) (((s) + ((XT_MIN_ALIGN)-1)) & ~((XT_MIN_ALIGN)-1))
+#endif
+
+#define XTC_LABEL_ACCEPT  "ACCEPT"
+#define XTC_LABEL_DROP    "DROP"
+#define XTC_LABEL_QUEUE   "QUEUE"
+#define XTC_LABEL_RETURN  "RETURN"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBXTC_H */
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/libipulog/libipulog.h b/include/libipulog/libipulog.h
new file mode 100644
index 0000000..3f4cc2c
--- /dev/null
+++ b/include/libipulog/libipulog.h
@@ -0,0 +1,39 @@
+#ifndef _LIBIPULOG_H
+#define _LIBIPULOG_H
+
+/* libipulog.h,v 1.3 2001/05/21 19:15:16 laforge Exp */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <net/if.h>
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC 
+#define MSG_TRUNC	0x20
+#endif
+
+struct ipulog_handle;
+
+u_int32_t ipulog_group2gmask(u_int32_t group);
+
+struct ipulog_handle *ipulog_create_handle(u_int32_t gmask);
+
+void ipulog_destroy_handle(struct ipulog_handle *h);
+
+ssize_t ipulog_read(struct ipulog_handle *h,
+		    unsigned char *buf, size_t len, int timeout);
+
+ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
+				     const unsigned char *buf,
+				     size_t len);
+
+void ipulog_perror(const char *s);
+
+#endif /* _LIBULOG_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
new file mode 100644
index 0000000..d4c59f6
--- /dev/null
+++ b/include/linux/kernel.h
@@ -0,0 +1,29 @@
+#ifndef _LINUX_KERNEL_H
+#define _LINUX_KERNEL_H
+
+/*
+ * 'kernel.h' contains some often-used function prototypes etc
+ */
+#define __ALIGN_KERNEL(x, a)		__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
+#define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))
+
+
+#define SI_LOAD_SHIFT	16
+struct sysinfo {
+	long uptime;			/* Seconds since boot */
+	unsigned long loads[3];		/* 1, 5, and 15 minute load averages */
+	unsigned long totalram;		/* Total usable main memory size */
+	unsigned long freeram;		/* Available memory size */
+	unsigned long sharedram;	/* Amount of shared memory */
+	unsigned long bufferram;	/* Memory used by buffers */
+	unsigned long totalswap;	/* Total swap space size */
+	unsigned long freeswap;		/* swap space still available */
+	unsigned short procs;		/* Number of current processes */
+	unsigned short pad;		/* explicit padding for m68k */
+	unsigned long totalhigh;	/* Total high memory size */
+	unsigned long freehigh;		/* Available high memory size */
+	unsigned int mem_unit;		/* Memory unit size in bytes */
+	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
+};
+
+#endif
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
new file mode 100644
index 0000000..5477131
--- /dev/null
+++ b/include/linux/netfilter.h
@@ -0,0 +1,69 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#include <linux/types.h>
+
+#include <linux/sysctl.h>
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * 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) << 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.
+   <= 0x2000 is used for protocol-flags. */
+#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,
+	NF_INET_FORWARD,
+	NF_INET_LOCAL_OUT,
+	NF_INET_POST_ROUTING,
+	NF_INET_NUMHOOKS
+};
+
+enum {
+	NFPROTO_UNSPEC =  0,
+	NFPROTO_IPV4   =  2,
+	NFPROTO_ARP    =  3,
+	NFPROTO_BRIDGE =  7,
+	NFPROTO_IPV6   = 10,
+	NFPROTO_DECNET = 12,
+	NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+	__u32		all[4];
+	__be32		ip;
+	__be32		ip6[4];
+	struct in_addr	in;
+	struct in6_addr	in6;
+};
+
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
new file mode 100644
index 0000000..0dcf5dd
--- /dev/null
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -0,0 +1,268 @@
+/* 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_GET_FNAME	0x00000008	/* Get set index and family */
+struct ip_set_req_get_set_family {
+	unsigned int op;
+	unsigned int version;
+	unsigned int family;
+	union ip_set_name_index 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
new file mode 100644
index 0000000..38aa52d
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -0,0 +1,113 @@
+#ifndef _NF_CONNTRACK_COMMON_H
+#define _NF_CONNTRACK_COMMON_H
+/* Connection state tracking for netfilter.  This is separated from,
+   but required by, the NAT layer; it can also be used by an iptables
+   extension. */
+enum ip_conntrack_info {
+	/* Part of an established connection (either direction). */
+	IP_CT_ESTABLISHED,
+
+	/* Like NEW, but related to an existing connection, or ICMP error
+	   (in either direction). */
+	IP_CT_RELATED,
+
+	/* Started a new connection to track (only
+           IP_CT_DIR_ORIGINAL); may be a retransmission. */
+	IP_CT_NEW,
+
+	/* >= 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
+};
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+	/* It's an expected connection: bit 0 set.  This bit never changed */
+	IPS_EXPECTED_BIT = 0,
+	IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+	/* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
+	IPS_SEEN_REPLY_BIT = 1,
+	IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+	/* Conntrack should never be early-expired. */
+	IPS_ASSURED_BIT = 2,
+	IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+
+	/* Connection is confirmed: originating packet has left box */
+	IPS_CONFIRMED_BIT = 3,
+	IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
+
+	/* Connection needs src nat in orig dir.  This bit never changed. */
+	IPS_SRC_NAT_BIT = 4,
+	IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT),
+
+	/* Connection needs dst nat in orig dir.  This bit never changed. */
+	IPS_DST_NAT_BIT = 5,
+	IPS_DST_NAT = (1 << IPS_DST_NAT_BIT),
+
+	/* Both together. */
+	IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT),
+
+	/* Connection needs TCP sequence adjusted. */
+	IPS_SEQ_ADJUST_BIT = 6,
+	IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT),
+
+	/* NAT initialization bits. */
+	IPS_SRC_NAT_DONE_BIT = 7,
+	IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT),
+
+	IPS_DST_NAT_DONE_BIT = 8,
+	IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT),
+
+	/* Both together */
+	IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE),
+
+	/* Connection is dying (removed from lists), can not be unset. */
+	IPS_DYING_BIT = 9,
+	IPS_DYING = (1 << IPS_DYING_BIT),
+
+	/* Connection has fixed timeout. */
+	IPS_FIXED_TIMEOUT_BIT = 10,
+	IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+	/* 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 */
+enum ip_conntrack_events {
+	IPCT_NEW,		/* new conntrack */
+	IPCT_RELATED,		/* related conntrack */
+	IPCT_DESTROY,		/* destroyed conntrack */
+	IPCT_REPLY,		/* connection has seen two-way traffic */
+	IPCT_ASSURED,		/* connection status has changed to assured */
+	IPCT_PROTOINFO,		/* protocol information has changed */
+	IPCT_HELPER,		/* new helper has been set */
+	IPCT_MARK,		/* new mark has been set */
+	IPCT_NATSEQADJ,		/* NAT is doing sequence adjustment */
+	IPCT_SECMARK,		/* new security mark has been set */
+};
+
+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
new file mode 100644
index 0000000..2f6bbc5
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_tuple_common.h
@@ -0,0 +1,39 @@
+#ifndef _NF_CONNTRACK_TUPLE_COMMON_H
+#define _NF_CONNTRACK_TUPLE_COMMON_H
+
+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
new file mode 100644
index 0000000..4120970
--- /dev/null
+++ b/include/linux/netfilter/x_tables.h
@@ -0,0 +1,185 @@
+#ifndef _X_TABLES_H
+#define _X_TABLES_H
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#define XT_FUNCTION_MAXNAMELEN 30
+#define XT_EXTENSION_MAXNAMELEN 29
+#define XT_TABLE_MAXNAMELEN 32
+
+struct xt_entry_match {
+	union {
+		struct {
+			__u16 match_size;
+
+			/* Used by userspace */
+			char name[XT_EXTENSION_MAXNAMELEN];
+			__u8 revision;
+		} user;
+		struct {
+			__u16 match_size;
+
+			/* Used inside the kernel */
+			struct xt_match *match;
+		} kernel;
+
+		/* Total length */
+		__u16 match_size;
+	} u;
+
+	unsigned char data[0];
+};
+
+struct xt_entry_target {
+	union {
+		struct {
+			__u16 target_size;
+
+			/* Used by userspace */
+			char name[XT_EXTENSION_MAXNAMELEN];
+			__u8 revision;
+		} user;
+		struct {
+			__u16 target_size;
+
+			/* Used inside the kernel */
+			struct xt_target *target;
+		} kernel;
+
+		/* Total length */
+		__u16 target_size;
+	} u;
+
+	unsigned char data[0];
+};
+
+#define XT_TARGET_INIT(__name, __size)					       \
+{									       \
+	.target.u.user = {						       \
+		.target_size	= XT_ALIGN(__size),			       \
+		.name		= __name,				       \
+	},								       \
+}
+
+struct xt_standard_target {
+	struct xt_entry_target target;
+	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 {
+	char name[XT_EXTENSION_MAXNAMELEN];
+	__u8 revision;
+};
+
+/* CONTINUE verdict for targets */
+#define XT_CONTINUE 0xFFFFFFFF
+
+/* For standard target */
+#define XT_RETURN (-NF_REPEAT - 1)
+
+/* this is a dummy structure to find out the alignment requirement for a struct
+ * containing all the fundamental data types that are used in ipt_entry,
+ * ip6t_entry and arpt_entry.  This sucks, and it is a hack.  It will be my
+ * personal pleasure to remove it -HW
+ */
+struct _xt_align {
+	__u8 u8;
+	__u16 u16;
+	__u32 u32;
+	__u64 u64;
+};
+
+#define XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _xt_align))
+
+/* Standard return verdict, or do jump. */
+#define XT_STANDARD_TARGET ""
+/* Error verdict. */
+#define XT_ERROR_TARGET "ERROR"
+
+#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
+#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
+
+struct xt_counters {
+	__u64 pcnt, bcnt;			/* Packet and byte counters */
+};
+
+/* The argument to IPT_SO_ADD_COUNTERS. */
+struct xt_counters_info {
+	/* Which table. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	unsigned int num_counters;
+
+	/* The counters (actually `number' of these). */
+	struct xt_counters counters[0];
+};
+
+#define XT_INV_PROTO		0x40	/* Invert the sense of PROTO. */
+
+/* fn returns 0 to continue iteration */
+#define XT_MATCH_ITERATE(type, e, fn, args...)			\
+({								\
+	unsigned int __i;					\
+	int __ret = 0;						\
+	struct xt_entry_match *__m;				\
+								\
+	for (__i = sizeof(type);				\
+	     __i < (e)->target_offset;				\
+	     __i += __m->u.match_size) {			\
+		__m = (void *)e + __i;				\
+								\
+		__ret = fn(__m , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/* fn returns 0 to continue iteration */
+#define XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
+({								\
+	unsigned int __i, __n;					\
+	int __ret = 0;						\
+	type *__entry;						\
+								\
+	for (__i = 0, __n = 0; __i < (size);			\
+	     __i += __entry->next_offset, __n++) { 		\
+		__entry = (void *)(entries) + __i;		\
+		if (__n < n)					\
+			continue;				\
+								\
+		__ret = fn(__entry , ## args);			\
+		if (__ret != 0)					\
+			break;					\
+	}							\
+	__ret;							\
+})
+
+/* fn returns 0 to continue iteration */
+#define XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
+	XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
+
+
+/* pos is normally a struct ipt_entry/ip6t_entry/etc. */
+#define xt_entry_foreach(pos, ehead, esize) \
+	for ((pos) = (typeof(pos))(ehead); \
+	     (pos) < (typeof(pos))((char *)(ehead) + (esize)); \
+	     (pos) = (typeof(pos))((char *)(pos) + (pos)->next_offset))
+
+/* can only be xt_entry_match, so no use of typeof here */
+#define xt_ematch_foreach(pos, entry) \
+	for ((pos) = (struct xt_entry_match *)entry->elems; \
+	     (pos) < (struct xt_entry_match *)((char *)(entry) + \
+	             (entry)->target_offset); \
+	     (pos) = (struct xt_entry_match *)((char *)(pos) + \
+	             (pos)->u.match_size))
+
+
+#endif /* _X_TABLES_H */
diff --git a/include/linux/netfilter/xt_AUDIT.h b/include/linux/netfilter/xt_AUDIT.h
new file mode 100644
index 0000000..38751d2
--- /dev/null
+++ b/include/linux/netfilter/xt_AUDIT.h
@@ -0,0 +1,30 @@
+/*
+ * Header file for iptables xt_AUDIT target
+ *
+ * (C) 2010-2011 Thomas Graf <tgraf@redhat.com>
+ * (C) 2010-2011 Red Hat, Inc.
+ *
+ * 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 _XT_AUDIT_TARGET_H
+#define _XT_AUDIT_TARGET_H
+
+#include <linux/types.h>
+
+enum {
+	XT_AUDIT_TYPE_ACCEPT = 0,
+	XT_AUDIT_TYPE_DROP,
+	XT_AUDIT_TYPE_REJECT,
+	__XT_AUDIT_TYPE_MAX,
+};
+
+#define XT_AUDIT_TYPE_MAX (__XT_AUDIT_TYPE_MAX - 1)
+
+struct xt_audit_info {
+	__u8 type; /* XT_AUDIT_TYPE_* */
+};
+
+#endif /* _XT_AUDIT_TARGET_H */
diff --git a/include/linux/netfilter/xt_CHECKSUM.h b/include/linux/netfilter/xt_CHECKSUM.h
new file mode 100644
index 0000000..9a2e466
--- /dev/null
+++ b/include/linux/netfilter/xt_CHECKSUM.h
@@ -0,0 +1,20 @@
+/* Header file for iptables ipt_CHECKSUM target
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 Red Hat Inc
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+*/
+#ifndef _XT_CHECKSUM_TARGET_H
+#define _XT_CHECKSUM_TARGET_H
+
+#include <linux/types.h>
+
+#define XT_CHECKSUM_OP_FILL	0x01	/* fill in checksum in IP header */
+
+struct xt_CHECKSUM_info {
+	__u8 operation;	/* bitset of operations */
+};
+
+#endif /* _XT_CHECKSUM_TARGET_H */
diff --git a/include/linux/netfilter/xt_CLASSIFY.h b/include/linux/netfilter/xt_CLASSIFY.h
new file mode 100644
index 0000000..a813bf1
--- /dev/null
+++ b/include/linux/netfilter/xt_CLASSIFY.h
@@ -0,0 +1,10 @@
+#ifndef _XT_CLASSIFY_H
+#define _XT_CLASSIFY_H
+
+#include <linux/types.h>
+
+struct xt_classify_target_info {
+	__u32 priority;
+};
+
+#endif /*_XT_CLASSIFY_H */
diff --git a/include/linux/netfilter/xt_CONNMARK.h b/include/linux/netfilter/xt_CONNMARK.h
new file mode 100644
index 0000000..2f2e48e
--- /dev/null
+++ b/include/linux/netfilter/xt_CONNMARK.h
@@ -0,0 +1,6 @@
+#ifndef _XT_CONNMARK_H_target
+#define _XT_CONNMARK_H_target
+
+#include <linux/netfilter/xt_connmark.h>
+
+#endif /*_XT_CONNMARK_H_target*/
diff --git a/include/linux/netfilter/xt_CONNSECMARK.h b/include/linux/netfilter/xt_CONNSECMARK.h
new file mode 100644
index 0000000..b973ff8
--- /dev/null
+++ b/include/linux/netfilter/xt_CONNSECMARK.h
@@ -0,0 +1,15 @@
+#ifndef _XT_CONNSECMARK_H_target
+#define _XT_CONNSECMARK_H_target
+
+#include <linux/types.h>
+
+enum {
+	CONNSECMARK_SAVE = 1,
+	CONNSECMARK_RESTORE,
+};
+
+struct xt_connsecmark_target_info {
+	__u8 mode;
+};
+
+#endif /*_XT_CONNSECMARK_H_target */
diff --git a/include/linux/netfilter/xt_CT.h b/include/linux/netfilter/xt_CT.h
new file mode 100644
index 0000000..54528fd
--- /dev/null
+++ b/include/linux/netfilter/xt_CT.h
@@ -0,0 +1,34 @@
+#ifndef _XT_CT_H
+#define _XT_CT_H
+
+#include <linux/types.h>
+
+enum {
+	XT_CT_NOTRACK		= 1 << 0,
+	XT_CT_NOTRACK_ALIAS	= 1 << 1,
+};
+
+struct xt_ct_target_info {
+	__u16 flags;
+	__u16 zone;
+	__u32 ct_events;
+	__u32 exp_events;
+	char helper[16];
+
+	/* Used internally by the kernel */
+	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_DSCP.h b/include/linux/netfilter/xt_DSCP.h
new file mode 100644
index 0000000..648e0b3
--- /dev/null
+++ b/include/linux/netfilter/xt_DSCP.h
@@ -0,0 +1,26 @@
+/* x_tables module for setting the IPv4/IPv6 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ * This software is distributed under GNU GPL v2, 1991
+ *
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * xt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp
+*/
+#ifndef _XT_DSCP_TARGET_H
+#define _XT_DSCP_TARGET_H
+#include <linux/netfilter/xt_dscp.h>
+#include <linux/types.h>
+
+/* target info */
+struct xt_DSCP_info {
+	__u8 dscp;
+};
+
+struct xt_tos_target_info {
+	__u8 tos_value;
+	__u8 tos_mask;
+};
+
+#endif /* _XT_DSCP_TARGET_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_IDLETIMER.h b/include/linux/netfilter/xt_IDLETIMER.h
new file mode 100644
index 0000000..208ae93
--- /dev/null
+++ b/include/linux/netfilter/xt_IDLETIMER.h
@@ -0,0 +1,45 @@
+/*
+ * linux/include/linux/netfilter/xt_IDLETIMER.h
+ *
+ * Header file for Xtables timer target module.
+ *
+ * Copyright (C) 2004, 2010 Nokia Corporation
+ * Written by Timo Teras <ext-timo.teras@nokia.com>
+ *
+ * Converted to x_tables and forward-ported to 2.6.34
+ * by Luciano Coelho <luciano.coelho@nokia.com>
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.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 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef _XT_IDLETIMER_H
+#define _XT_IDLETIMER_H
+
+#include <linux/types.h>
+
+#define MAX_IDLETIMER_LABEL_SIZE 28
+
+struct idletimer_tg_info {
+	__u32 timeout;
+
+	char label[MAX_IDLETIMER_LABEL_SIZE];
+
+	/* for kernel module internal use only */
+	struct idletimer_tg *timer __attribute__((aligned(8)));
+};
+
+#endif
diff --git a/include/linux/netfilter/xt_LED.h b/include/linux/netfilter/xt_LED.h
new file mode 100644
index 0000000..f5509e7
--- /dev/null
+++ b/include/linux/netfilter/xt_LED.h
@@ -0,0 +1,15 @@
+#ifndef _XT_LED_H
+#define _XT_LED_H
+
+#include <linux/types.h>
+
+struct xt_led_info {
+	char id[27];        /* Unique ID for this trigger in the LED class */
+	__u8 always_blink;  /* Blink even if the LED is already on */
+	__u32 delay;        /* Delay until LED is switched off after trigger */
+
+	/* Kernel data used in the module */
+	void *internal_data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_LED_H */
diff --git a/include/linux/netfilter/xt_MARK.h b/include/linux/netfilter/xt_MARK.h
new file mode 100644
index 0000000..41c456d
--- /dev/null
+++ b/include/linux/netfilter/xt_MARK.h
@@ -0,0 +1,6 @@
+#ifndef _XT_MARK_H_target
+#define _XT_MARK_H_target
+
+#include <linux/netfilter/xt_mark.h>
+
+#endif /*_XT_MARK_H_target */
diff --git a/include/linux/netfilter/xt_NFLOG.h b/include/linux/netfilter/xt_NFLOG.h
new file mode 100644
index 0000000..87b5831
--- /dev/null
+++ b/include/linux/netfilter/xt_NFLOG.h
@@ -0,0 +1,20 @@
+#ifndef _XT_NFLOG_TARGET
+#define _XT_NFLOG_TARGET
+
+#include <linux/types.h>
+
+#define XT_NFLOG_DEFAULT_GROUP		0x1
+#define XT_NFLOG_DEFAULT_THRESHOLD	0
+
+#define XT_NFLOG_MASK			0x0
+
+struct xt_nflog_info {
+	__u32	len;
+	__u16	group;
+	__u16	threshold;
+	__u16	flags;
+	__u16	pad;
+	char		prefix[64];
+};
+
+#endif /* _XT_NFLOG_TARGET */
diff --git a/include/linux/netfilter/xt_NFQUEUE.h b/include/linux/netfilter/xt_NFQUEUE.h
new file mode 100644
index 0000000..8bb5fe6
--- /dev/null
+++ b/include/linux/netfilter/xt_NFQUEUE.h
@@ -0,0 +1,38 @@
+/* iptables module for using NFQUEUE mechanism
+ *
+ * (C) 2005 Harald Welte <laforge@netfilter.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+*/
+#ifndef _XT_NFQ_TARGET_H
+#define _XT_NFQ_TARGET_H
+
+#include <linux/types.h>
+
+/* target info */
+struct xt_NFQ_info {
+	__u16 queuenum;
+};
+
+struct xt_NFQ_info_v1 {
+	__u16 queuenum;
+	__u16 queues_total;
+};
+
+struct xt_NFQ_info_v2 {
+	__u16 queuenum;
+	__u16 queues_total;
+	__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_RATEEST.h b/include/linux/netfilter/xt_RATEEST.h
new file mode 100644
index 0000000..6605e20
--- /dev/null
+++ b/include/linux/netfilter/xt_RATEEST.h
@@ -0,0 +1,15 @@
+#ifndef _XT_RATEEST_TARGET_H
+#define _XT_RATEEST_TARGET_H
+
+#include <linux/types.h>
+
+struct xt_rateest_target_info {
+	char			name[IFNAMSIZ];
+	__s8			interval;
+	__u8		ewma_log;
+
+	/* Used internally by the kernel */
+	struct xt_rateest	*est __attribute__((aligned(8)));
+};
+
+#endif /* _XT_RATEEST_TARGET_H */
diff --git a/include/linux/netfilter/xt_SECMARK.h b/include/linux/netfilter/xt_SECMARK.h
new file mode 100644
index 0000000..989092b
--- /dev/null
+++ b/include/linux/netfilter/xt_SECMARK.h
@@ -0,0 +1,22 @@
+#ifndef _XT_SECMARK_H_target
+#define _XT_SECMARK_H_target
+
+#include <linux/types.h>
+
+/*
+ * This is intended for use by various security subsystems (but not
+ * at the same time).
+ *
+ * 'mode' refers to the specific security subsystem which the
+ * packets are being marked for.
+ */
+#define SECMARK_MODE_SEL	0x01		/* SELinux */
+#define SECMARK_SECCTX_MAX	256
+
+struct xt_secmark_target_info {
+	__u8 mode;
+	__u32 secid;
+	char secctx[SECMARK_SECCTX_MAX];
+};
+
+#endif /*_XT_SECMARK_H_target */
diff --git a/include/linux/netfilter/xt_SYNPROXY.h b/include/linux/netfilter/xt_SYNPROXY.h
new file mode 100644
index 0000000..2d59fba
--- /dev/null
+++ b/include/linux/netfilter/xt_SYNPROXY.h
@@ -0,0 +1,16 @@
+#ifndef _XT_SYNPROXY_H
+#define _XT_SYNPROXY_H
+
+#define XT_SYNPROXY_OPT_MSS		0x01
+#define XT_SYNPROXY_OPT_WSCALE		0x02
+#define XT_SYNPROXY_OPT_SACK_PERM	0x04
+#define XT_SYNPROXY_OPT_TIMESTAMP	0x08
+#define XT_SYNPROXY_OPT_ECN		0x10
+
+struct xt_synproxy_info {
+	__u8	options;
+	__u8	wscale;
+	__u16	mss;
+};
+
+#endif /* _XT_SYNPROXY_H */
diff --git a/include/linux/netfilter/xt_TCPMSS.h b/include/linux/netfilter/xt_TCPMSS.h
new file mode 100644
index 0000000..9a6960a
--- /dev/null
+++ b/include/linux/netfilter/xt_TCPMSS.h
@@ -0,0 +1,12 @@
+#ifndef _XT_TCPMSS_H
+#define _XT_TCPMSS_H
+
+#include <linux/types.h>
+
+struct xt_tcpmss_info {
+	__u16 mss;
+};
+
+#define XT_TCPMSS_CLAMP_PMTU 0xffff
+
+#endif /* _XT_TCPMSS_H */
diff --git a/include/linux/netfilter/xt_TCPOPTSTRIP.h b/include/linux/netfilter/xt_TCPOPTSTRIP.h
new file mode 100644
index 0000000..7157318
--- /dev/null
+++ b/include/linux/netfilter/xt_TCPOPTSTRIP.h
@@ -0,0 +1,15 @@
+#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) \
+	(((1U << (idx & 31)) & bmap[(idx) >> 5]) != 0)
+
+struct xt_tcpoptstrip_target_info {
+	__u32 strip_bmap[8];
+};
+
+#endif /* _XT_TCPOPTSTRIP_H */
diff --git a/include/linux/netfilter/xt_TEE.h b/include/linux/netfilter/xt_TEE.h
new file mode 100644
index 0000000..5c21d5c
--- /dev/null
+++ b/include/linux/netfilter/xt_TEE.h
@@ -0,0 +1,12 @@
+#ifndef _XT_TEE_TARGET_H
+#define _XT_TEE_TARGET_H
+
+struct xt_tee_tginfo {
+	union nf_inet_addr gw;
+	char oif[16];
+
+	/* used internally by the kernel */
+	struct xt_tee_priv *priv __attribute__((aligned(8)));
+};
+
+#endif /* _XT_TEE_TARGET_H */
diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h
new file mode 100644
index 0000000..902043c
--- /dev/null
+++ b/include/linux/netfilter/xt_TPROXY.h
@@ -0,0 +1,23 @@
+#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. */
+struct xt_tproxy_target_info {
+	__u32 mark_mask;
+	__u32 mark_value;
+	__be32 laddr;
+	__be16 lport;
+};
+
+struct xt_tproxy_target_info_v1 {
+	__u32 mark_mask;
+	__u32 mark_value;
+	union nf_inet_addr laddr;
+	__be16 lport;
+};
+
+#endif /* _XT_TPROXY_H */
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
new file mode 100644
index 0000000..9b883c8
--- /dev/null
+++ b/include/linux/netfilter/xt_cluster.h
@@ -0,0 +1,19 @@
+#ifndef _XT_CLUSTER_MATCH_H
+#define _XT_CLUSTER_MATCH_H
+
+#include <linux/types.h>
+
+enum xt_cluster_flags {
+	XT_CLUSTER_F_INV	= (1 << 0)
+};
+
+struct xt_cluster_match_info {
+	__u32 total_nodes;
+	__u32 node_mask;
+	__u32 hash_seed;
+	__u32 flags;
+};
+
+#define XT_CLUSTER_NODES_MAX	32
+
+#endif /* _XT_CLUSTER_MATCH_H */
diff --git a/include/linux/netfilter/xt_comment.h b/include/linux/netfilter/xt_comment.h
new file mode 100644
index 0000000..0ea5e79
--- /dev/null
+++ b/include/linux/netfilter/xt_comment.h
@@ -0,0 +1,10 @@
+#ifndef _XT_COMMENT_H
+#define _XT_COMMENT_H
+
+#define XT_MAX_COMMENT_LEN 256
+
+struct xt_comment_info {
+	char comment[XT_MAX_COMMENT_LEN];
+};
+
+#endif /* XT_COMMENT_H */
diff --git a/include/linux/netfilter/xt_connbytes.h b/include/linux/netfilter/xt_connbytes.h
new file mode 100644
index 0000000..f1d6c15
--- /dev/null
+++ b/include/linux/netfilter/xt_connbytes.h
@@ -0,0 +1,26 @@
+#ifndef _XT_CONNBYTES_H
+#define _XT_CONNBYTES_H
+
+#include <linux/types.h>
+
+enum xt_connbytes_what {
+	XT_CONNBYTES_PKTS,
+	XT_CONNBYTES_BYTES,
+	XT_CONNBYTES_AVGPKT,
+};
+
+enum xt_connbytes_direction {
+	XT_CONNBYTES_DIR_ORIGINAL,
+	XT_CONNBYTES_DIR_REPLY,
+	XT_CONNBYTES_DIR_BOTH,
+};
+
+struct xt_connbytes_info {
+	struct {
+		__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 */
+};
+#endif
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
new file mode 100644
index 0000000..f9e8c67
--- /dev/null
+++ b/include/linux/netfilter/xt_connlimit.h
@@ -0,0 +1,34 @@
+#ifndef _XT_CONNLIMIT_H
+#define _XT_CONNLIMIT_H
+
+#include <linux/types.h>
+
+struct xt_connlimit_data;
+
+enum {
+	XT_CONNLIMIT_INVERT = 1 << 0,
+	XT_CONNLIMIT_DADDR  = 1 << 1,
+};
+
+struct xt_connlimit_info {
+	union {
+		union nf_inet_addr mask;
+		union {
+			__be32 v4_mask;
+			__be32 v6_mask[4];
+		};
+	};
+	unsigned int limit;
+	union {
+		/* revision 0 */
+		unsigned int inverse;
+
+		/* revision 1 */
+		__u32 flags;
+	};
+
+	/* Used internally by the kernel */
+	struct xt_connlimit_data *data __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CONNLIMIT_H */
diff --git a/include/linux/netfilter/xt_connmark.h b/include/linux/netfilter/xt_connmark.h
new file mode 100644
index 0000000..efc17a8
--- /dev/null
+++ b/include/linux/netfilter/xt_connmark.h
@@ -0,0 +1,31 @@
+#ifndef _XT_CONNMARK_H
+#define _XT_CONNMARK_H
+
+#include <linux/types.h>
+
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+enum {
+	XT_CONNMARK_SET = 0,
+	XT_CONNMARK_SAVE,
+	XT_CONNMARK_RESTORE
+};
+
+struct xt_connmark_tginfo1 {
+	__u32 ctmark, ctmask, nfmask;
+	__u8 mode;
+};
+
+struct xt_connmark_mtinfo1 {
+	__u32 mark, mask;
+	__u8 invert;
+};
+
+#endif /*_XT_CONNMARK_H*/
diff --git a/include/linux/netfilter/xt_conntrack.h b/include/linux/netfilter/xt_conntrack.h
new file mode 100644
index 0000000..e971501
--- /dev/null
+++ b/include/linux/netfilter/xt_conntrack.h
@@ -0,0 +1,77 @@
+/* Header file for kernel module to match connection tracking information.
+ * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ */
+
+#ifndef _XT_CONNTRACK_H
+#define _XT_CONNTRACK_H
+
+#include <linux/types.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+
+#define XT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
+#define XT_CONNTRACK_STATE_INVALID (1 << 0)
+
+#define XT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1))
+#define XT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2))
+#define XT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
+
+/* flags, invflags: */
+enum {
+	XT_CONNTRACK_STATE        = 1 << 0,
+	XT_CONNTRACK_PROTO        = 1 << 1,
+	XT_CONNTRACK_ORIGSRC      = 1 << 2,
+	XT_CONNTRACK_ORIGDST      = 1 << 3,
+	XT_CONNTRACK_REPLSRC      = 1 << 4,
+	XT_CONNTRACK_REPLDST      = 1 << 5,
+	XT_CONNTRACK_STATUS       = 1 << 6,
+	XT_CONNTRACK_EXPIRES      = 1 << 7,
+	XT_CONNTRACK_ORIGSRC_PORT = 1 << 8,
+	XT_CONNTRACK_ORIGDST_PORT = 1 << 9,
+	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 {
+	union nf_inet_addr origsrc_addr, origsrc_mask;
+	union nf_inet_addr origdst_addr, origdst_mask;
+	union nf_inet_addr replsrc_addr, replsrc_mask;
+	union nf_inet_addr repldst_addr, repldst_mask;
+	__u32 expires_min, expires_max;
+	__u16 l4proto;
+	__be16 origsrc_port, origdst_port;
+	__be16 replsrc_port, repldst_port;
+	__u16 match_flags, invert_flags;
+	__u8 state_mask, status_mask;
+};
+
+struct xt_conntrack_mtinfo2 {
+	union nf_inet_addr origsrc_addr, origsrc_mask;
+	union nf_inet_addr origdst_addr, origdst_mask;
+	union nf_inet_addr replsrc_addr, replsrc_mask;
+	union nf_inet_addr repldst_addr, repldst_mask;
+	__u32 expires_min, expires_max;
+	__u16 l4proto;
+	__be16 origsrc_port, origdst_port;
+	__be16 replsrc_port, repldst_port;
+	__u16 match_flags, invert_flags;
+	__u16 state_mask, status_mask;
+};
+
+struct xt_conntrack_mtinfo3 {
+	union nf_inet_addr origsrc_addr, origsrc_mask;
+	union nf_inet_addr origdst_addr, origdst_mask;
+	union nf_inet_addr replsrc_addr, replsrc_mask;
+	union nf_inet_addr repldst_addr, repldst_mask;
+	__u32 expires_min, expires_max;
+	__u16 l4proto;
+	__u16 origsrc_port, origdst_port;
+	__u16 replsrc_port, repldst_port;
+	__u16 match_flags, invert_flags;
+	__u16 state_mask, status_mask;
+	__u16 origsrc_port_high, origdst_port_high;
+	__u16 replsrc_port_high, repldst_port_high;
+};
+
+#endif /*_XT_CONNTRACK_H*/
diff --git a/include/linux/netfilter/xt_cpu.h b/include/linux/netfilter/xt_cpu.h
new file mode 100644
index 0000000..93c7f11
--- /dev/null
+++ b/include/linux/netfilter/xt_cpu.h
@@ -0,0 +1,11 @@
+#ifndef _XT_CPU_H
+#define _XT_CPU_H
+
+#include <linux/types.h>
+
+struct xt_cpu_info {
+	__u32	cpu;
+	__u32	invert;
+};
+
+#endif /*_XT_CPU_H*/
diff --git a/include/linux/netfilter/xt_dccp.h b/include/linux/netfilter/xt_dccp.h
new file mode 100644
index 0000000..a579e1b
--- /dev/null
+++ b/include/linux/netfilter/xt_dccp.h
@@ -0,0 +1,25 @@
+#ifndef _XT_DCCP_H_
+#define _XT_DCCP_H_
+
+#include <linux/types.h>
+
+#define XT_DCCP_SRC_PORTS	        0x01
+#define XT_DCCP_DEST_PORTS	        0x02
+#define XT_DCCP_TYPE			0x04
+#define XT_DCCP_OPTION			0x08
+
+#define XT_DCCP_VALID_FLAGS		0x0f
+
+struct xt_dccp_info {
+	__u16 dpts[2];  /* Min, Max */
+	__u16 spts[2];  /* Min, Max */
+
+	__u16 flags;
+	__u16 invflags;
+
+	__u16 typemask;
+	__u8 option;
+};
+
+#endif /* _XT_DCCP_H_ */
+
diff --git a/include/linux/netfilter/xt_devgroup.h b/include/linux/netfilter/xt_devgroup.h
new file mode 100644
index 0000000..1babde0
--- /dev/null
+++ b/include/linux/netfilter/xt_devgroup.h
@@ -0,0 +1,21 @@
+#ifndef _XT_DEVGROUP_H
+#define _XT_DEVGROUP_H
+
+#include <linux/types.h>
+
+enum xt_devgroup_flags {
+	XT_DEVGROUP_MATCH_SRC	= 0x1,
+	XT_DEVGROUP_INVERT_SRC	= 0x2,
+	XT_DEVGROUP_MATCH_DST	= 0x4,
+	XT_DEVGROUP_INVERT_DST	= 0x8,
+};
+
+struct xt_devgroup_info {
+	__u32	flags;
+	__u32	src_group;
+	__u32	src_mask;
+	__u32	dst_group;
+	__u32	dst_mask;
+};
+
+#endif /* _XT_DEVGROUP_H */
diff --git a/include/linux/netfilter/xt_dscp.h b/include/linux/netfilter/xt_dscp.h
new file mode 100644
index 0000000..15f8932
--- /dev/null
+++ b/include/linux/netfilter/xt_dscp.h
@@ -0,0 +1,31 @@
+/* x_tables module for matching the IPv4/IPv6 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * This software is distributed under GNU GPL v2, 1991
+ *
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * xt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp
+*/
+#ifndef _XT_DSCP_H
+#define _XT_DSCP_H
+
+#include <linux/types.h>
+
+#define XT_DSCP_MASK	0xfc	/* 11111100 */
+#define XT_DSCP_SHIFT	2
+#define XT_DSCP_MAX	0x3f	/* 00111111 */
+
+/* match info */
+struct xt_dscp_info {
+	__u8 dscp;
+	__u8 invert;
+};
+
+struct xt_tos_match_info {
+	__u8 tos_mask;
+	__u8 tos_value;
+	__u8 invert;
+};
+
+#endif /* _XT_DSCP_H */
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_esp.h b/include/linux/netfilter/xt_esp.h
new file mode 100644
index 0000000..ee68824
--- /dev/null
+++ b/include/linux/netfilter/xt_esp.h
@@ -0,0 +1,15 @@
+#ifndef _XT_ESP_H
+#define _XT_ESP_H
+
+#include <linux/types.h>
+
+struct xt_esp {
+	__u32 spis[2];	/* Security Parameter Index */
+	__u8  invflags;	/* Inverse flags */
+};
+
+/* Values for "invflags" field in struct xt_esp. */
+#define XT_ESP_INV_SPI	0x01	/* Invert the sense of spi. */
+#define XT_ESP_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_XT_ESP_H*/
diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h
new file mode 100644
index 0000000..141efbd
--- /dev/null
+++ b/include/linux/netfilter/xt_hashlimit.h
@@ -0,0 +1,72 @@
+#ifndef _XT_HASHLIMIT_H
+#define _XT_HASHLIMIT_H
+
+#include <linux/types.h>
+
+/* 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 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;
+
+enum {
+	XT_HASHLIMIT_HASH_DIP = 1 << 0,
+	XT_HASHLIMIT_HASH_DPT = 1 << 1,
+	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 {
+	__u32 mode;	  /* bitmask of XT_HASHLIMIT_HASH_* */
+	__u32 avg;    /* Average secs between packets * scale */
+	__u32 burst;  /* Period multiplier for upper limit. */
+
+	/* user specified */
+	__u32 size;		/* how many buckets */
+	__u32 max;		/* max number of entries */
+	__u32 gc_interval;	/* gc interval */
+	__u32 expire;	/* when do entries expire? */
+};
+
+struct xt_hashlimit_info {
+	char name [IFNAMSIZ];		/* name */
+	struct hashlimit_cfg cfg;
+
+	/* Used internally by the kernel */
+	struct xt_hashlimit_htable *hinfo;
+	union {
+		void *ptr;
+		struct xt_hashlimit_info *master;
+	} u;
+};
+
+struct hashlimit_cfg1 {
+	__u32 mode;	  /* bitmask of XT_HASHLIMIT_HASH_* */
+	__u32 avg;    /* Average secs between packets * scale */
+	__u32 burst;  /* Period multiplier for upper limit. */
+
+	/* user specified */
+	__u32 size;		/* how many buckets */
+	__u32 max;		/* max number of entries */
+	__u32 gc_interval;	/* gc interval */
+	__u32 expire;	/* when do entries expire? */
+
+	__u8 srcmask, dstmask;
+};
+
+struct xt_hashlimit_mtinfo1 {
+	char name[IFNAMSIZ];
+	struct hashlimit_cfg1 cfg;
+
+	/* Used internally by the kernel */
+	struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
+};
+
+#endif /*_XT_HASHLIMIT_H*/
diff --git a/include/linux/netfilter/xt_helper.h b/include/linux/netfilter/xt_helper.h
new file mode 100644
index 0000000..6b42763
--- /dev/null
+++ b/include/linux/netfilter/xt_helper.h
@@ -0,0 +1,8 @@
+#ifndef _XT_HELPER_H
+#define _XT_HELPER_H
+
+struct xt_helper_info {
+	int invert;
+	char name[30];
+};
+#endif /* _XT_HELPER_H */
diff --git a/include/linux/netfilter/xt_iprange.h b/include/linux/netfilter/xt_iprange.h
new file mode 100644
index 0000000..c1f21a7
--- /dev/null
+++ b/include/linux/netfilter/xt_iprange.h
@@ -0,0 +1,19 @@
+#ifndef _LINUX_NETFILTER_XT_IPRANGE_H
+#define _LINUX_NETFILTER_XT_IPRANGE_H 1
+
+#include <linux/types.h>
+
+enum {
+	IPRANGE_SRC     = 1 << 0,	/* match source IP address */
+	IPRANGE_DST     = 1 << 1,	/* match destination IP address */
+	IPRANGE_SRC_INV = 1 << 4,	/* negate the condition */
+	IPRANGE_DST_INV = 1 << 5,	/* -"- */
+};
+
+struct xt_iprange_mtinfo {
+	union nf_inet_addr src_min, src_max;
+	union nf_inet_addr dst_min, dst_max;
+	__u8 flags;
+};
+
+#endif /* _LINUX_NETFILTER_XT_IPRANGE_H */
diff --git a/include/linux/netfilter/xt_ipvs.h b/include/linux/netfilter/xt_ipvs.h
new file mode 100644
index 0000000..eff34ac
--- /dev/null
+++ b/include/linux/netfilter/xt_ipvs.h
@@ -0,0 +1,29 @@
+#ifndef _XT_IPVS_H
+#define _XT_IPVS_H
+
+#include <linux/types.h>
+
+enum {
+	XT_IPVS_IPVS_PROPERTY =	1 << 0, /* all other options imply this one */
+	XT_IPVS_PROTO =		1 << 1,
+	XT_IPVS_VADDR =		1 << 2,
+	XT_IPVS_VPORT =		1 << 3,
+	XT_IPVS_DIR =		1 << 4,
+	XT_IPVS_METHOD =	1 << 5,
+	XT_IPVS_VPORTCTL =	1 << 6,
+	XT_IPVS_MASK =		(1 << 7) - 1,
+	XT_IPVS_ONCE_MASK =	XT_IPVS_MASK & ~XT_IPVS_IPVS_PROPERTY
+};
+
+struct xt_ipvs_mtinfo {
+	union nf_inet_addr	vaddr, vmask;
+	__be16			vport;
+	__u8			l4proto;
+	__u8			fwd_method;
+	__be16			vportctl;
+
+	__u8			invert;
+	__u8			bitmask;
+};
+
+#endif /* _XT_IPVS_H */
diff --git a/include/linux/netfilter/xt_length.h b/include/linux/netfilter/xt_length.h
new file mode 100644
index 0000000..b82ed7c
--- /dev/null
+++ b/include/linux/netfilter/xt_length.h
@@ -0,0 +1,11 @@
+#ifndef _XT_LENGTH_H
+#define _XT_LENGTH_H
+
+#include <linux/types.h>
+
+struct xt_length_info {
+    __u16	min, max;
+    __u8	invert;
+};
+
+#endif /*_XT_LENGTH_H*/
diff --git a/include/linux/netfilter/xt_limit.h b/include/linux/netfilter/xt_limit.h
new file mode 100644
index 0000000..bb47fc4
--- /dev/null
+++ b/include/linux/netfilter/xt_limit.h
@@ -0,0 +1,24 @@
+#ifndef _XT_RATE_H
+#define _XT_RATE_H
+
+#include <linux/types.h>
+
+/* timings are in milliseconds. */
+#define XT_LIMIT_SCALE 10000
+
+struct xt_limit_priv;
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+struct xt_rateinfo {
+	__u32 avg;    /* Average secs between packets * scale */
+	__u32 burst;  /* Period multiplier for upper limit. */
+
+	/* Used internally by the kernel */
+	unsigned long prev; /* moved to xt_limit_priv */
+	__u32 credit; /* moved to xt_limit_priv */
+	__u32 credit_cap, cost;
+
+	struct xt_limit_priv *master;
+};
+#endif /*_XT_RATE_H*/
diff --git a/include/linux/netfilter/xt_mac.h b/include/linux/netfilter/xt_mac.h
new file mode 100644
index 0000000..b892cdc
--- /dev/null
+++ b/include/linux/netfilter/xt_mac.h
@@ -0,0 +1,8 @@
+#ifndef _XT_MAC_H
+#define _XT_MAC_H
+
+struct xt_mac_info {
+    unsigned char srcaddr[ETH_ALEN];
+    int invert;
+};
+#endif /*_XT_MAC_H*/
diff --git a/include/linux/netfilter/xt_mark.h b/include/linux/netfilter/xt_mark.h
new file mode 100644
index 0000000..ecadc40
--- /dev/null
+++ b/include/linux/netfilter/xt_mark.h
@@ -0,0 +1,15 @@
+#ifndef _XT_MARK_H
+#define _XT_MARK_H
+
+#include <linux/types.h>
+
+struct xt_mark_tginfo2 {
+	__u32 mark, mask;
+};
+
+struct xt_mark_mtinfo1 {
+	__u32 mark, mask;
+	__u8 invert;
+};
+
+#endif /*_XT_MARK_H*/
diff --git a/include/linux/netfilter/xt_multiport.h b/include/linux/netfilter/xt_multiport.h
new file mode 100644
index 0000000..5b7e72d
--- /dev/null
+++ b/include/linux/netfilter/xt_multiport.h
@@ -0,0 +1,29 @@
+#ifndef _XT_MULTIPORT_H
+#define _XT_MULTIPORT_H
+
+#include <linux/types.h>
+
+enum xt_multiport_flags {
+	XT_MULTIPORT_SOURCE,
+	XT_MULTIPORT_DESTINATION,
+	XT_MULTIPORT_EITHER
+};
+
+#define XT_MULTI_PORTS	15
+
+/* Must fit inside union xt_matchinfo: 16 bytes */
+struct xt_multiport {
+	__u8 flags;				/* Type of comparison */
+	__u8 count;				/* Number of ports */
+	__u16 ports[XT_MULTI_PORTS];	/* Ports */
+};
+
+struct xt_multiport_v1 {
+	__u8 flags;				/* Type of comparison */
+	__u8 count;				/* Number of ports */
+	__u16 ports[XT_MULTI_PORTS];	/* Ports */
+	__u8 pflags[XT_MULTI_PORTS];	/* Port flags */
+	__u8 invert;			/* Invert flag */
+};
+
+#endif /*_XT_MULTIPORT_H*/
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_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..18afa49
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XT_OSF_H
+#define _XT_OSF_H
+
+#include <linux/types.h>
+
+#define MAXGENRELEN		32
+
+#define XT_OSF_GENRE		(1<<0)
+#define	XT_OSF_TTL		(1<<1)
+#define XT_OSF_LOG		(1<<2)
+#define XT_OSF_INVERT		(1<<3)
+
+#define XT_OSF_LOGLEVEL_ALL	0	/* log all matched fingerprints */
+#define XT_OSF_LOGLEVEL_FIRST	1	/* log only the first matced fingerprint */
+#define XT_OSF_LOGLEVEL_ALL_KNOWN	2 /* do not log unknown packets */
+
+#define XT_OSF_TTL_TRUE		0	/* True ip and fingerprint TTL comparison */
+#define XT_OSF_TTL_LESS		1	/* Check if ip TTL is less than fingerprint one */
+#define XT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
+
+struct xt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct xt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct xt_osf_opt {
+	__u16			kind, length;
+	struct xt_osf_wc	wc;
+};
+
+struct xt_osf_user_finger {
+	struct xt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct xt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct xt_osf_nlmsg {
+	struct xt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+/*
+ * Initial window size option state machine: multiple of mss, mtu or
+ * plain numeric value. Can also be made as plain numeric value which
+ * is not a multiple of specified value.
+ */
+enum xt_osf_window_size_options {
+	OSF_WSS_PLAIN	= 0,
+	OSF_WSS_MSS,
+	OSF_WSS_MTU,
+	OSF_WSS_MODULO,
+	OSF_WSS_MAX,
+};
+
+/*
+ * Add/remove fingerprint from the kernel.
+ */
+enum xt_osf_msg_types {
+	OSF_MSG_ADD,
+	OSF_MSG_REMOVE,
+	OSF_MSG_MAX,
+};
+
+enum xt_osf_attr_type {
+	OSF_ATTR_UNSPEC,
+	OSF_ATTR_FINGER,
+	OSF_ATTR_MAX,
+};
+
+#endif				/* _XT_OSF_H */
diff --git a/include/linux/netfilter/xt_owner.h b/include/linux/netfilter/xt_owner.h
new file mode 100644
index 0000000..2081761
--- /dev/null
+++ b/include/linux/netfilter/xt_owner.h
@@ -0,0 +1,18 @@
+#ifndef _XT_OWNER_MATCH_H
+#define _XT_OWNER_MATCH_H
+
+#include <linux/types.h>
+
+enum {
+	XT_OWNER_UID    = 1 << 0,
+	XT_OWNER_GID    = 1 << 1,
+	XT_OWNER_SOCKET = 1 << 2,
+};
+
+struct xt_owner_match_info {
+	__u32 uid_min, uid_max;
+	__u32 gid_min, gid_max;
+	__u8 match, invert;
+};
+
+#endif /* _XT_OWNER_MATCH_H */
diff --git a/include/linux/netfilter/xt_physdev.h b/include/linux/netfilter/xt_physdev.h
new file mode 100644
index 0000000..7d53660
--- /dev/null
+++ b/include/linux/netfilter/xt_physdev.h
@@ -0,0 +1,23 @@
+#ifndef _XT_PHYSDEV_H
+#define _XT_PHYSDEV_H
+
+#include <linux/types.h>
+
+
+#define XT_PHYSDEV_OP_IN		0x01
+#define XT_PHYSDEV_OP_OUT		0x02
+#define XT_PHYSDEV_OP_BRIDGED		0x04
+#define XT_PHYSDEV_OP_ISIN		0x08
+#define XT_PHYSDEV_OP_ISOUT		0x10
+#define XT_PHYSDEV_OP_MASK		(0x20 - 1)
+
+struct xt_physdev_info {
+	char physindev[IFNAMSIZ];
+	char in_mask[IFNAMSIZ];
+	char physoutdev[IFNAMSIZ];
+	char out_mask[IFNAMSIZ];
+	__u8 invert;
+	__u8 bitmask;
+};
+
+#endif /*_XT_PHYSDEV_H*/
diff --git a/include/linux/netfilter/xt_pkttype.h b/include/linux/netfilter/xt_pkttype.h
new file mode 100644
index 0000000..f265cf5
--- /dev/null
+++ b/include/linux/netfilter/xt_pkttype.h
@@ -0,0 +1,8 @@
+#ifndef _XT_PKTTYPE_H
+#define _XT_PKTTYPE_H
+
+struct xt_pkttype_info {
+	int	pkttype;
+	int	invert;
+};
+#endif /*_XT_PKTTYPE_H*/
diff --git a/include/linux/netfilter/xt_policy.h b/include/linux/netfilter/xt_policy.h
new file mode 100644
index 0000000..d246eac
--- /dev/null
+++ b/include/linux/netfilter/xt_policy.h
@@ -0,0 +1,58 @@
+#ifndef _XT_POLICY_H
+#define _XT_POLICY_H
+
+#include <linux/types.h>
+
+#define XT_POLICY_MAX_ELEM	4
+
+enum xt_policy_flags {
+	XT_POLICY_MATCH_IN	= 0x1,
+	XT_POLICY_MATCH_OUT	= 0x2,
+	XT_POLICY_MATCH_NONE	= 0x4,
+	XT_POLICY_MATCH_STRICT	= 0x8,
+};
+
+enum xt_policy_modes {
+	XT_POLICY_MODE_TRANSPORT,
+	XT_POLICY_MODE_TUNNEL
+};
+
+struct xt_policy_spec {
+	__u8	saddr:1,
+			daddr:1,
+			proto:1,
+			mode:1,
+			spi:1,
+			reqid:1;
+};
+
+union xt_policy_addr {
+	struct in_addr	a4;
+	struct in6_addr	a6;
+};
+
+struct xt_policy_elem {
+	union {
+		struct {
+			union xt_policy_addr saddr;
+			union xt_policy_addr smask;
+			union xt_policy_addr daddr;
+			union xt_policy_addr dmask;
+		};
+	};
+	__be32			spi;
+	__u32		reqid;
+	__u8		proto;
+	__u8		mode;
+
+	struct xt_policy_spec	match;
+	struct xt_policy_spec	invert;
+};
+
+struct xt_policy_info {
+	struct xt_policy_elem pol[XT_POLICY_MAX_ELEM];
+	__u16 flags;
+	__u16 len;
+};
+
+#endif /* _XT_POLICY_H */
diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h
new file mode 100644
index 0000000..9314723
--- /dev/null
+++ b/include/linux/netfilter/xt_quota.h
@@ -0,0 +1,22 @@
+#ifndef _XT_QUOTA_H
+#define _XT_QUOTA_H
+
+#include <linux/types.h>
+
+enum xt_quota_flags {
+	XT_QUOTA_INVERT		= 0x1,
+};
+#define XT_QUOTA_MASK		0x1
+
+struct xt_quota_priv;
+
+struct xt_quota_info {
+	__u32 flags;
+	__u32 pad;
+	__aligned_u64 quota;
+
+	/* Used internally by the kernel */
+	struct xt_quota_priv	*master;
+};
+
+#endif /* _XT_QUOTA_H */
diff --git a/include/linux/netfilter/xt_rateest.h b/include/linux/netfilter/xt_rateest.h
new file mode 100644
index 0000000..d40a619
--- /dev/null
+++ b/include/linux/netfilter/xt_rateest.h
@@ -0,0 +1,37 @@
+#ifndef _XT_RATEEST_MATCH_H
+#define _XT_RATEEST_MATCH_H
+
+#include <linux/types.h>
+
+enum xt_rateest_match_flags {
+	XT_RATEEST_MATCH_INVERT	= 1<<0,
+	XT_RATEEST_MATCH_ABS	= 1<<1,
+	XT_RATEEST_MATCH_REL	= 1<<2,
+	XT_RATEEST_MATCH_DELTA	= 1<<3,
+	XT_RATEEST_MATCH_BPS	= 1<<4,
+	XT_RATEEST_MATCH_PPS	= 1<<5,
+};
+
+enum xt_rateest_match_mode {
+	XT_RATEEST_MATCH_NONE,
+	XT_RATEEST_MATCH_EQ,
+	XT_RATEEST_MATCH_LT,
+	XT_RATEEST_MATCH_GT,
+};
+
+struct xt_rateest_match_info {
+	char			name1[IFNAMSIZ];
+	char			name2[IFNAMSIZ];
+	__u16		flags;
+	__u16		mode;
+	__u32		bps1;
+	__u32		pps1;
+	__u32		bps2;
+	__u32		pps2;
+
+	/* Used internally by the kernel */
+	struct xt_rateest	*est1 __attribute__((aligned(8)));
+	struct xt_rateest	*est2 __attribute__((aligned(8)));
+};
+
+#endif /* _XT_RATEEST_MATCH_H */
diff --git a/include/linux/netfilter/xt_realm.h b/include/linux/netfilter/xt_realm.h
new file mode 100644
index 0000000..d4a82ee
--- /dev/null
+++ b/include/linux/netfilter/xt_realm.h
@@ -0,0 +1,12 @@
+#ifndef _XT_REALM_H
+#define _XT_REALM_H
+
+#include <linux/types.h>
+
+struct xt_realm_info {
+	__u32 id;
+	__u32 mask;
+	__u8 invert;
+};
+
+#endif /* _XT_REALM_H */
diff --git a/include/linux/netfilter/xt_recent.h b/include/linux/netfilter/xt_recent.h
new file mode 100644
index 0000000..6ef36c1
--- /dev/null
+++ b/include/linux/netfilter/xt_recent.h
@@ -0,0 +1,45 @@
+#ifndef _LINUX_NETFILTER_XT_RECENT_H
+#define _LINUX_NETFILTER_XT_RECENT_H 1
+
+#include <linux/types.h>
+
+enum {
+	XT_RECENT_CHECK    = 1 << 0,
+	XT_RECENT_SET      = 1 << 1,
+	XT_RECENT_UPDATE   = 1 << 2,
+	XT_RECENT_REMOVE   = 1 << 3,
+	XT_RECENT_TTL      = 1 << 4,
+	XT_RECENT_REAP     = 1 << 5,
+
+	XT_RECENT_SOURCE   = 0,
+	XT_RECENT_DEST     = 1,
+
+	XT_RECENT_NAME_LEN = 200,
+};
+
+/* Only allowed with --rcheck and --update */
+#define XT_RECENT_MODIFIERS (XT_RECENT_TTL|XT_RECENT_REAP)
+
+#define XT_RECENT_VALID_FLAGS (XT_RECENT_CHECK|XT_RECENT_SET|XT_RECENT_UPDATE|\
+			       XT_RECENT_REMOVE|XT_RECENT_TTL|XT_RECENT_REAP)
+
+struct xt_recent_mtinfo {
+	__u32 seconds;
+	__u32 hit_count;
+	__u8 check_set;
+	__u8 invert;
+	char name[XT_RECENT_NAME_LEN];
+	__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
new file mode 100644
index 0000000..a501e61
--- /dev/null
+++ b/include/linux/netfilter/xt_sctp.h
@@ -0,0 +1,92 @@
+#ifndef _XT_SCTP_H_
+#define _XT_SCTP_H_
+
+#include <linux/types.h>
+
+#define XT_SCTP_SRC_PORTS	        0x01
+#define XT_SCTP_DEST_PORTS	        0x02
+#define XT_SCTP_CHUNK_TYPES		0x04
+
+#define XT_SCTP_VALID_FLAGS		0x07
+
+struct xt_sctp_flag_info {
+	__u8 chunktype;
+	__u8 flag;
+	__u8 flag_mask;
+};
+
+#define XT_NUM_SCTP_FLAGS	4
+
+struct xt_sctp_info {
+	__u16 dpts[2];  /* Min, Max */
+	__u16 spts[2];  /* Min, Max */
+
+	__u32 chunkmap[256 / sizeof (__u32)];  /* Bit mask of chunks to be matched according to RFC 2960 */
+
+#define SCTP_CHUNK_MATCH_ANY   0x01  /* Match if any of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ALL   0x02  /* Match if all of the chunk types are present */
+#define SCTP_CHUNK_MATCH_ONLY  0x04  /* Match if these are the only chunk types present */
+
+	__u32 chunk_match_type;
+	struct xt_sctp_flag_info flag_info[XT_NUM_SCTP_FLAGS];
+	int flag_count;
+
+	__u32 flags;
+	__u32 invflags;
+};
+
+#define bytes(type) (sizeof(type) * 8)
+
+#define SCTP_CHUNKMAP_SET(chunkmap, type) 		\
+	do { 						\
+		(chunkmap)[type / bytes(__u32)] |= 	\
+			1 << (type % bytes(__u32));	\
+	} while (0)
+
+#define SCTP_CHUNKMAP_CLEAR(chunkmap, type)		 	\
+	do {							\
+		(chunkmap)[type / bytes(__u32)] &= 		\
+			~(1 << (type % bytes(__u32)));	\
+	} while (0)
+
+#define SCTP_CHUNKMAP_IS_SET(chunkmap, type) 			\
+({								\
+	((chunkmap)[type / bytes (__u32)] & 		\
+		(1 << (type % bytes (__u32)))) ? 1: 0;	\
+})
+
+#define SCTP_CHUNKMAP_RESET(chunkmap) \
+	memset((chunkmap), 0, sizeof(chunkmap))
+
+#define SCTP_CHUNKMAP_SET_ALL(chunkmap) \
+	memset((chunkmap), ~0U, sizeof(chunkmap))
+
+#define SCTP_CHUNKMAP_COPY(destmap, srcmap) \
+	memcpy((destmap), (srcmap), sizeof(srcmap))
+
+#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \
+	__sctp_chunkmap_is_clear((chunkmap), ARRAY_SIZE(chunkmap))
+static __inline__ bool
+__sctp_chunkmap_is_clear(const __u32 *chunkmap, unsigned int n)
+{
+	unsigned int i;
+	for (i = 0; i < n; ++i)
+		if (chunkmap[i])
+			return false;
+	return true;
+}
+
+#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \
+	__sctp_chunkmap_is_all_set((chunkmap), ARRAY_SIZE(chunkmap))
+static __inline__ bool
+__sctp_chunkmap_is_all_set(const __u32 *chunkmap, unsigned int n)
+{
+	unsigned int i;
+	for (i = 0; i < n; ++i)
+		if (chunkmap[i] != ~0U)
+			return false;
+	return true;
+}
+
+#endif /* _XT_SCTP_H_ */
+
diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h
new file mode 100644
index 0000000..964d3d4
--- /dev/null
+++ b/include/linux/netfilter/xt_set.h
@@ -0,0 +1,74 @@
+#ifndef _XT_SET_H
+#define _XT_SET_H
+
+#include <linux/types.h>
+#include <linux/netfilter/ipset/ip_set.h>
+
+/* Revision 0 interface: backward compatible with netfilter/iptables */
+
+/*
+ * Option flags for kernel operations (xt_set_info_v0)
+ */
+#define IPSET_SRC		0x01	/* Source match/add */
+#define IPSET_DST		0x02	/* Destination match/add */
+#define IPSET_MATCH_INV		0x04	/* Inverse matching */
+
+struct xt_set_info_v0 {
+	ip_set_id_t index;
+	union {
+		__u32 flags[IPSET_DIM_MAX + 1];
+		struct {
+			__u32 __flags[IPSET_DIM_MAX];
+			__u8 dim;
+			__u8 flags;
+		} compat;
+	} u;
+};
+
+/* match and target infos */
+struct xt_set_info_match_v0 {
+	struct xt_set_info_v0 match_set;
+};
+
+struct xt_set_info_target_v0 {
+	struct xt_set_info_v0 add_set;
+	struct xt_set_info_v0 del_set;
+};
+
+/* Revision 1  match and target */
+
+struct xt_set_info {
+	ip_set_id_t index;
+	__u8 dim;
+	__u8 flags;
+};
+
+/* match and target infos */
+struct xt_set_info_match_v1 {
+	struct xt_set_info match_set;
+};
+
+struct xt_set_info_target_v1 {
+	struct xt_set_info add_set;
+	struct xt_set_info del_set;
+};
+
+/* Revision 2 target */
+
+struct xt_set_info_target_v2 {
+	struct xt_set_info add_set;
+	struct xt_set_info del_set;
+	__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
new file mode 100644
index 0000000..6315e2a
--- /dev/null
+++ b/include/linux/netfilter/xt_socket.h
@@ -0,0 +1,21 @@
+#ifndef _XT_SOCKET_H
+#define _XT_SOCKET_H
+
+#include <linux/types.h>
+
+enum {
+	XT_SOCKET_TRANSPARENT = 1 << 0,
+	XT_SOCKET_NOWILDCARD = 1 << 1,
+};
+
+struct xt_socket_mtinfo1 {
+	__u8 flags;
+};
+#define XT_SOCKET_FLAGS_V1 XT_SOCKET_TRANSPARENT
+
+struct xt_socket_mtinfo2 {
+	__u8 flags;
+};
+#define XT_SOCKET_FLAGS_V2 (XT_SOCKET_TRANSPARENT | XT_SOCKET_NOWILDCARD)
+
+#endif /* _XT_SOCKET_H */
diff --git a/include/linux/netfilter/xt_state.h b/include/linux/netfilter/xt_state.h
new file mode 100644
index 0000000..7b32de8
--- /dev/null
+++ b/include/linux/netfilter/xt_state.h
@@ -0,0 +1,12 @@
+#ifndef _XT_STATE_H
+#define _XT_STATE_H
+
+#define XT_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
+#define XT_STATE_INVALID (1 << 0)
+
+#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
+
+struct xt_state_info {
+	unsigned int statemask;
+};
+#endif /*_XT_STATE_H*/
diff --git a/include/linux/netfilter/xt_statistic.h b/include/linux/netfilter/xt_statistic.h
new file mode 100644
index 0000000..4e983ef
--- /dev/null
+++ b/include/linux/netfilter/xt_statistic.h
@@ -0,0 +1,36 @@
+#ifndef _XT_STATISTIC_H
+#define _XT_STATISTIC_H
+
+#include <linux/types.h>
+
+enum xt_statistic_mode {
+	XT_STATISTIC_MODE_RANDOM,
+	XT_STATISTIC_MODE_NTH,
+	__XT_STATISTIC_MODE_MAX
+};
+#define XT_STATISTIC_MODE_MAX (__XT_STATISTIC_MODE_MAX - 1)
+
+enum xt_statistic_flags {
+	XT_STATISTIC_INVERT		= 0x1,
+};
+#define XT_STATISTIC_MASK		0x1
+
+struct xt_statistic_priv;
+
+struct xt_statistic_info {
+	__u16			mode;
+	__u16			flags;
+	union {
+		struct {
+			__u32	probability;
+		} random;
+		struct {
+			__u32	every;
+			__u32	packet;
+			__u32	count; /* unused */
+		} nth;
+	} u;
+	struct xt_statistic_priv *master __attribute__((aligned(8)));
+};
+
+#endif /* _XT_STATISTIC_H */
diff --git a/include/linux/netfilter/xt_string.h b/include/linux/netfilter/xt_string.h
new file mode 100644
index 0000000..235347c
--- /dev/null
+++ b/include/linux/netfilter/xt_string.h
@@ -0,0 +1,34 @@
+#ifndef _XT_STRING_H
+#define _XT_STRING_H
+
+#include <linux/types.h>
+
+#define XT_STRING_MAX_PATTERN_SIZE 128
+#define XT_STRING_MAX_ALGO_NAME_SIZE 16
+
+enum {
+	XT_STRING_FLAG_INVERT		= 0x01,
+	XT_STRING_FLAG_IGNORECASE	= 0x02
+};
+
+struct xt_string_info {
+	__u16 from_offset;
+	__u16 to_offset;
+	char	  algo[XT_STRING_MAX_ALGO_NAME_SIZE];
+	char 	  pattern[XT_STRING_MAX_PATTERN_SIZE];
+	__u8  patlen;
+	union {
+		struct {
+			__u8  invert;
+		} v0;
+
+		struct {
+			__u8  flags;
+		} v1;
+	} u;
+
+	/* Used internally by the kernel */
+	struct ts_config __attribute__((aligned(8))) *config;
+};
+
+#endif /*_XT_STRING_H*/
diff --git a/include/linux/netfilter/xt_tcpmss.h b/include/linux/netfilter/xt_tcpmss.h
new file mode 100644
index 0000000..fbac56b
--- /dev/null
+++ b/include/linux/netfilter/xt_tcpmss.h
@@ -0,0 +1,11 @@
+#ifndef _XT_TCPMSS_MATCH_H
+#define _XT_TCPMSS_MATCH_H
+
+#include <linux/types.h>
+
+struct xt_tcpmss_match_info {
+    __u16 mss_min, mss_max;
+    __u8 invert;
+};
+
+#endif /*_XT_TCPMSS_MATCH_H*/
diff --git a/include/linux/netfilter/xt_tcpudp.h b/include/linux/netfilter/xt_tcpudp.h
new file mode 100644
index 0000000..38aa7b3
--- /dev/null
+++ b/include/linux/netfilter/xt_tcpudp.h
@@ -0,0 +1,36 @@
+#ifndef _XT_TCPUDP_H
+#define _XT_TCPUDP_H
+
+#include <linux/types.h>
+
+/* TCP matching stuff */
+struct xt_tcp {
+	__u16 spts[2];			/* Source port range. */
+	__u16 dpts[2];			/* Destination port range. */
+	__u8 option;			/* TCP Option iff non-zero*/
+	__u8 flg_mask;			/* TCP flags mask byte */
+	__u8 flg_cmp;			/* TCP flags compare byte */
+	__u8 invflags;			/* Inverse flags */
+};
+
+/* Values for "inv" field in struct ipt_tcp. */
+#define XT_TCP_INV_SRCPT	0x01	/* Invert the sense of source ports. */
+#define XT_TCP_INV_DSTPT	0x02	/* Invert the sense of dest ports. */
+#define XT_TCP_INV_FLAGS	0x04	/* Invert the sense of TCP flags. */
+#define XT_TCP_INV_OPTION	0x08	/* Invert the sense of option test. */
+#define XT_TCP_INV_MASK		0x0F	/* All possible flags. */
+
+/* UDP matching stuff */
+struct xt_udp {
+	__u16 spts[2];			/* Source port range. */
+	__u16 dpts[2];			/* Destination port range. */
+	__u8 invflags;			/* Inverse flags */
+};
+
+/* Values for "invflags" field in struct ipt_udp. */
+#define XT_UDP_INV_SRCPT	0x01	/* Invert the sense of source ports. */
+#define XT_UDP_INV_DSTPT	0x02	/* Invert the sense of dest ports. */
+#define XT_UDP_INV_MASK	0x03	/* All possible flags. */
+
+
+#endif
diff --git a/include/linux/netfilter/xt_time.h b/include/linux/netfilter/xt_time.h
new file mode 100644
index 0000000..a21d5bf
--- /dev/null
+++ b/include/linux/netfilter/xt_time.h
@@ -0,0 +1,28 @@
+#ifndef _XT_TIME_H
+#define _XT_TIME_H 1
+
+#include <linux/types.h>
+
+struct xt_time_info {
+	__u32 date_start;
+	__u32 date_stop;
+	__u32 daytime_start;
+	__u32 daytime_stop;
+	__u32 monthdays_match;
+	__u8 weekdays_match;
+	__u8 flags;
+};
+
+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,
+	XT_TIME_ALL_WEEKDAYS  = 0xFE,
+	XT_TIME_MIN_DAYTIME   = 0,
+	XT_TIME_MAX_DAYTIME   = 24 * 60 * 60 - 1,
+};
+
+#endif /* _XT_TIME_H */
diff --git a/include/linux/netfilter/xt_u32.h b/include/linux/netfilter/xt_u32.h
new file mode 100644
index 0000000..04d1bfe
--- /dev/null
+++ b/include/linux/netfilter/xt_u32.h
@@ -0,0 +1,42 @@
+#ifndef _XT_U32_H
+#define _XT_U32_H 1
+
+#include <linux/types.h>
+
+enum xt_u32_ops {
+	XT_U32_AND,
+	XT_U32_LEFTSH,
+	XT_U32_RIGHTSH,
+	XT_U32_AT,
+};
+
+struct xt_u32_location_element {
+	__u32 number;
+	__u8 nextop;
+};
+
+struct xt_u32_value_element {
+	__u32 min;
+	__u32 max;
+};
+
+/*
+ * Any way to allow for an arbitrary number of elements?
+ * For now, I settle with a limit of 10 each.
+ */
+#define XT_U32_MAXSIZE 10
+
+struct xt_u32_test {
+	struct xt_u32_location_element location[XT_U32_MAXSIZE+1];
+	struct xt_u32_value_element value[XT_U32_MAXSIZE+1];
+	__u8 nnums;
+	__u8 nvalues;
+};
+
+struct xt_u32 {
+	struct xt_u32_test tests[XT_U32_MAXSIZE+1];
+	__u8 ntests;
+	__u8 invert;
+};
+
+#endif /* _XT_U32_H */
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
new file mode 100644
index 0000000..4d7ba3e
--- /dev/null
+++ b/include/linux/netfilter_ipv4.h
@@ -0,0 +1,75 @@
+#ifndef __LINUX_IP_NETFILTER_H
+#define __LINUX_IP_NETFILTER_H
+
+/* IPv4-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* only for userspace compatibility */
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP_SRC		0x0001
+/* Dest IP address. */
+#define NFC_IP_DST		0x0002
+/* Input device. */
+#define NFC_IP_IF_IN		0x0004
+/* Output device. */
+#define NFC_IP_IF_OUT		0x0008
+/* TOS. */
+#define NFC_IP_TOS		0x0010
+/* Protocol. */
+#define NFC_IP_PROTO		0x0020
+/* IP options. */
+#define NFC_IP_OPTIONS		0x0040
+/* Frag & flags. */
+#define NFC_IP_FRAG		0x0080
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP_TCPFLAGS		0x0100
+/* Source port. */
+#define NFC_IP_SRC_PT		0x0200
+/* Dest port. */
+#define NFC_IP_DST_PT		0x0400
+/* Something else about the proto */
+#define NFC_IP_PROTO_UNKNOWN	0x2000
+
+/* IP Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_IP_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_IP_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_IP_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_IP_POST_ROUTING	4
+#define NF_IP_NUMHOOKS		5
+
+enum nf_ip_hook_priorities {
+	NF_IP_PRI_FIRST = INT_MIN,
+	NF_IP_PRI_CONNTRACK_DEFRAG = -400,
+	NF_IP_PRI_RAW = -300,
+	NF_IP_PRI_SELINUX_FIRST = -225,
+	NF_IP_PRI_CONNTRACK = -200,
+	NF_IP_PRI_MANGLE = -150,
+	NF_IP_PRI_NAT_DST = -100,
+	NF_IP_PRI_FILTER = 0,
+	NF_IP_PRI_SECURITY = 50,
+	NF_IP_PRI_NAT_SRC = 100,
+	NF_IP_PRI_SELINUX_LAST = 225,
+	NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
+	NF_IP_PRI_LAST = INT_MAX,
+};
+
+/* Arguments for setsockopt SOL_IP: */
+/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
+/* 2.2 firewalling (+ masq) went from 64 through 76 */
+/* 2.4 firewalling went 64 through 67. */
+#define SO_ORIGINAL_DST 80
+
+
+#endif /*__LINUX_IP_NETFILTER_H*/
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
new file mode 100644
index 0000000..57fd82a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -0,0 +1,227 @@
+/*
+ * 25-Jul-1998 Major changes to allow for ip chain table
+ *
+ * 3-Jan-2000 Named tables to allow packet selection for different uses.
+ */
+
+/*
+ * 	Format of an IP firewall descriptor
+ *
+ * 	src, dst, src_mask, dst_mask are always stored in network byte order.
+ * 	flags are stored in host byte order (of course).
+ * 	Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IPTABLES_H
+#define _IPTABLES_H
+
+#include <linux/types.h>
+
+#include <linux/netfilter_ipv4.h>
+
+#include <linux/netfilter/x_tables.h>
+
+#define IPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
+#define IPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
+#define ipt_match xt_match
+#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 {
+	/* Source and destination IP addr */
+	struct in_addr src, dst;
+	/* Mask for src and dest IP addr */
+	struct in_addr smsk, dmsk;
+	char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+	/* Protocol, 0 = ANY */
+	u_int16_t proto;
+
+	/* Flags word */
+	u_int8_t flags;
+	/* Inverse flags */
+	u_int8_t invflags;
+};
+
+/* 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 */
+#define IPT_F_MASK		0x03	/* All possible flag bits mask. */
+
+/* Values for "inv" field in struct ipt_ip. */
+#define IPT_INV_VIA_IN		0x01	/* Invert the sense of IN IFACE. */
+#define IPT_INV_VIA_OUT		0x02	/* Invert the sense of OUT IFACE */
+#define IPT_INV_TOS		0x04	/* Invert the sense of TOS. */
+#define IPT_INV_SRCIP		0x08	/* Invert the sense of SRC IP. */
+#define IPT_INV_DSTIP		0x10	/* Invert the sense of DST OP. */
+#define IPT_INV_FRAG		0x20	/* Invert the sense of FRAG. */
+#define IPT_INV_PROTO		XT_INV_PROTO
+#define IPT_INV_MASK		0x7F	/* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules.  Consists of 3
+   parts which are 1) general IP header stuff 2) match specific
+   stuff 3) the target to perform if the rule matches */
+struct ipt_entry {
+	struct ipt_ip ip;
+
+	/* Mark with fields that we care about. */
+	unsigned int nfcache;
+
+	/* Size of ipt_entry + matches */
+	u_int16_t target_offset;
+	/* Size of ipt_entry + matches + target */
+	u_int16_t next_offset;
+
+	/* Back pointer */
+	unsigned int comefrom;
+
+	/* Packet and byte counters. */
+	struct xt_counters counters;
+
+	/* The matches (if any), then the target. */
+	unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls.
+ *
+ * ATTENTION: check linux/in.h before adding new number here.
+ */
+#define IPT_BASE_CTL		64
+
+#define IPT_SO_SET_REPLACE	(IPT_BASE_CTL)
+#define IPT_SO_SET_ADD_COUNTERS	(IPT_BASE_CTL + 1)
+#define IPT_SO_SET_MAX		IPT_SO_SET_ADD_COUNTERS
+
+#define IPT_SO_GET_INFO			(IPT_BASE_CTL)
+#define IPT_SO_GET_ENTRIES		(IPT_BASE_CTL + 1)
+#define IPT_SO_GET_REVISION_MATCH	(IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
+#define IPT_SO_GET_MAX			IPT_SO_GET_REVISION_TARGET
+
+/* ICMP matching stuff */
+struct ipt_icmp {
+	u_int8_t type;				/* type to match */
+	u_int8_t code[2];			/* range of code */
+	u_int8_t invflags;			/* Inverse flags */
+};
+
+/* Values for "inv" field for struct ipt_icmp. */
+#define IPT_ICMP_INV	0x01	/* Invert the sense of type/code test */
+
+/* The argument to IPT_SO_GET_INFO */
+struct ipt_getinfo {
+	/* Which table: caller fills this in. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* Kernel fills these in. */
+	/* Which hook entry points are valid: bitmask */
+	unsigned int valid_hooks;
+
+	/* Hook entry points: one per netfilter hook. */
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+	/* Underflow points. */
+	unsigned int underflow[NF_INET_NUMHOOKS];
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Size of entries. */
+	unsigned int size;
+};
+
+/* The argument to IPT_SO_SET_REPLACE. */
+struct ipt_replace {
+	/* Which table. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* Which hook entry points are valid: bitmask.  You can't
+           change this. */
+	unsigned int valid_hooks;
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Total size of new entries */
+	unsigned int size;
+
+	/* Hook entry points. */
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+	/* Underflow points. */
+	unsigned int underflow[NF_INET_NUMHOOKS];
+
+	/* Information about old entries: */
+	/* Number of counters (must be equal to current number of entries). */
+	unsigned int num_counters;
+	/* The old entries' counters. */
+	struct xt_counters *counters;
+
+	/* The entries (hang off end: not really an array). */
+	struct ipt_entry entries[0];
+};
+
+/* The argument to IPT_SO_GET_ENTRIES. */
+struct ipt_get_entries {
+	/* Which table: user fills this in. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* User fills this in: total entry size. */
+	unsigned int size;
+
+	/* The entries. */
+	struct ipt_entry entrytable[0];
+};
+
+/* Helper functions */
+static __inline__ struct xt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+	return (void *)e + e->target_offset;
+}
+
+/*
+ *	Main firewall chains definitions and global var's definitions.
+ */
+#endif /* _IPTABLES_H */
diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
new file mode 100644
index 0000000..c6a204c
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
@@ -0,0 +1,36 @@
+#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,
+    CLUSTERIP_HASHMODE_SIP_SPT_DPT,
+};
+
+#define CLUSTERIP_HASHMODE_MAX CLUSTERIP_HASHMODE_SIP_SPT_DPT
+
+#define CLUSTERIP_MAX_NODES 16
+
+#define CLUSTERIP_FLAG_NEW 0x00000001
+
+struct clusterip_config;
+
+struct ipt_clusterip_tgt_info {
+
+	__u32 flags;
+
+	/* only relevant for new ones */
+	__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;
+};
+
+#endif /*_IPT_CLUSTERIP_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h
new file mode 100644
index 0000000..bb88d53
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_ECN.h
@@ -0,0 +1,33 @@
+/* Header file for iptables ipt_ECN target
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp
+*/
+#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)
+
+#define IPT_ECN_OP_SET_IP	0x01	/* set ECN bits of IPv4 header */
+#define IPT_ECN_OP_SET_ECE	0x10	/* set ECE bit of TCP header */
+#define IPT_ECN_OP_SET_CWR	0x20	/* set CWR bit of TCP header */
+
+#define IPT_ECN_OP_MASK		0xce
+
+struct ipt_ECN_info {
+	__u8 operation;	/* bitset of operations */
+	__u8 ip_ect;	/* ECT codepoint of IPv4 header, pre-shifted */
+	union {
+		struct {
+			__u8 ece:1, cwr:1; /* TCP ECT bits */
+		} tcp;
+	} proto;
+};
+
+#endif /* _IPT_ECN_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_LOG.h b/include/linux/netfilter_ipv4/ipt_LOG.h
new file mode 100644
index 0000000..dcdbadf
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_LOG.h
@@ -0,0 +1,19 @@
+#ifndef _IPT_LOG_H
+#define _IPT_LOG_H
+
+/* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */
+#define IPT_LOG_TCPSEQ		0x01	/* Log TCP sequence numbers */
+#define IPT_LOG_TCPOPT		0x02	/* Log TCP options */
+#define IPT_LOG_IPOPT		0x04	/* Log IP options */
+#define IPT_LOG_UID		0x08	/* Log UID owning local socket */
+#define IPT_LOG_NFLOG		0x10	/* Unsupported, don't reuse */
+#define IPT_LOG_MACDECODE	0x20	/* Decode MAC header */
+#define IPT_LOG_MASK		0x2f
+
+struct ipt_log_info {
+	unsigned char level;
+	unsigned char logflags;
+	char prefix[30];
+};
+
+#endif /*_IPT_LOG_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_REJECT.h b/include/linux/netfilter_ipv4/ipt_REJECT.h
new file mode 100644
index 0000000..4293a1a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_REJECT.h
@@ -0,0 +1,20 @@
+#ifndef _IPT_REJECT_H
+#define _IPT_REJECT_H
+
+enum ipt_reject_with {
+	IPT_ICMP_NET_UNREACHABLE,
+	IPT_ICMP_HOST_UNREACHABLE,
+	IPT_ICMP_PROT_UNREACHABLE,
+	IPT_ICMP_PORT_UNREACHABLE,
+	IPT_ICMP_ECHOREPLY,
+	IPT_ICMP_NET_PROHIBITED,
+	IPT_ICMP_HOST_PROHIBITED,
+	IPT_TCP_RESET,
+	IPT_ICMP_ADMIN_PROHIBITED
+};
+
+struct ipt_reject_info {
+	enum ipt_reject_with with;      /* reject type */
+};
+
+#endif /*_IPT_REJECT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
new file mode 100644
index 0000000..a855167
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_SAME.h
@@ -0,0 +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;
+	__u32 rangesize;
+	__u32 ipnum;
+	__u32 *iparray;
+
+	/* hangs off end. */
+	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
new file mode 100644
index 0000000..f6ac169
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_TTL.h
@@ -0,0 +1,23 @@
+/* TTL modification module for IP tables
+ * (C) 2000 by Harald Welte <laforge@netfilter.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+#include <linux/types.h>
+
+enum {
+	IPT_TTL_SET = 0,
+	IPT_TTL_INC,
+	IPT_TTL_DEC
+};
+
+#define IPT_TTL_MAXMODE	IPT_TTL_DEC
+
+struct ipt_TTL_info {
+	__u8	mode;
+	__u8	ttl;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_ULOG.h b/include/linux/netfilter_ipv4/ipt_ULOG.h
new file mode 100644
index 0000000..417aad2
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_ULOG.h
@@ -0,0 +1,49 @@
+/* Header file for IP tables userspace logging, Version 1.8
+ *
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * 
+ * Distributed under the terms of GNU GPL */
+
+#ifndef _IPT_ULOG_H
+#define _IPT_ULOG_H
+
+#ifndef NETLINK_NFLOG
+#define NETLINK_NFLOG 	5
+#endif
+
+#define ULOG_DEFAULT_NLGROUP	1
+#define ULOG_DEFAULT_QTHRESHOLD	1
+
+#define ULOG_MAC_LEN	80
+#define ULOG_PREFIX_LEN	32
+
+#define ULOG_MAX_QLEN	50
+/* Why 50? Well... there is a limit imposed by the slab cache 131000
+ * bytes. So the multipart netlink-message has to be < 131000 bytes.
+ * Assuming a standard ethernet-mtu of 1500, we could define this up
+ * to 80... but even 50 seems to be big enough. */
+
+/* private data structure for each rule with a ULOG target */
+struct ipt_ulog_info {
+	unsigned int nl_group;
+	size_t copy_range;
+	size_t qthreshold;
+	char prefix[ULOG_PREFIX_LEN];
+};
+
+/* Format of the ULOG packets passed through netlink */
+typedef struct ulog_packet_msg {
+	unsigned long mark;
+	long timestamp_sec;
+	long timestamp_usec;
+	unsigned int hook;
+	char indev_name[IFNAMSIZ];
+	char outdev_name[IFNAMSIZ];
+	size_t data_len;
+	char prefix[ULOG_PREFIX_LEN];
+	unsigned char mac_len;
+	unsigned char mac[ULOG_MAC_LEN];
+	unsigned char payload[0];
+} ulog_packet_msg_t;
+
+#endif /*_IPT_ULOG_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_addrtype.h b/include/linux/netfilter_ipv4/ipt_addrtype.h
new file mode 100644
index 0000000..0da4223
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_addrtype.h
@@ -0,0 +1,27 @@
+#ifndef _IPT_ADDRTYPE_H
+#define _IPT_ADDRTYPE_H
+
+#include <linux/types.h>
+
+enum {
+	IPT_ADDRTYPE_INVERT_SOURCE	= 0x0001,
+	IPT_ADDRTYPE_INVERT_DEST	= 0x0002,
+	IPT_ADDRTYPE_LIMIT_IFACE_IN	= 0x0004,
+	IPT_ADDRTYPE_LIMIT_IFACE_OUT	= 0x0008,
+};
+
+struct ipt_addrtype_info_v1 {
+	__u16	source;		/* source-type mask */
+	__u16	dest;		/* dest-type mask */
+	__u32	flags;
+};
+
+/* revision 0 */
+struct ipt_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_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h
new file mode 100644
index 0000000..4e02bb0
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_ah.h
@@ -0,0 +1,17 @@
+#ifndef _IPT_AH_H
+#define _IPT_AH_H
+
+#include <linux/types.h>
+
+struct ipt_ah {
+	__u32 spis[2];			/* Security Parameter Index */
+	__u8  invflags;			/* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_ah. */
+#define IPT_AH_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IPT_AH_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IPT_AH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_realm.h b/include/linux/netfilter_ipv4/ipt_realm.h
new file mode 100644
index 0000000..b3996ea
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_realm.h
@@ -0,0 +1,7 @@
+#ifndef _IPT_REALM_H
+#define _IPT_REALM_H
+
+#include <linux/netfilter/xt_realm.h>
+#define ipt_realm_info xt_realm_info
+
+#endif /* _IPT_REALM_H */
diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h
new file mode 100644
index 0000000..37bee44
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_ttl.h
@@ -0,0 +1,23 @@
+/* IP tables module for matching the value of the TTL
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+#include <linux/types.h>
+
+enum {
+	IPT_TTL_EQ = 0,		/* equals */
+	IPT_TTL_NE,		/* not equals */
+	IPT_TTL_LT,		/* less than */
+	IPT_TTL_GT,		/* greater than */
+};
+
+
+struct ipt_ttl_info {
+	__u8	mode;
+	__u8	ttl;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h
new file mode 100644
index 0000000..f155b9d
--- /dev/null
+++ b/include/linux/netfilter_ipv6.h
@@ -0,0 +1,73 @@
+#ifndef __LINUX_IP6_NETFILTER_H
+#define __LINUX_IP6_NETFILTER_H
+
+/* IPv6-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ * (C)1999 David Jeffery
+ *   this header was blatantly ripped from netfilter_ipv4.h 
+ *   it's amazing what adding a bunch of 6s can do =8^)
+ */
+
+#include <linux/netfilter.h>
+
+/* only for userspace compatibility */
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP6_SRC              0x0001
+/* Dest IP address. */
+#define NFC_IP6_DST              0x0002
+/* Input device. */
+#define NFC_IP6_IF_IN            0x0004
+/* Output device. */
+#define NFC_IP6_IF_OUT           0x0008
+/* TOS. */
+#define NFC_IP6_TOS              0x0010
+/* Protocol. */
+#define NFC_IP6_PROTO            0x0020
+/* IP options. */
+#define NFC_IP6_OPTIONS          0x0040
+/* Frag & flags. */
+#define NFC_IP6_FRAG             0x0080
+
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP6_TCPFLAGS         0x0100
+/* Source port. */
+#define NFC_IP6_SRC_PT           0x0200
+/* Dest port. */
+#define NFC_IP6_DST_PT           0x0400
+/* Something else about the proto */
+#define NFC_IP6_PROTO_UNKNOWN    0x2000
+
+/* IP6 Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP6_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_IP6_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_IP6_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_IP6_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_IP6_POST_ROUTING	4
+#define NF_IP6_NUMHOOKS		5
+
+
+enum nf_ip6_hook_priorities {
+	NF_IP6_PRI_FIRST = INT_MIN,
+	NF_IP6_PRI_CONNTRACK_DEFRAG = -400,
+	NF_IP6_PRI_RAW = -300,
+	NF_IP6_PRI_SELINUX_FIRST = -225,
+	NF_IP6_PRI_CONNTRACK = -200,
+	NF_IP6_PRI_MANGLE = -150,
+	NF_IP6_PRI_NAT_DST = -100,
+	NF_IP6_PRI_FILTER = 0,
+	NF_IP6_PRI_SECURITY = 50,
+	NF_IP6_PRI_NAT_SRC = 100,
+	NF_IP6_PRI_SELINUX_LAST = 225,
+	NF_IP6_PRI_LAST = INT_MAX,
+};
+
+
+#endif /*__LINUX_IP6_NETFILTER_H*/
diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h
new file mode 100644
index 0000000..3f19a97
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6_tables.h
@@ -0,0 +1,265 @@
+/*
+ * 25-Jul-1998 Major changes to allow for ip chain table
+ *
+ * 3-Jan-2000 Named tables to allow packet selection for different uses.
+ */
+
+/*
+ * 	Format of an IP6 firewall descriptor
+ *
+ * 	src, dst, src_mask, dst_mask are always stored in network byte order.
+ * 	flags are stored in host byte order (of course).
+ * 	Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IP6_TABLES_H
+#define _IP6_TABLES_H
+
+#include <linux/types.h>
+
+#include <linux/netfilter_ipv6.h>
+
+#include <linux/netfilter/x_tables.h>
+
+#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 {
+	/* Source and destination IP6 addr */
+	struct in6_addr src, dst;		
+	/* Mask for src and dest IP6 addr */
+	struct in6_addr smsk, dmsk;
+	char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+	unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+	/* Upper protocol number
+	 * - The allowed value is 0 (any) or protocol number of last parsable
+	 *   header, which is 50 (ESP), 59 (No Next Header), 135 (MH), or
+	 *   the non IPv6 extension headers.
+	 * - The protocol numbers of IPv6 extension headers except of ESP and
+	 *   MH do not match any packets.
+	 * - You also need to set IP6T_FLAGS_PROTO to "flags" to check protocol.
+	 */
+	u_int16_t proto;
+	/* TOS to match iff flags & IP6T_F_TOS */
+	u_int8_t tos;
+
+	/* Flags word */
+	u_int8_t flags;
+	/* Inverse flags */
+	u_int8_t invflags;
+};
+
+/* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */
+#define IP6T_F_PROTO		0x01	/* Set if rule cares about upper 
+					   protocols */
+#define IP6T_F_TOS		0x02	/* Match the TOS. */
+#define IP6T_F_GOTO		0x04	/* Set if jump is a goto */
+#define IP6T_F_MASK		0x07	/* All possible flag bits mask. */
+
+/* Values for "inv" field in struct ip6t_ip6. */
+#define IP6T_INV_VIA_IN		0x01	/* Invert the sense of IN IFACE. */
+#define IP6T_INV_VIA_OUT		0x02	/* Invert the sense of OUT IFACE */
+#define IP6T_INV_TOS		0x04	/* Invert the sense of TOS. */
+#define IP6T_INV_SRCIP		0x08	/* Invert the sense of SRC IP. */
+#define IP6T_INV_DSTIP		0x10	/* Invert the sense of DST OP. */
+#define IP6T_INV_FRAG		0x20	/* Invert the sense of FRAG. */
+#define IP6T_INV_PROTO		XT_INV_PROTO
+#define IP6T_INV_MASK		0x7F	/* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules.  Consists of 3
+   parts which are 1) general IP header stuff 2) match specific
+   stuff 3) the target to perform if the rule matches */
+struct ip6t_entry {
+	struct ip6t_ip6 ipv6;
+
+	/* Mark with fields that we care about. */
+	unsigned int nfcache;
+
+	/* Size of ipt_entry + matches */
+	u_int16_t target_offset;
+	/* Size of ipt_entry + matches + target */
+	u_int16_t next_offset;
+
+	/* Back pointer */
+	unsigned int comefrom;
+
+	/* Packet and byte counters. */
+	struct xt_counters counters;
+
+	/* The matches (if any), then the target. */
+	unsigned char elems[0];
+};
+
+/* Standard entry */
+struct ip6t_standard {
+	struct ip6t_entry entry;
+	struct xt_standard_target target;
+};
+
+struct ip6t_error {
+	struct ip6t_entry entry;
+	struct xt_error_target target;
+};
+
+#define IP6T_ENTRY_INIT(__size)						       \
+{									       \
+	.target_offset	= sizeof(struct ip6t_entry),			       \
+	.next_offset	= (__size),					       \
+}
+
+#define IP6T_STANDARD_INIT(__verdict)					       \
+{									       \
+	.entry		= IP6T_ENTRY_INIT(sizeof(struct ip6t_standard)),       \
+	.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(XT_ERROR_TARGET,		       \
+					 sizeof(struct xt_error_target)),      \
+	.target.errorname = "ERROR",					       \
+}
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use
+ * a raw socket for this. Instead we check rights in the calls.
+ *
+ * ATTENTION: check linux/in6.h before adding new number here.
+ */
+#define IP6T_BASE_CTL			64
+
+#define IP6T_SO_SET_REPLACE		(IP6T_BASE_CTL)
+#define IP6T_SO_SET_ADD_COUNTERS	(IP6T_BASE_CTL + 1)
+#define IP6T_SO_SET_MAX			IP6T_SO_SET_ADD_COUNTERS
+
+#define IP6T_SO_GET_INFO		(IP6T_BASE_CTL)
+#define IP6T_SO_GET_ENTRIES		(IP6T_BASE_CTL + 1)
+#define IP6T_SO_GET_REVISION_MATCH	(IP6T_BASE_CTL + 4)
+#define IP6T_SO_GET_REVISION_TARGET	(IP6T_BASE_CTL + 5)
+#define IP6T_SO_GET_MAX			IP6T_SO_GET_REVISION_TARGET
+
+/* ICMP matching stuff */
+struct ip6t_icmp {
+	u_int8_t type;				/* type to match */
+	u_int8_t code[2];			/* range of code */
+	u_int8_t invflags;			/* Inverse flags */
+};
+
+/* Values for "inv" field for struct ipt_icmp. */
+#define IP6T_ICMP_INV	0x01	/* Invert the sense of type/code test */
+
+/* The argument to IP6T_SO_GET_INFO */
+struct ip6t_getinfo {
+	/* Which table: caller fills this in. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* Kernel fills these in. */
+	/* Which hook entry points are valid: bitmask */
+	unsigned int valid_hooks;
+
+	/* Hook entry points: one per netfilter hook. */
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+	/* Underflow points. */
+	unsigned int underflow[NF_INET_NUMHOOKS];
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Size of entries. */
+	unsigned int size;
+};
+
+/* The argument to IP6T_SO_SET_REPLACE. */
+struct ip6t_replace {
+	/* Which table. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* Which hook entry points are valid: bitmask.  You can't
+           change this. */
+	unsigned int valid_hooks;
+
+	/* Number of entries */
+	unsigned int num_entries;
+
+	/* Total size of new entries */
+	unsigned int size;
+
+	/* Hook entry points. */
+	unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+	/* Underflow points. */
+	unsigned int underflow[NF_INET_NUMHOOKS];
+
+	/* Information about old entries: */
+	/* Number of counters (must be equal to current number of entries). */
+	unsigned int num_counters;
+	/* The old entries' counters. */
+	struct xt_counters *counters;
+
+	/* The entries (hang off end: not really an array). */
+	struct ip6t_entry entries[0];
+};
+
+/* The argument to IP6T_SO_GET_ENTRIES. */
+struct ip6t_get_entries {
+	/* Which table: user fills this in. */
+	char name[XT_TABLE_MAXNAMELEN];
+
+	/* User fills this in: total entry size. */
+	unsigned int size;
+
+	/* The entries. */
+	struct ip6t_entry entrytable[0];
+};
+
+/* Helper functions */
+static __inline__ struct xt_entry_target *
+ip6t_get_target(struct ip6t_entry *e)
+{
+	return (void *)e + e->target_offset;
+}
+
+/*
+ *	Main firewall chains definitions and global var's definitions.
+ */
+
+#endif /* _IP6_TABLES_H */
diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h
new file mode 100644
index 0000000..ebd8ead
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_HL.h
@@ -0,0 +1,24 @@
+/* Hop Limit modification module for ip6tables
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's TTL module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+#include <linux/types.h>
+
+enum {
+	IP6T_HL_SET = 0,
+	IP6T_HL_INC,
+	IP6T_HL_DEC
+};
+
+#define IP6T_HL_MAXMODE	IP6T_HL_DEC
+
+struct ip6t_HL_info {
+	__u8	mode;
+	__u8	hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_LOG.h b/include/linux/netfilter_ipv6/ip6t_LOG.h
new file mode 100644
index 0000000..9dd5579
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_LOG.h
@@ -0,0 +1,19 @@
+#ifndef _IP6T_LOG_H
+#define _IP6T_LOG_H
+
+/* make sure not to change this without changing netfilter.h:NF_LOG_* (!) */
+#define IP6T_LOG_TCPSEQ		0x01	/* Log TCP sequence numbers */
+#define IP6T_LOG_TCPOPT		0x02	/* Log TCP options */
+#define IP6T_LOG_IPOPT		0x04	/* Log IP options */
+#define IP6T_LOG_UID		0x08	/* Log UID owning local socket */
+#define IP6T_LOG_NFLOG		0x10	/* Unsupported, don't use */
+#define IP6T_LOG_MACDECODE	0x20	/* Decode MAC header */
+#define IP6T_LOG_MASK		0x2f
+
+struct ip6t_log_info {
+	unsigned char level;
+	unsigned char logflags;
+	char prefix[30];
+};
+
+#endif /*_IPT_LOG_H*/
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
new file mode 100644
index 0000000..205ed62
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_REJECT.h
@@ -0,0 +1,20 @@
+#ifndef _IP6T_REJECT_H
+#define _IP6T_REJECT_H
+
+#include <linux/types.h>
+
+enum ip6t_reject_with {
+	IP6T_ICMP6_NO_ROUTE,
+	IP6T_ICMP6_ADM_PROHIBITED,
+	IP6T_ICMP6_NOT_NEIGHBOUR,
+	IP6T_ICMP6_ADDR_UNREACH,
+	IP6T_ICMP6_PORT_UNREACH,
+	IP6T_ICMP6_ECHOREPLY,
+	IP6T_TCP_RESET
+};
+
+struct ip6t_reject_info {
+	__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
new file mode 100644
index 0000000..5da2b65
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_ah.h
@@ -0,0 +1,22 @@
+#ifndef _IP6T_AH_H
+#define _IP6T_AH_H
+
+#include <linux/types.h>
+
+struct ip6t_ah {
+	__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
+#define IP6T_AH_LEN 0x02
+#define IP6T_AH_RES 0x04
+
+/* Values for "invflags" field in struct ip6t_ah. */
+#define IP6T_AH_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IP6T_AH_INV_LEN		0x02	/* Invert the sense of length. */
+#define IP6T_AH_INV_MASK	0x03	/* All possible flags. */
+
+#endif /*_IP6T_AH_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h
new file mode 100644
index 0000000..b47f61b
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_frag.h
@@ -0,0 +1,25 @@
+#ifndef _IP6T_FRAG_H
+#define _IP6T_FRAG_H
+
+#include <linux/types.h>
+
+struct ip6t_frag {
+	__u32 ids[2];			/* Security Parameter Index */
+	__u32 hdrlen;			/* Header Length */
+	__u8  flags;			/*  */
+	__u8  invflags;			/* Inverse flags */
+};
+
+#define IP6T_FRAG_IDS 		0x01
+#define IP6T_FRAG_LEN 		0x02
+#define IP6T_FRAG_RES 		0x04
+#define IP6T_FRAG_FST 		0x08
+#define IP6T_FRAG_MF  		0x10
+#define IP6T_FRAG_NMF  		0x20
+
+/* Values for "invflags" field in struct ip6t_frag. */
+#define IP6T_FRAG_INV_IDS	0x01	/* Invert the sense of ids. */
+#define IP6T_FRAG_INV_LEN	0x02	/* Invert the sense of length. */
+#define IP6T_FRAG_INV_MASK	0x03	/* All possible flags. */
+
+#endif /*_IP6T_FRAG_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h
new file mode 100644
index 0000000..6e76dbc
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_hl.h
@@ -0,0 +1,24 @@
+/* ip6tables module for matching the Hop Limit value
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+#include <linux/types.h>
+
+enum {
+	IP6T_HL_EQ = 0,		/* equals */
+	IP6T_HL_NE,		/* not equals */
+	IP6T_HL_LT,		/* less than */
+	IP6T_HL_GT,		/* greater than */
+};
+
+
+struct ip6t_hl_info {
+	__u8	mode;
+	__u8	hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_ipv6header.h b/include/linux/netfilter_ipv6/ip6t_ipv6header.h
new file mode 100644
index 0000000..efae3a2
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_ipv6header.h
@@ -0,0 +1,28 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+
+#ifndef __IPV6HEADER_H
+#define __IPV6HEADER_H
+
+#include <linux/types.h>
+
+struct ip6t_ipv6header_info {
+	__u8 matchflags;
+	__u8 invflags;
+	__u8 modeflag;
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /* __IPV6HEADER_H */
diff --git a/include/linux/netfilter_ipv6/ip6t_mh.h b/include/linux/netfilter_ipv6/ip6t_mh.h
new file mode 100644
index 0000000..a7729a5
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_mh.h
@@ -0,0 +1,16 @@
+#ifndef _IP6T_MH_H
+#define _IP6T_MH_H
+
+#include <linux/types.h>
+
+/* MH matching stuff */
+struct ip6t_mh {
+	__u8 types[2];	/* MH type range */
+	__u8 invflags;	/* Inverse flags */
+};
+
+/* Values for "invflags" field in struct ip6t_mh. */
+#define IP6T_MH_INV_TYPE	0x01	/* Invert the sense of type. */
+#define IP6T_MH_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IP6T_MH_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_opts.h b/include/linux/netfilter_ipv6/ip6t_opts.h
new file mode 100644
index 0000000..17d419a
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_opts.h
@@ -0,0 +1,24 @@
+#ifndef _IP6T_OPTS_H
+#define _IP6T_OPTS_H
+
+#include <linux/types.h>
+
+#define IP6T_OPTS_OPTSNR 16
+
+struct ip6t_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
+#define IP6T_OPTS_OPTS 		0x02
+#define IP6T_OPTS_NSTRICT	0x04
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_OPTS_INV_LEN	0x01	/* Invert the sense of length. */
+#define IP6T_OPTS_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IP6T_OPTS_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_rt.h b/include/linux/netfilter_ipv6/ip6t_rt.h
new file mode 100644
index 0000000..7605a5f
--- /dev/null
+++ b/include/linux/netfilter_ipv6/ip6t_rt.h
@@ -0,0 +1,33 @@
+#ifndef _IP6T_RT_H
+#define _IP6T_RT_H
+
+#include <linux/types.h>
+/*#include <linux/in6.h>*/
+
+#define IP6T_RT_HOPS 16
+
+struct ip6t_rt {
+	__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 */
+	__u8 addrnr;			/* Nr of Addresses */
+};
+
+#define IP6T_RT_TYP 		0x01
+#define IP6T_RT_SGS 		0x02
+#define IP6T_RT_LEN 		0x04
+#define IP6T_RT_RES 		0x08
+#define IP6T_RT_FST_MASK	0x30
+#define IP6T_RT_FST 		0x10
+#define IP6T_RT_FST_NSTRICT	0x20
+
+/* Values for "invflags" field in struct ip6t_rt. */
+#define IP6T_RT_INV_TYP		0x01	/* Invert the sense of type. */
+#define IP6T_RT_INV_SGS		0x02	/* Invert the sense of Segments. */
+#define IP6T_RT_INV_LEN		0x04	/* Invert the sense of length. */
+#define IP6T_RT_INV_MASK	0x07	/* All possible flags. */
+
+#endif /*_IP6T_RT_H*/
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/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
new file mode 100644
index 0000000..0217267
--- /dev/null
+++ b/include/xtables.h
@@ -0,0 +1,564 @@
+#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
+
+#include <xtables-version.h>
+
+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 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;
+	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;
+	void *udata;
+};
+
+/**
+ * @ext_name:	name of extension currently being processed
+ * @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, *udata;
+	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;
+};
+
+enum xtables_ext_flags {
+	XTABLES_EXT_ALIAS = 1 << 0,
+};
+
+/* 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;
+	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. */
+	size_t size;
+
+	/* Size of match data relevant 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);
+
+	/* 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;
+
+	/* New parser */
+	void (*x6_parse)(struct xt_option_call *);
+	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;
+	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;
+
+	/* 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;
+
+
+	/* Size of target data. */
+	size_t size;
+
+	/* Size of target data relevant 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);
+
+	/* 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;
+
+	/* New parser */
+	void (*x6_parse)(struct xt_option_call *);
+	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;
+	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);
+
+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);
+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)))
+
+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 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 **,
+	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 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 **,
+	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);
+
+#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
+#		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);
+
+/* 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 *);
+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
new file mode 100644
index 0000000..31baf7d
--- /dev/null
+++ b/iptables/.gitignore
@@ -0,0 +1,15 @@
+/ip6tables
+/ip6tables-save
+/ip6tables-restore
+/ip6tables-static
+/iptables
+/iptables.8
+/iptables-extensions.8
+/iptables-extensions.8.tmpl
+/iptables-save
+/iptables-restore
+/iptables-static
+/iptables-xml
+/xtables-multi
+
+/xtables.pc
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
new file mode 100644
index 0000000..a4246eb
--- /dev/null
+++ b/iptables/Makefile.am
@@ -0,0 +1,54 @@
+# -*- Makefile -*-
+
+AM_CFLAGS        = ${regular_CFLAGS}
+AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
+
+xtables_multi_SOURCES  = xtables-multi.c iptables-xml.c
+xtables_multi_CFLAGS   = ${AM_CFLAGS}
+xtables_multi_LDADD    = ../extensions/libext.a
+if ENABLE_STATIC
+xtables_multi_CFLAGS  += -DALL_INCLUSIVE
+endif
+if ENABLE_IPV4
+xtables_multi_SOURCES += iptables-save.c iptables-restore.c \
+                         iptables-standalone.c iptables.c
+xtables_multi_CFLAGS  += -DENABLE_IPV4
+xtables_multi_LDADD   += ../libiptc/libip4tc.la ../extensions/libext4.a
+endif
+if ENABLE_IPV6
+xtables_multi_SOURCES += ip6tables-save.c ip6tables-restore.c \
+                          ip6tables-standalone.c ip6tables.c
+xtables_multi_CFLAGS  += -DENABLE_IPV6
+xtables_multi_LDADD   += ../libiptc/libip6tc.la ../extensions/libext6.a
+endif
+xtables_multi_SOURCES += xshared.c
+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 iptables-extensions.8
+CLEANFILES       = iptables.8
+
+vx_bin_links   = iptables-xml
+if ENABLE_IPV4
+v4_sbin_links  = iptables iptables-restore iptables-save
+endif
+if ENABLE_IPV6
+v6_sbin_links  = ip6tables ip6tables-restore ip6tables-save
+endif
+
+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
+
+# Using if..fi avoids an ugly "error (ignored)" message :)
+install-exec-hook:
+	-if test -z "${DESTDIR}"; then /sbin/ldconfig; fi;
+	${INSTALL} -dm0755 "${DESTDIR}${bindir}";
+	for i in ${vx_bin_links}; do ${LN_S} -f "${sbindir}/xtables-multi" "${DESTDIR}${bindir}/$$i"; done;
+	for i in ${v4_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done;
+	for i in ${v6_sbin_links}; do ${LN_S} -f xtables-multi "${DESTDIR}${sbindir}/$$i"; done;
diff --git a/iptables/ip6tables-multi.h b/iptables/ip6tables-multi.h
new file mode 100644
index 0000000..551029a
--- /dev/null
+++ b/iptables/ip6tables-multi.h
@@ -0,0 +1,8 @@
+#ifndef _IP6TABLES_MULTI_H
+#define _IP6TABLES_MULTI_H 1
+
+extern int ip6tables_main(int, char **);
+extern int ip6tables_save_main(int, char **);
+extern int ip6tables_restore_main(int, char **);
+
+#endif /* _IP6TABLES_MULTI_H */
diff --git a/iptables/ip6tables-restore.8 b/iptables/ip6tables-restore.8
new file mode 100644
index 0000000..cf4ea3e
--- /dev/null
+++ b/iptables/ip6tables-restore.8
@@ -0,0 +1 @@
+.so man8/iptables-restore.8
diff --git a/iptables/ip6tables-restore.c b/iptables/ip6tables-restore.c
new file mode 100644
index 0000000..b8b9e0d
--- /dev/null
+++ b/iptables/ip6tables-restore.c
@@ -0,0 +1,462 @@
+/* Code to restore the iptables state, from file by ip6tables-save.
+ * Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ *
+ * based on iptables-restore
+ * Authors:
+ *      Harald Welte <laforge@gnumonks.org>
+ *      Rusty Russell <rusty@linuxcare.com.au>
+ * This code is distributed under the terms of GNU GPL v2
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ip6tables.h"
+#include "xtables.h"
+#include "libiptc/libip6tc.h"
+#include "ip6tables-multi.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static const struct option options[] = {
+	{.name = "binary",   .has_arg = false, .val = 'b'},
+	{.name = "counters", .has_arg = false, .val = 'c'},
+	{.name = "verbose",  .has_arg = false, .val = 'v'},
+	{.name = "test",     .has_arg = false, .val = 't'},
+	{.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},
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+			"	   [ --binary ]\n"
+			"	   [ --counters ]\n"
+			"	   [ --verbose ]\n"
+			"	   [ --test ]\n"
+			"	   [ --help ]\n"
+			"	   [ --noflush ]\n"
+			"          [ --modprobe=<command>]\n", name);
+
+	exit(1);
+}
+
+static struct xtc_handle *create_handle(const char *tablename)
+{
+	struct xtc_handle *handle;
+
+	handle = ip6tc_init(tablename);
+
+	if (!handle) {
+		/* try to insmod the module if iptc_init failed */
+		xtables_load_ko(xtables_modprobe_program, false);
+		handle = ip6tc_init(tablename);
+	}
+
+	if (!handle) {
+		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
+			"table '%s'\n", ip6tables_globals.program_name,
+			tablename);
+		exit(1);
+	}
+	return handle;
+}
+
+static int parse_counters(char *string, struct xt_counters *ctr)
+{
+	unsigned long long pcnt, bcnt;
+	int ret;
+
+	ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
+	ctr->pcnt = pcnt;
+	ctr->bcnt = bcnt;
+	return ret == 2;
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+	DEBUGP("add_argv: %s\n", what);
+	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
+		newargv[newargc] = strdup(what);
+		newargv[++newargc] = NULL;
+		return 1;
+	} else {
+		xtables_error(PARAMETER_PROBLEM,
+			"Parser cannot handle more arguments\n");
+		return 0;
+	}
+}
+
+static void free_argv(void) {
+	int i;
+
+	for (i = 0; i < newargc; i++)
+		free(newargv[i]);
+}
+
+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 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[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;
+
+	ip6tables_globals.program_name = "ip6tables-restore";
+	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
+	if (c < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				ip6tables_globals.program_name,
+				ip6tables_globals.program_version);
+		exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions6();
+#endif
+
+	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
+		switch (c) {
+			case 'b':
+				binary = 1;
+				break;
+			case 'c':
+				counters = 1;
+				break;
+			case 'v':
+				verbose = 1;
+				break;
+			case 't':
+				testing = 1;
+				break;
+			case 'h':
+				print_usage("ip6tables-restore",
+					    IPTABLES_VERSION);
+				break;
+			case 'n':
+				noflush = 1;
+				break;
+			case 'M':
+				xtables_modprobe_program = optarg;
+				break;
+			case 'T':
+				tablename = optarg;
+				break;
+		}
+	}
+
+	if (optind == argc - 1) {
+		in = fopen(argv[optind], "re");
+		if (!in) {
+			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
+				strerror(errno));
+			exit(1);
+		}
+	}
+	else if (optind < argc) {
+		fprintf(stderr, "Unknown arguments found on commandline\n");
+		exit(1);
+	}
+	else in = stdin;
+
+	/* Grab standard input. */
+	while (fgets(buffer, sizeof(buffer), in)) {
+		int ret = 0;
+
+		line++;
+		if (buffer[0] == '\n')
+			continue;
+		else if (buffer[0] == '#') {
+			if (verbose)
+				fputs(buffer, stdout);
+			continue;
+		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+			if (!testing) {
+				DEBUGP("Calling commit\n");
+				ret = ops->commit(handle);
+				ops->free(handle);
+				handle = NULL;
+			} else {
+				DEBUGP("Not calling commit, testing\n");
+				ret = 1;
+			}
+			in_table = 0;
+		} else if ((buffer[0] == '*') && (!in_table)) {
+			/* New table */
+			char *table;
+
+			table = strtok(buffer+1, " \t\n");
+			DEBUGP("line %u, table '%s'\n", line, table);
+			if (!table) {
+				xtables_error(PARAMETER_PROBLEM,
+					"%s: line %u table name invalid\n",
+					xt_params->program_name, line);
+				exit(1);
+			}
+			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
+			curtable[XT_TABLE_MAXNAMELEN] = '\0';
+
+			if (tablename != NULL && strcmp(tablename, table) != 0)
+				continue;
+			if (handle)
+				ops->free(handle);
+
+			handle = create_handle(table);
+			if (noflush == 0) {
+				DEBUGP("Cleaning all chains of table '%s'\n",
+					table);
+				for_each_chain6(flush_entries6, verbose, 1,
+						handle);
+
+				DEBUGP("Deleting all user-defined chains "
+				       "of table '%s'\n", table);
+				for_each_chain6(delete_chain6, verbose, 0,
+						handle);
+			}
+
+			ret = 1;
+			in_table = 1;
+
+		} else if ((buffer[0] == ':') && (in_table)) {
+			/* New chain. */
+			char *policy, *chain;
+
+			chain = strtok(buffer+1, " \t\n");
+			DEBUGP("line %u, chain '%s'\n", line, chain);
+			if (!chain) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u chain name invalid\n",
+					   xt_params->program_name, line);
+				exit(1);
+			}
+
+			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Invalid chain name `%s' "
+					   "(%u chars max)",
+					   chain, XT_EXTENSION_MAXNAMELEN - 1);
+
+			if (ops->builtin(chain, handle) <= 0) {
+				if (noflush && ops->is_chain(chain, handle)) {
+					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+					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 (!ops->create_chain(chain, handle))
+						xtables_error(PARAMETER_PROBLEM,
+							   "error creating chain "
+							   "'%s':%s\n", chain,
+							   strerror(errno));
+				}
+			}
+
+			policy = strtok(NULL, " \t\n");
+			DEBUGP("line %u, policy '%s'\n", line, policy);
+			if (!policy) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u policy invalid\n",
+					   xt_params->program_name, line);
+				exit(1);
+			}
+
+			if (strcmp(policy, "-") != 0) {
+				struct xt_counters count;
+
+				if (counters) {
+					char *ctrs;
+					ctrs = strtok(NULL, " \t\n");
+
+					if (!ctrs || !parse_counters(ctrs, &count))
+						xtables_error(PARAMETER_PROBLEM,
+							  "invalid policy counters "
+							  "for chain '%s'\n", chain);
+
+				} else {
+					memset(&count, 0, sizeof(count));
+				}
+
+				DEBUGP("Setting policy of chain %s to %s\n",
+					chain, policy);
+
+				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,
+						ops->strerror(errno));
+			}
+
+			ret = 1;
+
+		} else if (in_table) {
+			int a;
+			char *ptr = buffer;
+			char *pcnt = NULL;
+			char *bcnt = NULL;
+			char *parsestart;
+
+			/* reset the newargv */
+			newargc = 0;
+
+			if (buffer[0] == '[') {
+				/* we have counters in our input */
+				ptr = strchr(buffer, ']');
+				if (!ptr)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				pcnt = strtok(buffer+1, ":");
+				if (!pcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need :\n",
+						   line);
+
+				bcnt = strtok(NULL, "]");
+				if (!bcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				/* start command parsing after counter */
+				parsestart = ptr + 1;
+			} else {
+				/* start command parsing at start of line */
+				parsestart = buffer;
+			}
+
+			add_argv(argv[0]);
+			add_argv("-t");
+			add_argv(curtable);
+
+			if (counters && pcnt && bcnt) {
+				add_argv("--set-counters");
+				add_argv((char *) pcnt);
+				add_argv((char *) bcnt);
+			}
+
+			add_param_to_argv(parsestart);
+
+			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
+				newargc, curtable);
+
+			for (a = 0; a < newargc; a++)
+				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+			ret = do_command6(newargc, newargv,
+					 &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",
+					xt_params->program_name, line);
+			exit(1);
+		}
+	}
+	if (in_table) {
+		fprintf(stderr, "%s: COMMIT expected at line %u\n",
+				xt_params->program_name, line + 1);
+		exit(1);
+	}
+
+	fclose(in);
+	return 0;
+}
diff --git a/iptables/ip6tables-save.8 b/iptables/ip6tables-save.8
new file mode 100644
index 0000000..182f55c
--- /dev/null
+++ b/iptables/ip6tables-save.8
@@ -0,0 +1 @@
+.so man8/iptables-save.8
diff --git a/iptables/ip6tables-save.c b/iptables/ip6tables-save.c
new file mode 100644
index 0000000..d819b30
--- /dev/null
+++ b/iptables/ip6tables-save.c
@@ -0,0 +1,169 @@
+/* Code to save the ip6tables state, in human readable-form. */
+/* Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ * Original code: iptables-save
+ * Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and
+ *          Harald Welte <laforge@gnumonks.org>
+ * This code is distributed under the terms of GNU GPL v2
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "libiptc/libip6tc.h"
+#include "ip6tables.h"
+#include "ip6tables-multi.h"
+
+#ifndef NO_SHARED_LIBS
+#include <dlfcn.h>
+#endif
+
+static int show_counters = 0;
+
+static const struct option options[] = {
+	{.name = "counters", .has_arg = false, .val = 'c'},
+	{.name = "dump",     .has_arg = false, .val = 'd'},
+	{.name = "table",    .has_arg = true,  .val = 't'},
+	{.name = "modprobe", .has_arg = true,  .val = 'M'},
+	{NULL},
+};
+
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+	int ret = 1;
+	FILE *procfile = NULL;
+	char tablename[XT_TABLE_MAXNAMELEN+1];
+
+	procfile = fopen("/proc/net/ip6_tables_names", "re");
+	if (!procfile)
+		return ret;
+
+	while (fgets(tablename, sizeof(tablename), procfile)) {
+		if (tablename[strlen(tablename) - 1] != '\n')
+			xtables_error(OTHER_PROBLEM,
+				   "Badly formed tablename `%s'\n",
+				   tablename);
+		tablename[strlen(tablename) - 1] = '\0';
+		ret &= func(tablename);
+	}
+
+	fclose(procfile);
+	return ret;
+}
+
+
+static int do_output(const char *tablename)
+{
+	struct xtc_handle *h;
+	const char *chain = NULL;
+
+	if (!tablename)
+		return for_each_table(&do_output);
+
+	h = ip6tc_init(tablename);
+	if (h == NULL) {
+		xtables_load_ko(xtables_modprobe_program, false);
+		h = ip6tc_init(tablename);
+	}
+	if (!h)
+		xtables_error(OTHER_PROBLEM, "Cannot initialize: %s\n",
+			   ip6tc_strerror(errno));
+
+	time_t now = time(NULL);
+
+	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)) {
+
+		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));
+	ip6tc_free(h);
+
+	return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int ip6tables_save_main(int argc, char *argv[])
+{
+	const char *tablename = NULL;
+	int c;
+
+	ip6tables_globals.program_name = "ip6tables-save";
+	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
+	if (c < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				ip6tables_globals.program_name,
+				ip6tables_globals.program_version);
+		exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions6();
+#endif
+
+	while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+		switch (c) {
+		case 'c':
+			show_counters = 1;
+			break;
+
+		case 't':
+			/* Select specific table. */
+			tablename = optarg;
+			break;
+		case 'M':
+			xtables_modprobe_program = optarg;
+			break;
+		case 'd':
+			do_output(tablename);
+			exit(0);
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr, "Unknown arguments found on commandline\n");
+		exit(1);
+	}
+
+	return !do_output(tablename);
+}
diff --git a/iptables/ip6tables-standalone.c b/iptables/ip6tables-standalone.c
new file mode 100644
index 0000000..35d2d9a
--- /dev/null
+++ b/iptables/ip6tables-standalone.c
@@ -0,0 +1,81 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * 		    Marc Boucher <marc+nf@mbsi.ca>
+ * 		    James Morris <jmorris@intercode.com.au>
+ * 		    Harald Welte <laforge@gnumonks.org>
+ * 		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ *	iptables -- IP firewall administration for kernels with
+ *	firewall table (aimed for the 2.3 kernels)
+ *
+ *	See the accompanying manual page iptables(8) for information
+ *	about proper usage of this program.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include "ip6tables-multi.h"
+
+int
+ip6tables_main(int argc, char *argv[])
+{
+	int ret;
+	char *table = "filter";
+	struct xtc_handle *handle = NULL;
+
+	ip6tables_globals.program_name = "ip6tables";
+	ret = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
+	if (ret < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				ip6tables_globals.program_name,
+				ip6tables_globals.program_version);
+		exit(1);
+	}
+
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions6();
+#endif
+
+	ret = do_command6(argc, argv, &table, &handle, false);
+	if (ret) {
+		ret = ip6tc_commit(handle);
+		ip6tc_free(handle);
+	}
+
+	if (!ret) {
+		if (errno == EINVAL) {
+			fprintf(stderr, "ip6tables: %s. "
+					"Run `dmesg' for more information.\n",
+				ip6tc_strerror(errno));
+		} else {
+			fprintf(stderr, "ip6tables: %s.\n",
+				ip6tc_strerror(errno));
+		}
+		if (errno == EAGAIN)
+			exit(RESOURCE_PROBLEM);
+	}
+
+	exit(!ret);
+}
diff --git a/iptables/ip6tables.8 b/iptables/ip6tables.8
new file mode 100644
index 0000000..0dee41a
--- /dev/null
+++ b/iptables/ip6tables.8
@@ -0,0 +1 @@
+.so man8/iptables.8
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
new file mode 100644
index 0000000..a5199d5
--- /dev/null
+++ b/iptables/ip6tables.c
@@ -0,0 +1,1956 @@
+/* Code to take an ip6tables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * 		    Marc Boucher <marc+nf@mbsi.ca>
+ * 		    James Morris <jmorris@intercode.com.au>
+ * 		    Harald Welte <laforge@gnumonks.org>
+ * 		    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 as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ip6tables.h>
+#include <xtables.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "ip6tables-multi.h"
+#include "xshared.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define CMD_NONE		0x0000U
+#define CMD_INSERT		0x0001U
+#define CMD_DELETE		0x0002U
+#define CMD_DELETE_NUM		0x0004U
+#define CMD_REPLACE		0x0008U
+#define CMD_APPEND		0x0010U
+#define CMD_LIST		0x0020U
+#define CMD_FLUSH		0x0040U
+#define CMD_ZERO		0x0080U
+#define CMD_NEW_CHAIN		0x0100U
+#define CMD_DELETE_CHAIN	0x0200U
+#define CMD_SET_POLICY		0x0400U
+#define CMD_RENAME_CHAIN	0x0800U
+#define CMD_LIST_RULES		0x1000U
+#define CMD_ZERO_NUM		0x2000U
+#define CMD_CHECK		0x4000U
+#define NUMBER_OF_CMD	16
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+				 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
+
+#define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
+static const char optflags[]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c'};
+
+static struct option original_opts[] = {
+	{.name = "append",        .has_arg = 1, .val = 'A'},
+	{.name = "delete",        .has_arg = 1, .val = 'D'},
+	{.name = "check" ,        .has_arg = 1, .val = 'C'},
+	{.name = "insert",        .has_arg = 1, .val = 'I'},
+	{.name = "replace",       .has_arg = 1, .val = 'R'},
+	{.name = "list",          .has_arg = 2, .val = 'L'},
+	{.name = "list-rules",    .has_arg = 2, .val = 'S'},
+	{.name = "flush",         .has_arg = 2, .val = 'F'},
+	{.name = "zero",          .has_arg = 2, .val = 'Z'},
+	{.name = "new-chain",     .has_arg = 1, .val = 'N'},
+	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
+	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
+	{.name = "policy",        .has_arg = 1, .val = 'P'},
+	{.name = "source",        .has_arg = 1, .val = 's'},
+	{.name = "destination",   .has_arg = 1, .val = 'd'},
+	{.name = "src",           .has_arg = 1, .val = 's'}, /* synonym */
+	{.name = "dst",           .has_arg = 1, .val = 'd'}, /* synonym */
+	{.name = "protocol",      .has_arg = 1, .val = 'p'},
+	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
+	{.name = "jump",          .has_arg = 1, .val = 'j'},
+	{.name = "table",         .has_arg = 1, .val = 't'},
+	{.name = "match",         .has_arg = 1, .val = 'm'},
+	{.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'},
+	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
+	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
+	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
+	{.name = "goto",          .has_arg = 1, .val = 'g'},
+	{.name = "ipv4",          .has_arg = 0, .val = '4'},
+	{.name = "ipv6",          .has_arg = 0, .val = '6'},
+	{NULL},
+};
+
+void ip6tables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
+struct xtables_globals ip6tables_globals = {
+	.option_offset = 0,
+	.program_version = IPTABLES_VERSION,
+	.orig_opts = original_opts,
+	.exit_err = ip6tables_exit_error,
+};
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o --line -c */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x'},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' '},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO_NUM*/  {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/     {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
+};
+
+static const unsigned int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IP6T_INV_SRCIP,
+/* -d */ IP6T_INV_DSTIP,
+/* -p */ XT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IP6T_INV_VIA_IN,
+/* -o */ IP6T_INV_VIA_OUT,
+/*--line*/ 0,
+/* -c */ 0,
+};
+
+#define opts ip6tables_globals.opts
+#define prog_name ip6tables_globals.program_name
+#define prog_vers ip6tables_globals.program_version
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+	const char *name;
+	uint8_t num;
+};
+
+static void __attribute__((noreturn))
+exit_tryhelp(int status)
+{
+	if (line != -1)
+		fprintf(stderr, "Error occurred at line: %d\n", line);
+	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+			prog_name, prog_name);
+	xtables_free_opts(1);
+	exit(status);
+}
+
+static void
+exit_printhelp(const struct xtables_rule_match *matches)
+{
+	printf("%s v%s\n\n"
+"Usage: %s -[ACD] chain rule-specification [options]\n"
+"       %s -I chain [rulenum] rule-specification [options]\n"
+"       %s -R chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LS] [chain [rulenum]] [options]\n"
+"       %s -[FZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+	       prog_name, prog_vers, prog_name, prog_name,
+	       prog_name, prog_name, prog_name, prog_name,
+	       prog_name, prog_name, prog_name, prog_name);
+
+	printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain		Append to chain\n"
+"  --check   -C chain		Check for the existence of a rule\n"
+"  --delete  -D chain		Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"				Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"				Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"				Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain [rulenum]]\n"
+"				List the rules in a chain or all chains\n"
+"  --list-rules -S [chain [rulenum]]\n"
+"				Print the rules in a chain or all chains\n"
+"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain [rulenum]]\n"
+"				Zero counters in chain or all chains\n"
+"  --new     -N chain		Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]		Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"				Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"				Change chain name, (moving any references)\n"
+
+"Options:\n"
+"    --ipv4	-4		Error (line is ignored by ip6tables-restore)\n"
+"    --ipv6	-6		Nothing (line is ignored by iptables-restore)\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"
+"				destination specification\n"
+"[!] --in-interface -i input name[+]\n"
+"				network interface name ([+] for wildcard)\n"
+"  --jump	-j target\n"
+"				target for rule (may load target extension)\n"
+#ifdef IP6T_F_GOTO
+"  --goto	-g chain\n"
+"				jump to chain with no return\n"
+#endif
+"  --match	-m match\n"
+"				extended match (may load extension)\n"
+"  --numeric	-n		numeric output of addresses and ports\n"
+"[!] --out-interface -o output name[+]\n"
+"				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"*/
+"  --modprobe=<command>		try to insert modules using this command\n"
+"  --set-counters PKTS BYTES	set the counter during insert/append\n"
+"[!] --version	-V		print package version.\n");
+
+	print_extension_helps(xtables_targets, matches);
+	exit(0);
+}
+
+void
+ip6tables_exit_error(enum xtables_exittype status, const char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+	if (status == PARAMETER_PROBLEM)
+		exit_tryhelp(status);
+	if (status == VERSION_PROBLEM)
+		fprintf(stderr,
+			"Perhaps ip6tables or your kernel needs to be upgraded.\n");
+	/* On error paths, make sure that we don't leak memory */
+	xtables_free_opts(1);
+	exit(status);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+	int i, j, legal = 0;
+
+	/* Check that commands are valid with options.  Complicated by the
+	 * fact that if an option is legal with *any* command given, it is
+	 * legal overall (ie. -z and -l).
+	 */
+	for (i = 0; i < NUMBER_OF_OPT; i++) {
+		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+		for (j = 0; j < NUMBER_OF_CMD; j++) {
+			if (!(command & (1<<j)))
+				continue;
+
+			if (!(options & (1<<i))) {
+				if (commands_v_options[j][i] == '+')
+					xtables_error(PARAMETER_PROBLEM,
+						   "You need to supply the `-%c' "
+						   "option for this command\n",
+						   optflags[i]);
+			} else {
+				if (commands_v_options[j][i] != 'x')
+					legal = 1;
+				else if (legal == 0)
+					legal = -1;
+			}
+		}
+		if (legal == -1)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Illegal option `-%c' with this command\n",
+				   optflags[i]);
+	}
+}
+
+static char
+opt2char(int option)
+{
+	const char *ptr;
+	for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+	const char *ptr;
+	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const int othercmds,
+	    int invert)
+{
+	if (invert)
+		xtables_error(PARAMETER_PROBLEM, "unexpected '!' flag");
+	if (*cmd & (~othercmds))
+		xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
+			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+	*cmd |= newcmd;
+}
+
+/*
+ *	All functions starting with "parse" should succeed, otherwise
+ *	the program fails.
+ *	Most routines return pointers to static data that may change
+ *	between calls to the same or other routines with a few exceptions:
+ *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *	return global static data.
+*/
+
+/* These are invalid numbers as upper layer protocol */
+static int is_exthdr(uint16_t proto)
+{
+	return (proto == IPPROTO_ROUTING ||
+		proto == IPPROTO_FRAGMENT ||
+		proto == IPPROTO_AH ||
+		proto == IPPROTO_DSTOPTS);
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+	unsigned int rulenum;
+
+	if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid rule number `%s'", rule);
+
+	return rulenum;
+}
+
+static void
+parse_chain(const char *chainname)
+{
+	const char *ptr;
+
+	if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name `%s' too long (must be under %u chars)",
+			   chainname, XT_EXTENSION_MAXNAMELEN);
+
+	if (*chainname == '-' || *chainname == '!')
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name not allowed to start "
+			   "with `%c'\n", *chainname);
+
+	if (xtables_find_target(chainname, XTF_TRY_LOAD))
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name may not clash "
+			   "with target name\n");
+
+	for (ptr = chainname; *ptr; ptr++)
+		if (isspace(*ptr))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid chain name `%s'", chainname);
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+	const char *ptr;
+
+	if (strlen(targetname) < 1)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid target name (too short)");
+
+	if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid target name `%s' (%u chars max)",
+			   targetname, XT_EXTENSION_MAXNAMELEN - 1);
+
+	for (ptr = targetname; *ptr; ptr++)
+		if (isspace(*ptr))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid target name `%s'", targetname);
+	return targetname;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
+	   int invert)
+{
+	if (*options & option)
+		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+			   opt2char(option));
+	*options |= option;
+
+	if (invert) {
+		unsigned int i;
+		for (i = 0; 1 << i != option; i++);
+
+		if (!inverse_for_options[i])
+			xtables_error(PARAMETER_PROBLEM,
+				   "cannot have ! before -%c",
+				   opt2char(option));
+		*invflg |= inverse_for_options[i];
+	}
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
+{
+	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);
+			xtables_print_num(counters.pcnt, (format|FMT_NOTABLE));
+			fputs("packets, ", stdout);
+			xtables_print_num(counters.bcnt, (format|FMT_NOTABLE));
+			fputs("bytes", stdout);
+		}
+		printf(")\n");
+	} else {
+		unsigned int refs;
+		if (!ip6tc_get_references(&refs, chain, handle))
+			printf(" (ERROR obtaining refs)\n");
+		else
+			printf(" (%u references)\n", refs);
+	}
+
+	if (format & FMT_LINENUMBERS)
+		printf(FMT("%-4s ", "%s "), "num");
+	if (!(format & FMT_NOCOUNTS)) {
+		if (format & FMT_KILOMEGAGIGA) {
+			printf(FMT("%5s ","%s "), "pkts");
+			printf(FMT("%5s ","%s "), "bytes");
+		} else {
+			printf(FMT("%8s ","%s "), "pkts");
+			printf(FMT("%10s ","%s "), "bytes");
+		}
+	}
+	if (!(format & FMT_NOTARGET))
+		printf(FMT("%-9s ","%s "), "target");
+	fputs(" prot ", stdout);
+	if (format & FMT_OPTIONS)
+		fputs("opt", stdout);
+	if (format & FMT_VIA) {
+		printf(FMT(" %-6s ","%s "), "in");
+		printf(FMT("%-6s ","%s "), "out");
+	}
+	printf(FMT(" %-19s ","%s "), "source");
+	printf(FMT(" %-19s "," %s "), "destination");
+	printf("\n");
+}
+
+
+static int
+print_match(const struct xt_entry_match *m,
+	    const struct ip6t_ip6 *ip,
+	    int numeric)
+{
+	const struct xtables_match *match =
+		xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL);
+
+	if (match) {
+		if (match->print)
+			match->print(ip, m, numeric);
+		else
+			printf("%s ", match->name);
+	} else {
+		if (m->u.user.name[0])
+			printf("UNKNOWN match `%s' ", m->u.user.name);
+	}
+	/* Don't stop iterating. */
+	return 0;
+}
+
+/* e is called `fw' here for historical reasons */
+static void
+print_firewall(const struct ip6t_entry *fw,
+	       const char *targname,
+	       unsigned int num,
+	       unsigned int format,
+	       struct xtc_handle *const handle)
+{
+	const struct xtables_target *target = NULL;
+	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(XT_STANDARD_TARGET,
+		         XTF_LOAD_MUST_SUCCEED);
+
+	t = ip6t_get_target((struct ip6t_entry *)fw);
+
+	if (format & FMT_LINENUMBERS)
+		printf(FMT("%-4u ", "%u "), num);
+
+	if (!(format & FMT_NOCOUNTS)) {
+		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 & XT_INV_PROTO ? '!' : ' ', stdout);
+	{
+		const char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC);
+		if (pname)
+			printf(FMT("%-5s", "%s "), pname);
+		else
+			printf(FMT("%-5hu", "%hu "), fw->ipv6.proto);
+	}
+
+	if (format & FMT_OPTIONS) {
+		if (format & FMT_NOTABLE)
+			fputs("opt ", stdout);
+		fputc(' ', stdout); /* Invert flag of FRAG */
+		fputc(' ', stdout); /* -f */
+		fputc(' ', stdout);
+	}
+
+	if (format & FMT_VIA) {
+		char iface[IFNAMSIZ+2];
+
+		if (fw->ipv6.invflags & IP6T_INV_VIA_IN) {
+			iface[0] = '!';
+			iface[1] = '\0';
+		}
+		else iface[0] = '\0';
+
+		if (fw->ipv6.iniface[0] != '\0') {
+			strcat(iface, fw->ipv6.iniface);
+		}
+		else if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+		printf(FMT(" %-6s ","in %s "), iface);
+
+		if (fw->ipv6.invflags & IP6T_INV_VIA_OUT) {
+			iface[0] = '!';
+			iface[1] = '\0';
+		}
+		else iface[0] = '\0';
+
+		if (fw->ipv6.outiface[0] != '\0') {
+			strcat(iface, fw->ipv6.outiface);
+		}
+		else if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+		printf(FMT("%-6s ","out %s "), iface);
+	}
+
+	fputc(fw->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
+	if (!memcmp(&fw->ipv6.smsk, &in6addr_any, sizeof in6addr_any)
+	    && !(format & FMT_NUMERIC))
+		printf(FMT("%-19s ","%s "), "anywhere");
+	else {
+		if (format & FMT_NUMERIC)
+			strcpy(buf, xtables_ip6addr_to_numeric(&fw->ipv6.src));
+		else
+			strcpy(buf, xtables_ip6addr_to_anyname(&fw->ipv6.src));
+		strcat(buf, xtables_ip6mask_to_numeric(&fw->ipv6.smsk));
+		printf(FMT("%-19s ","%s "), buf);
+	}
+
+	fputc(fw->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
+	if (!memcmp(&fw->ipv6.dmsk, &in6addr_any, sizeof in6addr_any)
+	    && !(format & FMT_NUMERIC))
+		printf(FMT("%-19s ","-> %s"), "anywhere");
+	else {
+		if (format & FMT_NUMERIC)
+			strcpy(buf, xtables_ip6addr_to_numeric(&fw->ipv6.dst));
+		else
+			strcpy(buf, xtables_ip6addr_to_anyname(&fw->ipv6.dst));
+		strcat(buf, xtables_ip6mask_to_numeric(&fw->ipv6.dmsk));
+		printf(FMT("%-19s ","-> %s"), buf);
+	}
+
+	if (format & FMT_NOTABLE)
+		fputs("  ", stdout);
+
+#ifdef IP6T_F_GOTO
+	if(fw->ipv6.flags & IP6T_F_GOTO)
+		printf("[goto] ");
+#endif
+
+	IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC);
+
+	if (target) {
+		if (target->print)
+			/* Print the target information. */
+			target->print(&fw->ipv6, t, format & FMT_NUMERIC);
+	} else if (t->u.target_size != sizeof(*t))
+		printf("[%u bytes of unknown target data] ",
+		       (unsigned int)(t->u.target_size - sizeof(*t)));
+
+	if (!(format & FMT_NONEWLINE))
+		fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ip6t_entry *fw,
+		    struct xtc_handle *const h)
+{
+	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 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[],
+	     int verbose,
+	     struct xtc_handle *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ipv6.src = saddrs[i];
+		fw->ipv6.smsk = smasks[i];
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ipv6.dst = daddrs[j];
+			fw->ipv6.dmsk = dmasks[j];
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= ip6tc_append_entry(chain, fw, handle);
+		}
+	}
+
+	return ret;
+}
+
+static int
+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 xtc_handle *handle)
+{
+	fw->ipv6.src = *saddr;
+	fw->ipv6.dst = *daddr;
+	fw->ipv6.smsk = *smask;
+	fw->ipv6.dmsk = *dmask;
+
+	if (verbose)
+		print_firewall_line(fw, handle);
+	return ip6tc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const xt_chainlabel chain,
+	     struct ip6t_entry *fw,
+	     unsigned int rulenum,
+	     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[],
+	     int verbose,
+	     struct xtc_handle *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ipv6.src = saddrs[i];
+		fw->ipv6.smsk = smasks[i];
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ipv6.dst = daddrs[j];
+			fw->ipv6.dmsk = dmasks[j];
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= ip6tc_insert_entry(chain, fw, rulenum, handle);
+		}
+	}
+
+	return ret;
+}
+
+static unsigned char *
+make_delete_mask(const struct xtables_rule_match *matches,
+		 const struct xtables_target *target)
+{
+	/* Establish mask for comparison */
+	unsigned int size;
+	const struct xtables_rule_match *matchp;
+	unsigned char *mask, *mptr;
+
+	size = sizeof(struct ip6t_entry);
+	for (matchp = matches; matchp; matchp = matchp->next)
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+
+	mask = xtables_calloc(1, size
+			 + XT_ALIGN(sizeof(struct xt_entry_target))
+			 + target->size);
+
+	memset(mask, 0xFF, sizeof(struct ip6t_entry));
+	mptr = mask + sizeof(struct ip6t_entry);
+
+	for (matchp = matches; matchp; matchp = matchp->next) {
+		memset(mptr, 0xFF,
+		       XT_ALIGN(sizeof(struct xt_entry_match))
+		       + matchp->match->userspacesize);
+		mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+	}
+
+	memset(mptr, 0xFF,
+	       XT_ALIGN(sizeof(struct xt_entry_target))
+	       + target->userspacesize);
+
+	return mask;
+}
+
+static int
+delete_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[],
+	     int verbose,
+	     struct xtc_handle *handle,
+	     struct xtables_rule_match *matches,
+	     const struct xtables_target *target)
+{
+	unsigned int i, j;
+	int ret = 1;
+	unsigned char *mask;
+
+	mask = make_delete_mask(matches, target);
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ipv6.src = saddrs[i];
+		fw->ipv6.smsk = smasks[i];
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ipv6.dst = daddrs[j];
+			fw->ipv6.dmsk = dmasks[j];
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= ip6tc_delete_entry(chain, fw, mask, handle);
+		}
+	}
+	free(mask);
+
+	return ret;
+}
+
+static int
+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 xtc_handle *handle,
+	    struct xtables_rule_match *matches,
+	    const struct xtables_target *target)
+{
+	unsigned int i, j;
+	int ret = 1;
+	unsigned char *mask;
+
+	mask = make_delete_mask(matches, target);
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ipv6.src = saddrs[i];
+		fw->ipv6.smsk = smasks[i];
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ipv6.dst = daddrs[j];
+			fw->ipv6.dmsk = dmasks[j];
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= ip6tc_check_entry(chain, fw, mask, handle);
+		}
+	}
+
+	free(mask);
+	return ret;
+}
+
+int
+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;
+	char *chains;
+	unsigned int i, chaincount = 0;
+
+	chain = ip6tc_first_chain(handle);
+	while (chain) {
+		chaincount++;
+		chain = ip6tc_next_chain(handle);
+	}
+
+	chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount);
+	i = 0;
+	chain = ip6tc_first_chain(handle);
+	while (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(xt_chainlabel),
+				    handle) == 1)
+			continue;
+		ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle);
+	}
+
+	free(chains);
+	return ret;
+}
+
+int
+flush_entries6(const xt_chainlabel chain, int verbose,
+	      struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain6(flush_entries6, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Flushing chain `%s'\n", chain);
+	return ip6tc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain6(zero_entries, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Zeroing chain `%s'\n", chain);
+	return ip6tc_zero_entries(chain, handle);
+}
+
+int
+delete_chain6(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain6(delete_chain6, verbose, 0, handle);
+
+	if (verbose)
+		fprintf(stdout, "Deleting chain `%s'\n", chain);
+	return ip6tc_delete_chain(chain, handle);
+}
+
+static int
+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;
+	const char *this;
+
+	format = FMT_OPTIONS;
+	if (!verbose)
+		format |= FMT_NOCOUNTS;
+	else
+		format |= FMT_VIA;
+
+	if (numeric)
+		format |= FMT_NUMERIC;
+
+	if (!expanded)
+		format |= FMT_KILOMEGAGIGA;
+
+	if (linenumbers)
+		format |= FMT_LINENUMBERS;
+
+	for (this = ip6tc_first_chain(handle);
+	     this;
+	     this = ip6tc_next_chain(handle)) {
+		const struct ip6t_entry *i;
+		unsigned int num;
+
+		if (chain && strcmp(chain, this) != 0)
+			continue;
+
+		if (found) printf("\n");
+
+		if (!rulenum)
+		    print_header(format, this, handle);
+		i = ip6tc_first_rule(this, handle);
+
+		num = 0;
+		while (i) {
+			num++;
+			if (!rulenum || num == rulenum)
+				print_firewall(i,
+					       ip6tc_get_target(i, handle),
+					       num,
+					       format,
+					       handle);
+			i = ip6tc_next_rule(i, handle);
+		}
+		found = 1;
+	}
+
+	errno = ENOENT;
+	return found;
+}
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+	    int invert)
+{
+	unsigned int i;
+
+	if (mask[0] == 0)
+		return;
+
+	printf("%s -%c ", invert ? " !" : "", letter);
+
+	for (i = 0; i < IFNAMSIZ; i++) {
+		if (mask[i] != 0) {
+			if (iface[i] != '\0')
+				printf("%c", iface[i]);
+		} else {
+			/* we can access iface[i-1] here, because
+			 * a few lines above we make sure that mask[0] != 0 */
+			if (iface[i-1] != '\0')
+				printf("+");
+			break;
+		}
+	}
+}
+
+/* The ip6tables looks up the /etc/protocols. */
+static void print_proto(uint16_t proto, int invert)
+{
+	if (proto) {
+		unsigned int i;
+		const char *invertstr = invert ? " !" : "";
+
+		const struct protoent *pent = getprotobynumber(proto);
+		if (pent) {
+			printf("%s -p %s",
+			       invertstr, pent->p_name);
+			return;
+		}
+
+		for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
+			if (xtables_chain_protos[i].num == proto) {
+				printf("%s -p %s",
+				       invertstr, xtables_chain_protos[i].name);
+				return;
+			}
+
+		printf("%s -p %u", invertstr, proto);
+	}
+}
+
+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",
+			match->alias ? match->alias(e) : e->u.user.name);
+
+		/* some matches don't provide a save function */
+		if (match->save)
+			match->save(ip, e);
+	} else {
+		if (e->u.match_size) {
+			fprintf(stderr,
+				"Can't find library for match `%s'\n",
+				e->u.user.name);
+			exit(1);
+		}
+	}
+	return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(const char *prefix, const struct in6_addr *ip,
+		     const struct in6_addr *mask, int invert)
+{
+	char buf[51];
+	int l = xtables_ip6mask_to_cidr(mask);
+
+	if (l == 0 && !invert)
+		return;
+
+	printf("%s %s %s",
+		invert ? " !" : "",
+		prefix,
+		inet_ntop(AF_INET6, ip, buf, sizeof buf));
+
+	if (l == -1)
+		printf("/%s", inet_ntop(AF_INET6, mask, buf, sizeof buf));
+	else
+		printf("/%d", l);
+}
+
+/* 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 xtc_handle *h, const char *chain, int counters)
+{
+	const struct xt_entry_target *t;
+	const char *target_name;
+
+	/* print counters for iptables-save */
+	if (counters > 0)
+		printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+	/* print chain name */
+	printf("-A %s", chain);
+
+	/* Print IP part. */
+	print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
+			e->ipv6.invflags & IP6T_INV_SRCIP);
+
+	print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
+			e->ipv6.invflags & IP6T_INV_DSTIP);
+
+	print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
+		    e->ipv6.invflags & IP6T_INV_VIA_IN);
+
+	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 & XT_INV_PROTO);
+
+#if 0
+	/* not definied in ipv6
+	 * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
+	if (e->ipv6.flags & IPT_F_FRAG)
+		printf("%s -f",
+		       e->ipv6.invflags & IP6T_INV_FRAG ? " !" : "");
+#endif
+
+	if (e->ipv6.flags & IP6T_F_TOS)
+		printf("%s -? %d",
+		       e->ipv6.invflags & IP6T_INV_TOS ? " !" : "",
+		       e->ipv6.tos);
+
+	/* Print matchinfo part */
+	if (e->target_offset) {
+		IP6T_MATCH_ITERATE(e, print_match_save, &e->ipv6);
+	}
+
+	/* print counters for iptables -R */
+	if (counters < 0)
+		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+	/* Print target name and targinfo part */
+	target_name = ip6tc_get_target(e, h);
+	t = ip6t_get_target((struct ip6t_entry *)e);
+	if (t->u.user.name[0]) {
+		struct xtables_target *target =
+			xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
+
+		if (!target) {
+			fprintf(stderr, "Can't find library for target `%s'\n",
+				t->u.user.name);
+			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 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 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 xt_chainlabel chain, int rulenum, int counters,
+	     struct xtc_handle *handle)
+{
+	const char *this = NULL;
+	int found = 0;
+
+	if (counters)
+	    counters = -1;		/* iptables -c format */
+
+	/* Dump out chain names first,
+	 * thereby preventing dependency conflicts */
+	if (!rulenum) for (this = ip6tc_first_chain(handle);
+	     this;
+	     this = ip6tc_next_chain(handle)) {
+		if (chain && strcmp(this, chain) != 0)
+			continue;
+
+		if (ip6tc_builtin(this, handle)) {
+			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);
+			printf("\n");
+		} else {
+			printf("-N %s\n", this);
+		}
+	}
+
+	for (this = ip6tc_first_chain(handle);
+	     this;
+	     this = ip6tc_next_chain(handle)) {
+		const struct ip6t_entry *e;
+		int num = 0;
+
+		if (chain && strcmp(this, chain) != 0)
+			continue;
+
+		/* Dump out rules */
+		e = ip6tc_first_rule(this, handle);
+		while(e) {
+			num++;
+			if (!rulenum || num == rulenum)
+			    print_rule6(e, handle, this, counters);
+			e = ip6tc_next_rule(e, handle);
+		}
+		found = 1;
+	}
+
+	errno = ENOENT;
+	return found;
+}
+
+static struct ip6t_entry *
+generate_entry(const struct ip6t_entry *fw,
+	       struct xtables_rule_match *matches,
+	       struct xt_entry_target *target)
+{
+	unsigned int size;
+	struct xtables_rule_match *matchp;
+	struct ip6t_entry *e;
+
+	size = sizeof(struct ip6t_entry);
+	for (matchp = matches; matchp; matchp = matchp->next)
+		size += matchp->match->m->u.match_size;
+
+	e = xtables_malloc(size + target->u.target_size);
+	*e = *fw;
+	e->target_offset = size;
+	e->next_offset = size + target->u.target_size;
+
+	size = 0;
+	for (matchp = matches; matchp; matchp = matchp->next) {
+		memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+		size += matchp->match->m->u.match_size;
+	}
+	memcpy(e->elems + size, target, target->u.target_size);
+
+	return e;
+}
+
+static void command_jump(struct iptables_command_state *cs)
+{
+	size_t size;
+
+	set_option(&cs->options, OPT_JUMP, &cs->fw6.ipv6.invflags, cs->invert);
+	cs->jumpto = parse_target(optarg);
+	/* TRY_LOAD (may be chain name) */
+	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+
+	if (cs->target == NULL)
+		return;
+
+	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;
+	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;
+
+	xs_init_target(cs->target);
+	if (cs->target->x6_options != NULL)
+		opts = xtables_options_xfrm(ip6tables_globals.orig_opts, opts,
+					    cs->target->x6_options,
+					    &cs->target->option_offset);
+	else
+		opts = xtables_merge_options(ip6tables_globals.orig_opts, opts,
+					     cs->target->extra_opts,
+					     &cs->target->option_offset);
+	if (opts == NULL)
+		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+}
+
+static void command_match(struct iptables_command_state *cs)
+{
+	struct xtables_match *m;
+	size_t size;
+
+	if (cs->invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "unexpected ! flag before --match");
+
+	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
+	m->m = xtables_calloc(1, size);
+	m->m->u.match_size = size;
+	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;
+
+	xs_init_match(m);
+	if (m == m->next)
+		return;
+	/* Merge options for non-cloned matches */
+	if (m->x6_options != NULL)
+		opts = xtables_options_xfrm(ip6tables_globals.orig_opts, opts,
+					    m->x6_options, &m->option_offset);
+	else if (m->extra_opts != NULL)
+		opts = xtables_merge_options(ip6tables_globals.orig_opts, opts,
+					     m->extra_opts, &m->option_offset);
+}
+
+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;
+	unsigned int nsaddrs = 0, ndaddrs = 0;
+	struct in6_addr *saddrs = NULL, *daddrs = NULL;
+	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;
+	unsigned int rulenum = 0, command = 0;
+	const char *pcnt = NULL, *bcnt = NULL;
+	int ret = 1;
+	struct xtables_match *m;
+	struct xtables_rule_match *matchp;
+	struct xtables_target *t;
+	unsigned long long cnt;
+
+	memset(&cs, 0, sizeof(cs));
+	cs.jumpto = "";
+	cs.argv = argv;
+
+	/* re-set optind to 0 in case do_command6 gets called
+	 * a second time */
+	optind = 0;
+
+	/* clear mflags in case do_command6 gets called a second time
+	 * (we clear the global list of all matches for security)*/
+	for (m = xtables_matches; m; m = m->next)
+		m->mflags = 0;
+
+	for (t = xtables_targets; t; t = t->next) {
+		t->tflags = 0;
+		t->used = 0;
+	}
+
+	/* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+	opterr = 0;
+
+	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:bvwnt:m:xc:g:46",
+					   opts, NULL)) != -1) {
+		switch (cs.c) {
+			/*
+			 * Command selection
+			 */
+		case 'A':
+			add_command(&command, CMD_APPEND, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			break;
+
+		case 'C':
+			add_command(&command, CMD_CHECK, CMD_NONE,
+			            cs.invert);
+			chain = optarg;
+			break;
+
+		case 'D':
+			add_command(&command, CMD_DELETE, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!') {
+				rulenum = parse_rulenumber(argv[optind++]);
+				command = CMD_DELETE_NUM;
+			}
+			break;
+
+		case 'R':
+			add_command(&command, CMD_REPLACE, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires a rule number",
+					   cmd2char(CMD_REPLACE));
+			break;
+
+		case 'I':
+			add_command(&command, CMD_INSERT, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else rulenum = 1;
+			break;
+
+		case 'L':
+			add_command(&command, CMD_LIST,
+				    CMD_ZERO | CMD_ZERO_NUM, cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			break;
+
+		case 'S':
+			add_command(&command, CMD_LIST_RULES,
+				    CMD_ZERO | CMD_ZERO_NUM, cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			break;
+
+		case 'F':
+			add_command(&command, CMD_FLUSH, CMD_NONE,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'Z':
+			add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				&& argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+				&& argv[optind][0] != '!') {
+				rulenum = parse_rulenumber(argv[optind++]);
+				command = CMD_ZERO_NUM;
+			}
+			break;
+
+		case 'N':
+			parse_chain(optarg);
+			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			break;
+
+		case 'X':
+			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'E':
+			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				newname = argv[optind++];
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires old-chain-name and "
+					   "new-chain-name",
+					    cmd2char(CMD_RENAME_CHAIN));
+			break;
+
+		case 'P':
+			add_command(&command, CMD_SET_POLICY, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				policy = argv[optind++];
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires a chain and a policy",
+					   cmd2char(CMD_SET_POLICY));
+			break;
+
+		case 'h':
+			if (!optarg)
+				optarg = argv[optind];
+
+			/* ip6tables -p icmp -h */
+			if (!cs.matches && cs.protocol)
+				xtables_find_match(cs.protocol, XTF_TRY_LOAD,
+					&cs.matches);
+
+			exit_printhelp(cs.matches);
+
+			/*
+			 * Option selection
+			 */
+		case 'p':
+			set_option(&cs.options, OPT_PROTOCOL, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+
+			/* Canonicalize into lower case */
+			for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
+				*cs.protocol = tolower(*cs.protocol);
+
+			cs.protocol = optarg;
+			cs.fw6.ipv6.proto = xtables_parse_protocol(cs.protocol);
+			cs.fw6.ipv6.flags |= IP6T_F_PROTO;
+
+			if (cs.fw6.ipv6.proto == 0
+			    && (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 & XT_INV_PROTO) == 0)
+				fprintf(stderr,
+					"Warning: never matched protocol: %s. "
+					"use extension match instead.\n",
+					cs.protocol);
+			break;
+
+		case 's':
+			set_option(&cs.options, OPT_SOURCE, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			shostnetworkmask = optarg;
+			break;
+
+		case 'd':
+			set_option(&cs.options, OPT_DESTINATION, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			dhostnetworkmask = optarg;
+			break;
+
+#ifdef IP6T_F_GOTO
+		case 'g':
+			set_option(&cs.options, OPT_JUMP, &cs.fw6.ipv6.invflags,
+					cs.invert);
+			cs.fw6.ipv6.flags |= IP6T_F_GOTO;
+			cs.jumpto = parse_target(optarg);
+			break;
+#endif
+
+		case 'j':
+			command_jump(&cs);
+			break;
+
+
+		case 'i':
+			if (*optarg == '\0')
+				xtables_error(PARAMETER_PROBLEM,
+					"Empty interface is likely to be "
+					"undesired");
+			set_option(&cs.options, OPT_VIANAMEIN, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			xtables_parse_interface(optarg,
+					cs.fw6.ipv6.iniface,
+					cs.fw6.ipv6.iniface_mask);
+			break;
+
+		case 'o':
+			if (*optarg == '\0')
+				xtables_error(PARAMETER_PROBLEM,
+					"Empty interface is likely to be "
+					"undesired");
+			set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			xtables_parse_interface(optarg,
+					cs.fw6.ipv6.outiface,
+					cs.fw6.ipv6.outiface_mask);
+			break;
+
+		case 'v':
+			if (!verbose)
+				set_option(&cs.options, OPT_VERBOSE,
+					   &cs.fw6.ipv6.invflags, cs.invert);
+			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;
+
+		case 'n':
+			set_option(&cs.options, OPT_NUMERIC, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			break;
+
+		case 't':
+			if (cs.invert)
+				xtables_error(PARAMETER_PROBLEM,
+					   "unexpected ! flag before --table");
+			*table = optarg;
+			break;
+
+		case 'x':
+			set_option(&cs.options, OPT_EXPANDED, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			break;
+
+		case 'V':
+			if (cs.invert)
+				printf("Not %s ;-)\n", prog_vers);
+			else
+				printf("%s v%s\n",
+				       prog_name, prog_vers);
+			exit(0);
+
+		case '0':
+			set_option(&cs.options, OPT_LINENUMBERS, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			break;
+
+		case 'M':
+			xtables_modprobe_program = optarg;
+			break;
+
+		case 'c':
+
+			set_option(&cs.options, OPT_COUNTERS, &cs.fw6.ipv6.invflags,
+				   cs.invert);
+			pcnt = optarg;
+			bcnt = strchr(pcnt + 1, ',');
+			if (bcnt)
+			    bcnt++;
+			if (!bcnt && optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				bcnt = argv[optind++];
+			if (!bcnt)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c requires packet and byte counter",
+					opt2char(OPT_COUNTERS));
+
+			if (sscanf(pcnt, "%llu", &cnt) != 1)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c packet counter not numeric",
+					opt2char(OPT_COUNTERS));
+			cs.fw6.counters.pcnt = cnt;
+
+			if (sscanf(bcnt, "%llu", &cnt) != 1)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c byte counter not numeric",
+					opt2char(OPT_COUNTERS));
+			cs.fw6.counters.bcnt = cnt;
+			break;
+
+		case '4':
+			/* This is not the IPv4 iptables */
+			if (line != -1)
+				return 1; /* success: line ignored */
+			fprintf(stderr, "This is the IPv6 version of ip6tables.\n");
+			exit_tryhelp(2);
+
+		case '6':
+			/* This is indeed the IPv6 ip6tables */
+			break;
+
+		case 1: /* non option */
+			if (optarg[0] == '!' && optarg[1] == '\0') {
+				if (cs.invert)
+					xtables_error(PARAMETER_PROBLEM,
+						   "multiple consecutive ! not"
+						   " allowed");
+				cs.invert = TRUE;
+				optarg[0] = '\0';
+				continue;
+			}
+			fprintf(stderr, "Bad argument `%s'\n", optarg);
+			exit_tryhelp(2);
+
+		default:
+			if (command_default(&cs, &ip6tables_globals) == 1)
+				/*
+				 * If new options were loaded, we must retry
+				 * getopt immediately and not allow
+				 * cs.invert=FALSE to be executed.
+				 */
+				continue;
+			break;
+		}
+		cs.invert = FALSE;
+	}
+
+	for (matchp = cs.matches; matchp; matchp = matchp->next)
+		xtables_option_mfcall(matchp->match);
+	if (cs.target != NULL)
+		xtables_option_tfcall(cs.target);
+
+	/* Fix me: must put inverse options checking here --MN */
+
+	if (optind < argc)
+		xtables_error(PARAMETER_PROBLEM,
+			   "unknown arguments found on commandline");
+	if (!command)
+		xtables_error(PARAMETER_PROBLEM, "no command specified");
+	if (cs.invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "nothing appropriate following !");
+
+	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
+		if (!(cs.options & OPT_DESTINATION))
+			dhostnetworkmask = "::0/0";
+		if (!(cs.options & OPT_SOURCE))
+			shostnetworkmask = "::0/0";
+	}
+
+	if (shostnetworkmask)
+		xtables_ip6parse_multiple(shostnetworkmask, &saddrs,
+					  &smasks, &nsaddrs);
+
+	if (dhostnetworkmask)
+		xtables_ip6parse_multiple(dhostnetworkmask, &daddrs,
+					  &dmasks, &ndaddrs);
+
+	if ((nsaddrs > 1 || ndaddrs > 1) &&
+	    (cs.fw6.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
+		xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+			   " source or destination IP addresses");
+
+	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
+			   "specify a unique address");
+
+	generic_opt_check(command, cs.options);
+
+	/* 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);
+
+	/* try to insmod the module if iptc_init failed */
+	if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1)
+		*handle = ip6tc_init(*table);
+
+	if (!*handle)
+		xtables_error(VERSION_PROBLEM,
+			"can't initialize ip6tables table `%s': %s",
+			*table, ip6tc_strerror(errno));
+
+	if (command == CMD_APPEND
+	    || command == CMD_DELETE
+	    || command == CMD_CHECK
+	    || command == CMD_INSERT
+	    || command == CMD_REPLACE) {
+		if (strcmp(chain, "PREROUTING") == 0
+		    || strcmp(chain, "INPUT") == 0) {
+			/* -o not valid with incoming packets. */
+			if (cs.options & OPT_VIANAMEOUT)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEOUT),
+					   chain);
+		}
+
+		if (strcmp(chain, "POSTROUTING") == 0
+		    || strcmp(chain, "OUTPUT") == 0) {
+			/* -i not valid with outgoing packets */
+			if (cs.options & OPT_VIANAMEIN)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEIN),
+					   chain);
+		}
+
+		if (cs.target && ip6tc_is_chain(cs.jumpto, *handle)) {
+			fprintf(stderr,
+				"Warning: using chain %s, not extension\n",
+				cs.jumpto);
+
+			if (cs.target->t)
+				free(cs.target->t);
+
+			cs.target = NULL;
+		}
+
+		/* If they didn't specify a target, or it's a chain
+		   name, use standard. */
+		if (!cs.target
+		    && (strlen(cs.jumpto) == 0
+			|| ip6tc_is_chain(cs.jumpto, *handle))) {
+			size_t size;
+
+			cs.target = xtables_find_target(XT_STANDARD_TARGET,
+					XTF_LOAD_MUST_SUCCEED);
+
+			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);
+			xs_init_target(cs.target);
+		}
+
+		if (!cs.target) {
+			/* it is no chain, and we can't load a plugin.
+			 * We cannot know if the plugin is corrupt, non
+			 * existant OR if the user just misspelled a
+			 * chain. */
+#ifdef IP6T_F_GOTO
+			if (cs.fw6.ipv6.flags & IP6T_F_GOTO)
+				xtables_error(PARAMETER_PROBLEM,
+						"goto '%s' is not a chain\n",
+						cs.jumpto);
+#endif
+			xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
+		} else {
+			e = generate_entry(&cs.fw6, cs.matches, cs.target->t);
+			free(cs.target->t);
+		}
+	}
+
+	switch (command) {
+	case CMD_APPEND:
+		ret = append_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		break;
+	case CMD_DELETE:
+		ret = delete_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle, cs.matches, cs.target);
+		break;
+	case CMD_DELETE_NUM:
+		ret = ip6tc_delete_num_entry(chain, rulenum - 1, *handle);
+		break;
+	case CMD_CHECK:
+		ret = check_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle, cs.matches, cs.target);
+		break;
+	case CMD_REPLACE:
+		ret = replace_entry(chain, e, rulenum - 1,
+				    saddrs, smasks, daddrs, dmasks,
+				    cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_INSERT:
+		ret = insert_entry(chain, e, rulenum - 1,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		break;
+	case CMD_FLUSH:
+		ret = flush_entries6(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_ZERO:
+		ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_ZERO_NUM:
+		ret = ip6tc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_LIST:
+	case CMD_LIST|CMD_ZERO:
+	case CMD_LIST|CMD_ZERO_NUM:
+		ret = list_entries(chain,
+				   rulenum,
+				   cs.options&OPT_VERBOSE,
+				   cs.options&OPT_NUMERIC,
+				   cs.options&OPT_EXPANDED,
+				   cs.options&OPT_LINENUMBERS,
+				   *handle);
+		if (ret && (command & CMD_ZERO))
+			ret = zero_entries(chain,
+					   cs.options&OPT_VERBOSE, *handle);
+		if (ret && (command & CMD_ZERO_NUM))
+			ret = ip6tc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_LIST_RULES:
+	case CMD_LIST_RULES|CMD_ZERO:
+	case CMD_LIST_RULES|CMD_ZERO_NUM:
+		ret = list_rules(chain,
+				   rulenum,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		if (ret && (command & CMD_ZERO))
+			ret = zero_entries(chain,
+					   cs.options&OPT_VERBOSE, *handle);
+		if (ret && (command & CMD_ZERO_NUM))
+			ret = ip6tc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_NEW_CHAIN:
+		ret = ip6tc_create_chain(chain, *handle);
+		break;
+	case CMD_DELETE_CHAIN:
+		ret = delete_chain6(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_RENAME_CHAIN:
+		ret = ip6tc_rename_chain(chain, newname,	*handle);
+		break;
+	case CMD_SET_POLICY:
+		ret = ip6tc_set_policy(chain, policy, cs.options&OPT_COUNTERS ? &cs.fw6.counters : NULL, *handle);
+		break;
+	default:
+		/* We should never reach this... */
+		exit_tryhelp(2);
+	}
+
+	if (verbose > 1)
+		dump_entries6(*handle);
+
+	xtables_rule_matches_free(&cs.matches);
+
+	if (e != NULL) {
+		free(e);
+		e = NULL;
+	}
+
+	free(saddrs);
+	free(smasks);
+	free(daddrs);
+	free(dmasks);
+	xtables_free_opts(1);
+
+	return ret;
+}
diff --git a/iptables/iptables-apply b/iptables/iptables-apply
new file mode 100755
index 0000000..86b8d5a
--- /dev/null
+++ b/iptables/iptables-apply
@@ -0,0 +1,175 @@
+#!/bin/bash
+#
+# iptables-apply -- a safer way to update iptables remotely
+#
+# Copyright © Martin F. Krafft <madduck@madduck.net>
+# Released under the terms of the Artistic Licence 2.0
+#
+set -eu
+
+PROGNAME="${0##*/}";
+VERSION=1.0
+
+TIMEOUT=10
+
+function blurb()
+{
+	cat <<-_eof
+	$PROGNAME $VERSION -- a safer way to update iptables remotely
+	_eof
+}
+
+function copyright()
+{
+	cat <<-_eof
+	$PROGNAME is C Martin F. Krafft <madduck@madduck.net>.
+
+	The program has been published under the terms of the Artistic Licence 2.0
+	_eof
+}
+
+function about()
+{
+	blurb
+	echo
+	copyright
+}
+
+function usage()
+{
+	cat <<-_eof
+	Usage: $PROGNAME [options] ruleset
+
+	The script will try to apply a new ruleset (as output by iptables-save/read
+	by iptables-restore) to iptables, then prompt the user whether the changes
+	are okay. If the new ruleset cut the existing connection, the user will not
+	be able to answer affirmatively. In this case, the script rolls back to the
+	previous ruleset.
+
+	The following options may be specified, using standard conventions:
+
+	-t | --timeout	Specify the timeout in seconds (default: $TIMEOUT)
+	-V | --version	Display version information
+	-h | --help	Display this help text
+	_eof
+}
+
+SHORTOPTS="t:Vh";
+LONGOPTS="timeout:,version,help";
+
+OPTS=$(getopt -s bash -o "$SHORTOPTS" -l "$LONGOPTS" -n "$PROGNAME" -- "$@") || exit $?
+for opt in $OPTS; do
+	case "$opt" in
+		(-*) unset OPT_STATE;;
+		(*)
+			case "${OPT_STATE:-}" in
+				(SET_TIMEOUT)
+					eval TIMEOUT=$opt
+					case "$TIMEOUT" in
+						([0-9]*) :;;
+						(*)
+							echo "E: non-numeric timeout value." >&2
+							exit 1
+							;;
+					esac
+					;;
+			esac
+			;;
+	esac
+
+	case "$opt" in
+		(-h|--help) usage >&2; exit 0;;
+		(-V|--version) about >&2; exit 0;;
+		(-t|--timeout) OPT_STATE=SET_TIMEOUT;;
+		(--) break;;
+	esac
+	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
+	echo "E: missing file argument." >&2
+	exit 1
+fi
+
+if [[ ! -r "$FILE" ]]; then
+	echo "E: cannot read $FILE" >&2
+	exit 2
+fi
+
+COMMANDS=(tempfile "$SAVE" "$RESTORE")
+
+for cmd in "${COMMANDS[@]}"; do
+	if ! command -v $cmd >/dev/null; then
+		echo "E: command not found: $cmd" >&2
+		exit 127
+	fi
+done
+
+umask 0700
+
+TMPFILE=$(tempfile -p iptap)
+trap "rm -f $TMPFILE" EXIT 1 2 3 4 5 6 7 8 10 11 12 13 14 15
+
+if ! "$SAVE" >"$TMPFILE"; then
+	if ! grep -q ipt /proc/modules 2>/dev/null; then
+		echo "E: iptables support lacking from the kernel." >&2
+		exit 3
+	else
+		echo "E: unknown error saving current iptables ruleset." >&2
+		exit 4
+	fi
+fi
+
+[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban stop
+
+echo -n "Applying new ruleset... "
+if ! "$RESTORE" <"$FILE"; then
+	echo "failed."
+	echo "E: unknown error applying new iptables ruleset." >&2
+	exit 5
+else
+	echo done.
+fi
+
+echo -n "Can you establish NEW connections to the machine? (y/N) "
+
+read -n1 -t "${TIMEOUT:-15}" ret 2>&1 || :
+case "${ret:-}" in
+	(y*|Y*)
+		echo
+		echo ... then my job is done. See you next time.
+		;;
+	(*)
+		if [[ -z "${ret:-}" ]]; then
+			echo "apparently not..."
+		else
+			echo
+		fi
+		echo "Timeout. Something happened (or did not). Better play it safe..."
+		echo -n "Reverting to old ruleset... "
+		"$RESTORE" <"$TMPFILE";
+		echo done.
+		exit 255
+		;;
+esac
+
+[ -x /etc/init.d/fail2ban ] && /etc/init.d/fail2ban start
+
+exit 0
+
+# vim:noet:sw=8
diff --git a/iptables/iptables-apply.8.in b/iptables/iptables-apply.8.in
new file mode 100644
index 0000000..cdc9c44
--- /dev/null
+++ b/iptables/iptables-apply.8.in
@@ -0,0 +1,44 @@
+.\"     Title: iptables-apply
+.\"    Author: Martin F. Krafft
+.\"      Date: Jun 04, 2006
+.\"
+.TH IPTABLES\-APPLY 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.\" disable hyphenation
+.nh
+.SH NAME
+iptables-apply \- a safer way to update iptables remotely
+.SH SYNOPSIS
+\fBiptables\-apply\fP [\-\fBhV\fP] [\fB-t\fP \fItimeout\fP] \fIruleset\-file\fP
+.SH "DESCRIPTION"
+.PP
+iptables\-apply will try to apply a new ruleset (as output by
+iptables\-save/read by iptables\-restore) to iptables, then prompt the
+user whether the changes are okay. If the new ruleset cut the existing
+connection, the user will not be able to answer affirmatively. In this
+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 \fBip6tables\-apply\fP, the script will use
+ip6tables\-save/\-restore instead.
+.SH OPTIONS
+.TP
+\fB\-t\fP \fIseconds\fR, \fB\-\-timeout\fP \fIseconds\fR
+Sets the timeout after which the script will roll back to the previous
+ruleset.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Display usage information.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Display version information.
+.SH "SEE ALSO"
+.PP
+\fBiptables-restore\fP(8), \fBiptables-save\fP(8), \fBiptables\fR(8).
+.SH LEGALESE
+.PP
+iptables\-apply is copyright by Martin F. Krafft.
+.PP
+This manual page was written by Martin F. Krafft <madduck@madduck.net>
+.PP
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the Artistic License 2.0.
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-multi.h b/iptables/iptables-multi.h
new file mode 100644
index 0000000..a2bb878
--- /dev/null
+++ b/iptables/iptables-multi.h
@@ -0,0 +1,8 @@
+#ifndef _IPTABLES_MULTI_H
+#define _IPTABLES_MULTI_H 1
+
+extern int iptables_main(int, char **);
+extern int iptables_save_main(int, char **);
+extern int iptables_restore_main(int, char **);
+
+#endif /* _IPTABLES_MULTI_H */
diff --git a/iptables/iptables-restore.8.in b/iptables/iptables-restore.8.in
new file mode 100644
index 0000000..feb57ef
--- /dev/null
+++ b/iptables/iptables-restore.8.in
@@ -0,0 +1,72 @@
+.TH IPTABLES-RESTORE 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"	This program is free software; you can redistribute it and/or modify
+.\"	it under the terms of the GNU General Public License as published by
+.\"	the Free Software Foundation; either version 2 of the License, or
+.\"	(at your option) any later version.
+.\"
+.\"	This program is distributed in the hope that it will be useful,
+.\"	but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"	GNU General Public License for more details.
+.\"
+.\"	You should have received a copy of the GNU General Public License
+.\"	along with this program; if not, write to the Free Software
+.\"	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables-restore \(em Restore IP Tables
+.P
+ip6tables-restore \(em Restore IPv6 Tables
+.SH SYNOPSIS
+\fBiptables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+.P
+\fBip6tables\-restore\fP [\fB\-chntv\fP] [\fB\-M\fP \fImodprobe\fP]
+[\fB\-T\fP \fIname\fP]
+.SH DESCRIPTION
+.PP
+.B iptables-restore
+and
+.B ip6tables-restore
+are used to restore IP and IPv6 Tables from data specified on STDIN. Use
+I/O redirection provided by your shell to read from a file
+.TP
+\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,
+both commands flush (delete) 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 AUTHORS
+Harald Welte <laforge@gnumonks.org> wrote iptables-restore based on code
+from Rusty Russell.
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-restore.
+.SH SEE ALSO
+\fBiptables\-save\fP(8), \fBiptables\fP(8)
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
new file mode 100644
index 0000000..8c942ff
--- /dev/null
+++ b/iptables/iptables-restore.c
@@ -0,0 +1,462 @@
+/* Code to restore the iptables state, from file by iptables-save.
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "xtables.h"
+#include "libiptc/libiptc.h"
+#include "iptables-multi.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static const struct option options[] = {
+	{.name = "binary",   .has_arg = false, .val = 'b'},
+	{.name = "counters", .has_arg = false, .val = 'c'},
+	{.name = "verbose",  .has_arg = false, .val = 'v'},
+	{.name = "test",     .has_arg = false, .val = 't'},
+	{.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},
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+#define prog_name iptables_globals.program_name
+
+static void print_usage(const char *name, const char *version)
+{
+	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
+			"	   [ --binary ]\n"
+			"	   [ --counters ]\n"
+			"	   [ --verbose ]\n"
+			"	   [ --test ]\n"
+			"	   [ --help ]\n"
+			"	   [ --noflush ]\n"
+			"	   [ --table=<TABLE> ]\n"
+			"          [ --modprobe=<command>]\n", name);
+
+	exit(1);
+}
+
+static struct xtc_handle *create_handle(const char *tablename)
+{
+	struct xtc_handle *handle;
+
+	handle = iptc_init(tablename);
+
+	if (!handle) {
+		/* try to insmod the module if iptc_init failed */
+		xtables_load_ko(xtables_modprobe_program, false);
+		handle = iptc_init(tablename);
+	}
+
+	if (!handle) {
+		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
+			"table '%s'\n", prog_name, tablename);
+		exit(1);
+	}
+	return handle;
+}
+
+static int parse_counters(char *string, struct xt_counters *ctr)
+{
+	unsigned long long pcnt, bcnt;
+	int ret;
+
+	ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
+	ctr->pcnt = pcnt;
+	ctr->bcnt = bcnt;
+	return ret == 2;
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+	DEBUGP("add_argv: %s\n", what);
+	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
+		newargv[newargc] = strdup(what);
+		newargv[++newargc] = NULL;
+		return 1;
+	} else {
+		xtables_error(PARAMETER_PROBLEM,
+			"Parser cannot handle more arguments\n");
+		return 0;
+	}
+}
+
+static void free_argv(void) {
+	int i;
+
+	for (i = 0; i < newargc; i++)
+		free(newargv[i]);
+}
+
+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[])
+{
+	struct xtc_handle *handle = NULL;
+	char buffer[10240];
+	int c;
+	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;
+
+	iptables_globals.program_name = "iptables-restore";
+	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+	if (c < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				iptables_globals.program_name,
+				iptables_globals.program_version);
+		exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions4();
+#endif
+
+	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
+		switch (c) {
+			case 'b':
+				binary = 1;
+				break;
+			case 'c':
+				counters = 1;
+				break;
+			case 'v':
+				verbose = 1;
+				break;
+			case 't':
+				testing = 1;
+				break;
+			case 'h':
+				print_usage("iptables-restore",
+					    IPTABLES_VERSION);
+				break;
+			case 'n':
+				noflush = 1;
+				break;
+			case 'M':
+				xtables_modprobe_program = optarg;
+				break;
+			case 'T':
+				tablename = optarg;
+				break;
+		}
+	}
+
+	if (optind == argc - 1) {
+		in = fopen(argv[optind], "re");
+		if (!in) {
+			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
+				strerror(errno));
+			exit(1);
+		}
+	}
+	else if (optind < argc) {
+		fprintf(stderr, "Unknown arguments found on commandline\n");
+		exit(1);
+	}
+	else in = stdin;
+
+	/* Grab standard input. */
+	while (fgets(buffer, sizeof(buffer), in)) {
+		int ret = 0;
+
+		line++;
+		if (buffer[0] == '\n')
+			continue;
+		else if (buffer[0] == '#') {
+			if (verbose)
+				fputs(buffer, stdout);
+			continue;
+		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+			if (!testing) {
+				DEBUGP("Calling commit\n");
+				ret = ops->commit(handle);
+				ops->free(handle);
+				handle = NULL;
+			} else {
+				DEBUGP("Not calling commit, testing\n");
+				ret = 1;
+			}
+			in_table = 0;
+		} else if ((buffer[0] == '*') && (!in_table)) {
+			/* New table */
+			char *table;
+
+			table = strtok(buffer+1, " \t\n");
+			DEBUGP("line %u, table '%s'\n", line, table);
+			if (!table) {
+				xtables_error(PARAMETER_PROBLEM,
+					"%s: line %u table name invalid\n",
+					xt_params->program_name, line);
+				exit(1);
+			}
+			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
+			curtable[XT_TABLE_MAXNAMELEN] = '\0';
+
+			if (tablename && (strcmp(tablename, table) != 0))
+				continue;
+			if (handle)
+				ops->free(handle);
+
+			handle = create_handle(table);
+			if (noflush == 0) {
+				DEBUGP("Cleaning all chains of table '%s'\n",
+					table);
+				for_each_chain4(flush_entries4, verbose, 1,
+						handle);
+
+				DEBUGP("Deleting all user-defined chains "
+				       "of table '%s'\n", table);
+				for_each_chain4(delete_chain4, verbose, 0,
+						handle);
+			}
+
+			ret = 1;
+			in_table = 1;
+
+		} else if ((buffer[0] == ':') && (in_table)) {
+			/* New chain. */
+			char *policy, *chain;
+
+			chain = strtok(buffer+1, " \t\n");
+			DEBUGP("line %u, chain '%s'\n", line, chain);
+			if (!chain) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u chain name invalid\n",
+					   xt_params->program_name, line);
+				exit(1);
+			}
+
+			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Invalid chain name `%s' "
+					   "(%u chars max)",
+					   chain, XT_EXTENSION_MAXNAMELEN - 1);
+
+			if (ops->builtin(chain, handle) <= 0) {
+				if (noflush && ops->is_chain(chain, handle)) {
+					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
+					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 (!ops->create_chain(chain, handle))
+						xtables_error(PARAMETER_PROBLEM,
+							   "error creating chain "
+							   "'%s':%s\n", chain,
+							   strerror(errno));
+				}
+			}
+
+			policy = strtok(NULL, " \t\n");
+			DEBUGP("line %u, policy '%s'\n", line, policy);
+			if (!policy) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u policy invalid\n",
+					   xt_params->program_name, line);
+				exit(1);
+			}
+
+			if (strcmp(policy, "-") != 0) {
+				struct xt_counters count;
+
+				if (counters) {
+					char *ctrs;
+					ctrs = strtok(NULL, " \t\n");
+
+					if (!ctrs || !parse_counters(ctrs, &count))
+						xtables_error(PARAMETER_PROBLEM,
+							   "invalid policy counters "
+							   "for chain '%s'\n", chain);
+
+				} else {
+					memset(&count, 0, sizeof(count));
+				}
+
+				DEBUGP("Setting policy of chain %s to %s\n",
+					chain, policy);
+
+				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,
+						ops->strerror(errno));
+			}
+
+			ret = 1;
+
+		} else if (in_table) {
+			int a;
+			char *ptr = buffer;
+			char *pcnt = NULL;
+			char *bcnt = NULL;
+			char *parsestart;
+
+			/* reset the newargv */
+			newargc = 0;
+
+			if (buffer[0] == '[') {
+				/* we have counters in our input */
+				ptr = strchr(buffer, ']');
+				if (!ptr)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				pcnt = strtok(buffer+1, ":");
+				if (!pcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need :\n",
+						   line);
+
+				bcnt = strtok(NULL, "]");
+				if (!bcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				/* start command parsing after counter */
+				parsestart = ptr + 1;
+			} else {
+				/* start command parsing at start of line */
+				parsestart = buffer;
+			}
+
+			add_argv(argv[0]);
+			add_argv("-t");
+			add_argv(curtable);
+
+			if (counters && pcnt && bcnt) {
+				add_argv("--set-counters");
+				add_argv((char *) pcnt);
+				add_argv((char *) bcnt);
+			}
+
+			add_param_to_argv(parsestart);
+
+			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
+				newargc, curtable);
+
+			for (a = 0; a < newargc; a++)
+				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+			ret = do_command4(newargc, newargv,
+					 &newargv[2], &handle, true);
+
+			free_argv();
+			fflush(stdout);
+		}
+		if (tablename && (strcmp(tablename, curtable) != 0))
+			continue;
+		if (!ret) {
+			fprintf(stderr, "%s: line %u failed\n",
+					xt_params->program_name, line);
+			exit(1);
+		}
+	}
+	if (in_table) {
+		fprintf(stderr, "%s: COMMIT expected at line %u\n",
+				xt_params->program_name, line + 1);
+		exit(1);
+	}
+
+	fclose(in);
+	return 0;
+}
diff --git a/iptables/iptables-save.8.in b/iptables/iptables-save.8.in
new file mode 100644
index 0000000..5e939b1
--- /dev/null
+++ b/iptables/iptables-save.8.in
@@ -0,0 +1,62 @@
+.TH IPTABLES-SAVE 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"	This program is free software; you can redistribute it and/or modify
+.\"	it under the terms of the GNU General Public License as published by
+.\"	the Free Software Foundation; either version 2 of the License, or
+.\"	(at your option) any later version.
+.\"
+.\"	This program is distributed in the hope that it will be useful,
+.\"	but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"	GNU General Public License for more details.
+.\"
+.\"	You should have received a copy of the GNU General Public License
+.\"	along with this program; if not, write to the Free Software
+.\"	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables-save \(em dump iptables rules to stdout
+.P
+ip6tables-save \(em dump iptables rules to stdout
+.SH SYNOPSIS
+\fBiptables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
+[\fB\-t\fP \fItable\fP]
+.P
+\fBip6tables\-save\fP [\fB\-M\fP \fImodprobe\fP] [\fB\-c\fP]
+[\fB\-t\fP \fItable\fP
+.SH DESCRIPTION
+.PP
+.B iptables-save
+and
+.B ip6tables-save
+are used to dump the contents of IP or IPv6 Table in easily parseable format
+to STDOUT. Use I/O-redirection provided by your shell to write to a file.
+.TP
+\fB\-M\fP \fImodprobe_program\fP
+Specify the path to the modprobe program. By default, iptables-save will
+inspect /proc/sys/kernel/modprobe to determine the executable's path.
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+include the current values of all packet and byte counters in the output
+.TP
+\fB\-t\fR, \fB\-\-table\fR \fItablename\fP
+restrict output to only one table. If not specified, output includes all
+available tables.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org>
+.br
+Rusty Russell <rusty@rustcorp.com.au>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu> contributed ip6tables-save.
+.SH SEE ALSO
+\fBiptables\-restore\fP(8), \fBiptables\fP(8)
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
new file mode 100644
index 0000000..e599fce
--- /dev/null
+++ b/iptables/iptables-save.c
@@ -0,0 +1,168 @@
+/* Code to save the iptables state, in human readable-form. */
+/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netdb.h>
+#include "libiptc/libiptc.h"
+#include "iptables.h"
+#include "iptables-multi.h"
+
+#ifndef NO_SHARED_LIBS
+#include <dlfcn.h>
+#endif
+
+static int show_counters = 0;
+
+static const struct option options[] = {
+	{.name = "counters", .has_arg = false, .val = 'c'},
+	{.name = "dump",     .has_arg = false, .val = 'd'},
+	{.name = "table",    .has_arg = true,  .val = 't'},
+	{.name = "modprobe", .has_arg = true,  .val = 'M'},
+	{NULL},
+};
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+	int ret = 1;
+	FILE *procfile = NULL;
+	char tablename[XT_TABLE_MAXNAMELEN+1];
+
+	procfile = fopen("/proc/net/ip_tables_names", "re");
+	if (!procfile)
+		return ret;
+
+	while (fgets(tablename, sizeof(tablename), procfile)) {
+		if (tablename[strlen(tablename) - 1] != '\n')
+			xtables_error(OTHER_PROBLEM,
+				   "Badly formed tablename `%s'\n",
+				   tablename);
+		tablename[strlen(tablename) - 1] = '\0';
+		ret &= func(tablename);
+	}
+
+	fclose(procfile);
+	return ret;
+}
+
+
+static int do_output(const char *tablename)
+{
+	struct xtc_handle *h;
+	const char *chain = NULL;
+
+	if (!tablename)
+		return for_each_table(&do_output);
+
+	h = iptc_init(tablename);
+	if (h == NULL) {
+		xtables_load_ko(xtables_modprobe_program, false);
+		h = iptc_init(tablename);
+	}
+	if (!h)
+		xtables_error(OTHER_PROBLEM, "Cannot initialize: %s\n",
+			   iptc_strerror(errno));
+
+	time_t now = time(NULL);
+
+	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)) {
+
+		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));
+	iptc_free(h);
+
+	return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int
+iptables_save_main(int argc, char *argv[])
+{
+	const char *tablename = NULL;
+	int c;
+
+	iptables_globals.program_name = "iptables-save";
+	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+	if (c < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				iptables_globals.program_name,
+				iptables_globals.program_version);
+		exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions4();
+#endif
+
+	while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+		switch (c) {
+		case 'c':
+			show_counters = 1;
+			break;
+
+		case 't':
+			/* Select specific table. */
+			tablename = optarg;
+			break;
+		case 'M':
+			xtables_modprobe_program = optarg;
+			break;
+		case 'd':
+			do_output(tablename);
+			exit(0);
+		}
+	}
+
+	if (optind < argc) {
+		fprintf(stderr, "Unknown arguments found on commandline\n");
+		exit(1);
+	}
+
+	return !do_output(tablename);
+}
diff --git a/iptables/iptables-standalone.c b/iptables/iptables-standalone.c
new file mode 100644
index 0000000..4da1d7f
--- /dev/null
+++ b/iptables/iptables-standalone.c
@@ -0,0 +1,82 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * 		    Marc Boucher <marc+nf@mbsi.ca>
+ * 		    James Morris <jmorris@intercode.com.au>
+ * 		    Harald Welte <laforge@gnumonks.org>
+ * 		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *	iptables -- IP firewall administration for kernels with
+ *	firewall table (aimed for the 2.3 kernels)
+ *
+ *	See the accompanying manual page iptables(8) for information
+ *	about proper usage of this program.
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+#include "iptables-multi.h"
+
+int
+iptables_main(int argc, char *argv[])
+{
+	int ret;
+	char *table = "filter";
+	struct xtc_handle *handle = NULL;
+
+	iptables_globals.program_name = "iptables";
+	ret = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
+	if (ret < 0) {
+		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
+				iptables_globals.program_name,
+				iptables_globals.program_version);
+				exit(1);
+	}
+#if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
+	init_extensions();
+	init_extensions4();
+#endif
+
+	ret = do_command4(argc, argv, &table, &handle, false);
+	if (ret) {
+		ret = iptc_commit(handle);
+		iptc_free(handle);
+	}
+
+	if (!ret) {
+		if (errno == EINVAL) {
+			fprintf(stderr, "iptables: %s. "
+					"Run `dmesg' for more information.\n",
+				iptc_strerror(errno));
+		} else {
+			fprintf(stderr, "iptables: %s.\n",
+				iptc_strerror(errno));
+		}
+		if (errno == EAGAIN) {
+			exit(RESOURCE_PROBLEM);
+		}
+	}
+
+	exit(!ret);
+}
diff --git a/iptables/iptables-xml.1.in b/iptables/iptables-xml.1.in
new file mode 100644
index 0000000..7b7878f
--- /dev/null
+++ b/iptables/iptables-xml.1.in
@@ -0,0 +1,87 @@
+.TH IPTABLES-XML 1 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.\"
+.\" Man page written by Sam Liddicott <azez@ufomechanic.net>
+.\" It is based on the iptables-save man page.
+.\"
+.\"	This program is free software; you can redistribute it and/or modify
+.\"	it under the terms of the GNU General Public License as published by
+.\"	the Free Software Foundation; either version 2 of the License, or
+.\"	(at your option) any later version.
+.\"
+.\"	This program is distributed in the hope that it will be useful,
+.\"	but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"	GNU General Public License for more details.
+.\"
+.\"	You should have received a copy of the GNU General Public License
+.\"	along with this program; if not, write to the Free Software
+.\"	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables-xml \(em Convert iptables-save format to XML
+.SH SYNOPSIS
+\fBiptables\-xml\fP [\fB\-c\fP] [\fB\-v\fP]
+.SH DESCRIPTION
+.PP
+.B iptables-xml
+is used to convert the output of iptables-save into an easily manipulatable
+XML format to STDOUT.  Use I/O-redirection provided by your shell to write to 
+a file.
+.TP
+\fB\-c\fR, \fB\-\-combine\fR
+combine consecutive rules with the same matches but different targets. iptables
+does not currently support more than one target per match, so this simulates 
+that by collecting the targets from consecutive iptables rules into one action
+tag, but only when the rule matches are identical. Terminating actions like
+RETURN, DROP, ACCEPT and QUEUE are not combined with subsequent targets.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Output xml comments containing the iptables line from which the XML is derived
+
+.PP
+iptables-xml does a mechanistic conversion to a very expressive xml
+format; the only semantic considerations are for \-g and \-j targets in
+order to discriminate between <call> <goto> and <nane-of-target> as it
+helps xml processing scripts if they can tell the difference between a
+target like SNAT and another chain.
+
+Some sample output is:
+
+<iptables-rules>
+  <table name="mangle">
+    <chain name="PREROUTING" policy="ACCEPT" packet-count="63436"
+byte-count="7137573">
+      <rule>
+       <conditions>
+        <match>
+          <p>tcp</p>
+        </match>
+        <tcp>
+          <sport>8443</sport>
+        </tcp>
+       </conditions>
+       <actions>
+        <call>
+          <check_ip/>
+        </call>
+        <ACCEPT/>
+       </actions>
+      </rule>
+    </chain>
+  </table>
+</iptables-rules>
+
+.PP
+Conversion from XML to iptables-save format may be done using the 
+iptables.xslt script and xsltproc, or a custom program using
+libxsltproc or similar; in this fashion:
+
+xsltproc iptables.xslt my-iptables.xml | iptables-restore
+
+.SH BUGS
+None known as of iptables-1.3.7 release
+.SH AUTHOR
+Sam Liddicott <azez@ufomechanic.net>
+.SH SEE ALSO
+\fBiptables\-save\fP(8), \fBiptables\-restore\fP(8), \fBiptables\fP(8)
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
new file mode 100644
index 0000000..9628447
--- /dev/null
+++ b/iptables/iptables-xml.c
@@ -0,0 +1,871 @@
+/* Code to convert iptables-save format to xml format,
+ * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
+ * based on iptables-restore (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+#include "xtables-multi.h"
+#include <xtables.h>
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#endif
+
+struct xtables_globals iptables_xml_globals = {
+	.option_offset = 0,
+	.program_version = IPTABLES_VERSION,
+	.program_name = "iptables-xml",
+};
+#define prog_name iptables_xml_globals.program_name
+#define prog_vers iptables_xml_globals.program_version
+
+static void print_usage(const char *name, const char *version)
+	    __attribute__ ((noreturn));
+
+static int verbose = 0;
+/* Whether to combine actions of sequential rules with identical conditions */
+static int combine = 0;
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+	{"verbose", 0, NULL, 'v'},
+	{"combine", 0, NULL, 'c'},
+	{"help", 0, NULL, 'h'},
+	{ .name = NULL }
+};
+
+static void
+print_usage(const char *name, const char *version)
+{
+	fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
+		"          [--combine ]\n"
+		"	   [ --verbose ]\n" "	   [ --help ]\n", name);
+
+	exit(1);
+}
+
+static int
+parse_counters(char *string, struct xt_counters *ctr)
+{
+	__u64 *pcnt, *bcnt;
+
+	if (string != NULL) {
+		pcnt = &ctr->pcnt;
+		bcnt = &ctr->bcnt;
+		return (sscanf
+			(string, "[%llu:%llu]",
+			 (unsigned long long *)pcnt,
+			 (unsigned long long *)bcnt) == 2);
+	} else
+		return (0 == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static unsigned int newargc = 0;
+
+static char *oldargv[255];
+static unsigned int oldargc = 0;
+
+/* arg meta data, were they quoted, frinstance */
+static int newargvattr[255];
+
+#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 xt_counters count;
+	int created;
+};
+
+#define maxChains 10240		/* max chains per table */
+static struct chain chains[maxChains];
+static int nextChain = 0;
+
+/* funCtion adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int
+add_argv(char *what, int quoted)
+{
+	DEBUGP("add_argv: %d %s\n", newargc, what);
+	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
+		newargv[newargc] = strdup(what);
+		newargvattr[newargc] = quoted;
+		newargc++;
+		return 1;
+	} else
+		return 0;
+}
+
+static void
+free_argv(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < newargc; i++) {
+		free(newargv[i]);
+		newargv[i] = NULL;
+	}
+	newargc = 0;
+
+	for (i = 0; i < oldargc; i++) {
+		free(oldargv[i]);
+		oldargv[i] = NULL;
+	}
+	oldargc = 0;
+}
+
+/* save parsed rule for comparison with next rule 
+   to perform action agregation on duplicate conditions */
+static void
+save_argv(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < oldargc; i++)
+		free(oldargv[i]);
+	oldargc = newargc;
+	newargc = 0;
+	for (i = 0; i < oldargc; i++) {
+		oldargv[i] = newargv[i];
+		newargv[i] = NULL;
+	}
+}
+
+/* like puts but with xml encoding */
+static void
+xmlEncode(char *text)
+{
+	while (text && *text) {
+		if ((unsigned char) (*text) >= 127)
+			printf("&#%d;", (unsigned char) (*text));
+		else if (*text == '&')
+			printf("&amp;");
+		else if (*text == '<')
+			printf("&lt;");
+		else if (*text == '>')
+			printf("&gt;");
+		else if (*text == '"')
+			printf("&quot;");
+		else
+			putchar(*text);
+		text++;
+	}
+}
+
+/* Output text as a comment, avoiding a double hyphen */
+static void
+xmlCommentEscape(char *comment)
+{
+	int h_count = 0;
+
+	while (comment && *comment) {
+		if (*comment == '-') {
+			h_count++;
+			if (h_count >= 2) {
+				h_count = 0;
+				putchar(' ');
+			}
+			putchar('*');
+		}
+		/* strip trailing newline */
+		if (*comment == '\n' && *(comment + 1) == 0);
+		else
+			putchar(*comment);
+		comment++;
+	}
+}
+
+static void
+xmlComment(char *comment)
+{
+	printf("<!-- ");
+	xmlCommentEscape(comment);
+	printf(" -->\n");
+}
+
+static void
+xmlAttrS(char *name, char *value)
+{
+	printf("%s=\"", name);
+	xmlEncode(value);
+	printf("\" ");
+}
+
+static void
+xmlAttrI(char *name, long long int num)
+{
+	printf("%s=\"%lld\" ", name, num);
+}
+
+static void
+closeChain(void)
+{
+	if (curChain[0] == 0)
+		return;
+
+	if (closeActionTag[0])
+		printf("%s\n", closeActionTag);
+	closeActionTag[0] = 0;
+	if (closeRuleTag[0])
+		printf("%s\n", closeRuleTag);
+	closeRuleTag[0] = 0;
+	if (curChain[0])
+		printf("    </chain>\n");
+	curChain[0] = 0;
+	//lastRule[0]=0;
+}
+
+static void
+openChain(char *chain, char *policy, struct xt_counters *ctr, char close)
+{
+	closeChain();
+
+	strncpy(curChain, chain, XT_CHAIN_MAXNAMELEN);
+	curChain[XT_CHAIN_MAXNAMELEN] = '\0';
+
+	printf("    <chain ");
+	xmlAttrS("name", curChain);
+	if (strcmp(policy, "-") != 0)
+		xmlAttrS("policy", policy);
+	xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
+	xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
+	if (close) {
+		printf("%c", close);
+		curChain[0] = 0;
+	}
+	printf(">\n");
+}
+
+static int
+existsChain(char *chain)
+{
+	/* open a saved chain */
+	int c = 0;
+
+	if (0 == strcmp(curChain, chain))
+		return 1;
+	for (c = 0; c < nextChain; c++)
+		if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
+			return 1;
+	return 0;
+}
+
+static void
+needChain(char *chain)
+{
+	/* open a saved chain */
+	int c = 0;
+
+	if (0 == strcmp(curChain, chain))
+		return;
+
+	for (c = 0; c < nextChain; c++)
+		if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
+			openChain(chains[c].chain, chains[c].policy,
+				  &(chains[c].count), '\0');
+			/* And, mark it as done so we don't create 
+			   an empty chain at table-end time */
+			chains[c].created = 1;
+		}
+}
+
+static void
+saveChain(char *chain, char *policy, struct xt_counters *ctr)
+{
+	if (nextChain >= maxChains) {
+		xtables_error(PARAMETER_PROBLEM,
+			   "%s: line %u chain name invalid\n",
+			   prog_name, line);
+		exit(1);
+	};
+	chains[nextChain].chain = strdup(chain);
+	chains[nextChain].policy = strdup(policy);
+	chains[nextChain].count = *ctr;
+	chains[nextChain].created = 0;
+	nextChain++;
+}
+
+static void
+finishChains(void)
+{
+	int c;
+
+	for (c = 0; c < nextChain; c++)
+		if (!chains[c].created) {
+			openChain(chains[c].chain, chains[c].policy,
+				  &(chains[c].count), '/');
+			free(chains[c].chain);
+			free(chains[c].policy);
+		}
+	nextChain = 0;
+}
+
+static void
+closeTable(void)
+{
+	closeChain();
+	finishChains();
+	if (curTable[0])
+		printf("  </table>\n");
+	curTable[0] = 0;
+}
+
+static void
+openTable(char *table)
+{
+	closeTable();
+
+	strncpy(curTable, table, XT_TABLE_MAXNAMELEN);
+	curTable[XT_TABLE_MAXNAMELEN] = '\0';
+
+	printf("  <table ");
+	xmlAttrS("name", curTable);
+	printf(">\n");
+}
+
+// is char* -j --jump -g or --goto
+static int
+isTarget(char *arg)
+{
+	return ((arg)
+		&& (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
+		    || strcmp((arg), "-g") == 0
+		    || strcmp((arg), "--goto") == 0));
+}
+
+// is it a terminating target like -j ACCEPT, etc
+// (or I guess -j SNAT in nat table, but we don't check for that yet
+static int
+isTerminatingTarget(char *arg)
+{
+	return ((arg)
+		&& (strcmp((arg), "ACCEPT") == 0
+		    || strcmp((arg), "DROP") == 0
+		    || strcmp((arg), "QUEUE") == 0
+		    || strcmp((arg), "RETURN") == 0));
+}
+
+// part=-1 means do conditions, part=1 means do rules, part=0 means do both
+static void
+do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
+	     char *argv[], int argvattr[])
+{
+	int i;
+	int arg = 2;		// ignore leading -A <chain>
+	char invert_next = 0;
+	char *spacer = "";	// space when needed to assemble arguments
+	char *level1 = NULL;
+	char *level2 = NULL;
+	char *leveli1 = "        ";
+	char *leveli2 = "          ";
+
+#define CLOSE_LEVEL(LEVEL) \
+	do { \
+		if (level ## LEVEL) printf("</%s>\n", \
+		(leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
+		level ## LEVEL=NULL;\
+	} while(0)
+
+#define OPEN_LEVEL(LEVEL,TAG) \
+	do {\
+		level ## LEVEL=TAG;\
+		if (leveltag ## LEVEL) {\
+			printf("%s<%s ", (leveli ## LEVEL), \
+				(leveltag ## LEVEL));\
+			xmlAttrS("type", (TAG)); \
+		} else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
+	} while(0)
+
+	if (part == 1) {	/* skip */
+		/* use argvattr to tell which arguments were quoted 
+		   to avoid comparing quoted arguments, like comments, to -j, */
+		while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
+			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.
+	 * 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");
+	}
+	while (arg < argc) {
+		// If ! is followed by -* then apply to that else output as data
+		// Stop, if we need to
+		if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
+			break;
+		} else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
+			if ((arg + 1) < argc && argv[arg + 1][0] == '-')
+				invert_next = '!';
+			else
+				printf("%s%s", spacer, argv[arg]);
+			spacer = " ";
+		} else if (!argvattr[arg] && isTarget(argv[arg])
+			   && existsChain(argv[arg + 1])
+			   && (2 + arg >= argc)) {
+			if (!((1 + arg) < argc))
+				// no args to -j, -m or -g, ignore & finish loop
+				break;
+			CLOSE_LEVEL(2);
+			if (level1)
+				printf("%s", leveli1);
+			CLOSE_LEVEL(1);
+			spacer = "";
+			invert_next = 0;
+			if (strcmp(argv[arg], "-g") == 0
+			    || strcmp(argv[arg], "--goto") == 0) {
+				/* goto user chain */
+				OPEN_LEVEL(1, "goto");
+				printf(">\n");
+				arg++;
+				OPEN_LEVEL(2, argv[arg]);
+				printf("/>\n");
+				level2 = NULL;
+			} else {
+				/* call user chain */
+				OPEN_LEVEL(1, "call");
+				printf(">\n");
+				arg++;
+				OPEN_LEVEL(2, argv[arg]);
+				printf("/>\n");
+				level2 = NULL;
+			}
+		} else if (!argvattr[arg]
+			   && (isTarget(argv[arg])
+			       || strcmp(argv[arg], "-m") == 0
+			       || strcmp(argv[arg], "--module") == 0)) {
+			if (!((1 + arg) < argc))
+				// no args to -j, -m or -g, ignore & finish loop
+				break;
+			CLOSE_LEVEL(2);
+			if (level1)
+				printf("%s", leveli1);
+			CLOSE_LEVEL(1);
+			spacer = "";
+			invert_next = 0;
+			arg++;
+			OPEN_LEVEL(1, (argv[arg]));
+			// Optimize case, can we close this tag already?
+			if ((arg + 1) >= argc || (!argvattr[arg + 1]
+						  && (isTarget(argv[arg + 1])
+						      || strcmp(argv[arg + 1],
+								"-m") == 0
+						      || strcmp(argv[arg + 1],
+								"--module") ==
+						      0))) {
+				printf(" />\n");
+				level1 = NULL;
+			} else {
+				printf(">\n");
+			}
+		} else if (!argvattr[arg] && argv[arg][0] == '-') {
+			char *tag;
+			CLOSE_LEVEL(2);
+			// Skip past any -
+			tag = argv[arg];
+			while (*tag == '-' && *tag)
+				tag++;
+
+			spacer = "";
+			OPEN_LEVEL(2, tag);
+			if (invert_next)
+				printf(" invert=\"1\"");
+			invert_next = 0;
+
+			// Optimize case, can we close this tag already?
+			if (!((arg + 1) < argc)
+			    || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
+				printf(" />\n");
+				level2 = NULL;
+			} else {
+				printf(">");
+			}
+		} else {	// regular data
+			char *spaces = strchr(argv[arg], ' ');
+			printf("%s", spacer);
+			if (spaces || argvattr[arg])
+				printf("&quot;");
+			// if argv[arg] contains a space, enclose in quotes
+			xmlEncode(argv[arg]);
+			if (spaces || argvattr[arg])
+				printf("&quot;");
+			spacer = " ";
+		}
+		arg++;
+	}
+	CLOSE_LEVEL(2);
+	if (level1)
+		printf("%s", leveli1);
+	CLOSE_LEVEL(1);
+}
+
+static int
+compareRules(void)
+{
+	/* compare arguments up to -j or -g for match.
+	   NOTE: We don't want to combine actions if there were no criteria 
+	   in each rule, or rules didn't have an action 
+	   NOTE: Depends on arguments being in some kind of "normal" order which 
+	   is the case when processing the ACTUAL output of actual iptables-save 
+	   rather than a file merely in a compatable format */
+
+	unsigned int old = 0;
+	unsigned int new = 0;
+
+	int compare = 0;
+
+	while (new < newargc && old < oldargc) {
+		if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
+			/* if oldarg was a terminating action then it makes no sense
+			 * to combine further actions into the same xml */
+			if (((strcmp((oldargv[old]), "-j") == 0 
+					|| strcmp((oldargv[old]), "--jump") == 0) 
+				&& old+1 < oldargc
+				&& isTerminatingTarget(oldargv[old+1]) )
+			    || strcmp((oldargv[old]), "-g") == 0 
+			    || strcmp((oldargv[old]), "--goto") == 0 ) {
+				/* Previous rule had terminating action */	
+				compare = 0;
+			} else {
+				compare = 1;
+			}
+			break;
+		}
+		// break when old!=new
+		if (strcmp(oldargv[old], newargv[new]) != 0) {
+			compare = 0;
+			break;
+		}
+
+		old++;
+		new++;
+	}
+	// We won't match unless both rules had a target. 
+	// This means we don't combine target-less rules, which is good
+
+	return compare == 1;
+}
+
+/* has a nice parsed rule starting with -A */
+static void
+do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
+{
+	/* are these conditions the same as the previous rule?
+	 * If so, skip arg straight to -j or -g */
+	if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
+		xmlComment("Combine action from next rule");
+	} else {
+
+		if (closeActionTag[0]) {
+			printf("%s\n", closeActionTag);
+			closeActionTag[0] = 0;
+		}
+		if (closeRuleTag[0]) {
+			printf("%s\n", closeRuleTag);
+			closeRuleTag[0] = 0;
+		}
+
+		printf("      <rule ");
+		//xmlAttrS("table",curTable); // not needed in full mode 
+		//xmlAttrS("chain",argv[1]); // not needed in full mode 
+		if (pcnt)
+			xmlAttrS("packet-count", pcnt);
+		if (bcnt)
+			xmlAttrS("byte-count", bcnt);
+		printf(">\n");
+
+		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])) {
+			printf("       <conditions>\n");
+			do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
+			printf("       </conditions>\n");
+		}
+	}
+	/* Write out the action */
+	//do_rule_part("action","arg",1,argc,argv,argvattr);
+	if (!closeActionTag[0]) {
+		printf("       <actions>\n");
+		strncpy(closeActionTag, "       </actions>\n",
+			XT_TABLE_MAXNAMELEN);
+		closeActionTag[XT_TABLE_MAXNAMELEN] = '\0';
+	}
+	do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
+}
+
+int
+iptables_xml_main(int argc, char *argv[])
+{
+	char buffer[10240];
+	int c;
+	FILE *in;
+
+	line = 0;
+
+	xtables_set_params(&iptables_xml_globals);
+	while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
+		switch (c) {
+		case 'c':
+			combine = 1;
+			break;
+		case 'v':
+			printf("xptables-xml\n");
+			verbose = 1;
+			break;
+		case 'h':
+			print_usage("iptables-xml", IPTABLES_VERSION);
+			break;
+		}
+	}
+
+	if (optind == argc - 1) {
+		in = fopen(argv[optind], "re");
+		if (!in) {
+			fprintf(stderr, "Can't open %s: %s", argv[optind],
+				strerror(errno));
+			exit(1);
+		}
+	} else if (optind < argc) {
+		fprintf(stderr, "Unknown arguments found on commandline");
+		exit(1);
+	} else
+		in = stdin;
+
+	printf("<iptables-rules version=\"1.0\">\n");
+
+	/* Grab standard input. */
+	while (fgets(buffer, sizeof(buffer), in)) {
+		int ret = 0;
+
+		line++;
+
+		if (buffer[0] == '\n')
+			continue;
+		else if (buffer[0] == '#') {
+			xmlComment(buffer);
+			continue;
+		}
+
+		if (verbose) {
+			printf("<!-- line %d ", line);
+			xmlCommentEscape(buffer);
+			printf(" -->\n");
+		}
+
+		if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
+			DEBUGP("Calling commit\n");
+			closeTable();
+			ret = 1;
+		} else if ((buffer[0] == '*')) {
+			/* New table */
+			char *table;
+
+			table = strtok(buffer + 1, " \t\n");
+			DEBUGP("line %u, table '%s'\n", line, table);
+			if (!table) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u table name invalid\n",
+					   prog_name, line);
+				exit(1);
+			}
+			openTable(table);
+
+			ret = 1;
+		} else if ((buffer[0] == ':') && (curTable[0])) {
+			/* New chain. */
+			char *policy, *chain;
+			struct xt_counters count;
+			char *ctrs;
+
+			chain = strtok(buffer + 1, " \t\n");
+			DEBUGP("line %u, chain '%s'\n", line, chain);
+			if (!chain) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u chain name invalid\n",
+					   prog_name, line);
+				exit(1);
+			}
+
+			DEBUGP("Creating new chain '%s'\n", chain);
+
+			policy = strtok(NULL, " \t\n");
+			DEBUGP("line %u, policy '%s'\n", line, policy);
+			if (!policy) {
+				xtables_error(PARAMETER_PROBLEM,
+					   "%s: line %u policy invalid\n",
+					   prog_name, line);
+				exit(1);
+			}
+
+			ctrs = strtok(NULL, " \t\n");
+			parse_counters(ctrs, &count);
+			saveChain(chain, policy, &count);
+
+			ret = 1;
+		} else if (curTable[0]) {
+			unsigned int a;
+			char *ptr = buffer;
+			char *pcnt = NULL;
+			char *bcnt = NULL;
+			char *parsestart;
+			char *chain = NULL;
+
+			/* the parser */
+			char *param_start, *curchar;
+			int quote_open, quoted;
+			char param_buffer[1024];
+
+			/* reset the newargv */
+			newargc = 0;
+
+			if (buffer[0] == '[') {
+				/* we have counters in our input */
+				ptr = strchr(buffer, ']');
+				if (!ptr)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				pcnt = strtok(buffer + 1, ":");
+				if (!pcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need :\n",
+						   line);
+
+				bcnt = strtok(NULL, "]");
+				if (!bcnt)
+					xtables_error(PARAMETER_PROBLEM,
+						   "Bad line %u: need ]\n",
+						   line);
+
+				/* start command parsing after counter */
+				parsestart = ptr + 1;
+			} else {
+				/* start command parsing at start of line */
+				parsestart = buffer;
+			}
+
+
+			/* This is a 'real' parser crafted in artist mode
+			 * not hacker mode. If the author can live with that
+			 * then so can everyone else */
+
+			quote_open = 0;
+			/* We need to know which args were quoted so we 
+			   can preserve quote */
+			quoted = 0;
+			param_start = parsestart;
+
+			for (curchar = parsestart; *curchar; curchar++) {
+				if (*curchar == '"') {
+					/* quote_open cannot be true if there
+					 * was no previous character.  Thus, 
+					 * curchar-1 has to be within bounds */
+					if (quote_open &&
+					    *(curchar - 1) != '\\') {
+						quote_open = 0;
+						*curchar = ' ';
+					} else {
+						quote_open = 1;
+						quoted = 1;
+						param_start++;
+					}
+				}
+				if (*curchar == ' '
+				    || *curchar == '\t' || *curchar == '\n') {
+					int param_len = curchar - param_start;
+
+					if (quote_open)
+						continue;
+
+					if (!param_len) {
+						/* two spaces? */
+						param_start++;
+						continue;
+					}
+
+					/* end of one parameter */
+					strncpy(param_buffer, param_start,
+						param_len);
+					*(param_buffer + param_len) = '\0';
+
+					/* check if table name specified */
+					if (!strncmp(param_buffer, "-t", 3)
+					    || !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, quoted);
+					if (newargc >= 2
+					    && 0 ==
+					    strcmp(newargv[newargc - 2], "-A"))
+						chain = newargv[newargc - 1];
+					quoted = 0;
+					param_start += param_len + 1;
+				} else {
+					/* regular character, skip */
+				}
+			}
+
+			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
+			       newargc, curTable);
+
+			for (a = 0; a < newargc; a++)
+				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+			needChain(chain);// Should we explicitly look for -A
+			do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
+
+			save_argv();
+			ret = 1;
+		}
+		if (!ret) {
+			fprintf(stderr, "%s: line %u failed\n",
+				prog_name, line);
+			exit(1);
+		}
+	}
+	if (curTable[0]) {
+		fprintf(stderr, "%s: COMMIT expected at line %u\n",
+			prog_name, line + 1);
+		exit(1);
+	}
+
+	fclose(in);
+	printf("</iptables-rules>\n");
+	free_argv();
+
+	return 0;
+}
diff --git a/iptables/iptables.8.in b/iptables/iptables.8.in
new file mode 100644
index 0000000..155c97e
--- /dev/null
+++ b/iptables/iptables.8.in
@@ -0,0 +1,467 @@
+.TH IPTABLES 8 "" "@PACKAGE_STRING@" "@PACKAGE_STRING@"
+.\"
+.\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
+.\" It is based on ipchains page.
+.\" TODO: add a word for protocol helpers (FTP, IRC, SNMP-ALG)
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\"	This program is free software; you can redistribute it and/or modify
+.\"	it under the terms of the GNU General Public License as published by
+.\"	the Free Software Foundation; either version 2 of the License, or
+.\"	(at your option) any later version.
+.\"
+.\"	This program is distributed in the hope that it will be useful,
+.\"	but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"	GNU General Public License for more details.
+.\"
+.\"	You should have received a copy of the GNU General Public License
+.\"	along with this program; if not, write to the Free Software
+.\"	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables/ip6tables \(em administration tool for IPv4/IPv6 packet filtering and NAT
+.SH SYNOPSIS
+\fBiptables\fP [\fB\-t\fP \fItable\fP] {\fB\-A\fP|\fB\-C\fP|\fB\-D\fP}
+\fIchain\fP \fIrule-specification\fP
+.P
+\fBip6tables\fP [\fB\-t\fP \fItable\fP] {\fB\-A\fP|\fB\-C\fP|\fB\-D\fP}
+\fIchain rule-specification\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-I\fP \fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-R\fP \fIchain rulenum rule-specification\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-D\fP \fIchain rulenum\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-S\fP [\fIchain\fP [\fIrulenum\fP]]
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] {\fB\-F\fP|\fB\-L\fP|\fB\-Z\fP} [\fIchain\fP [\fIrulenum\fP]] [\fIoptions...\fP]
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-N\fP \fIchain\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-X\fP [\fIchain\fP]
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-P\fP \fIchain target\fP
+.PP
+\fBiptables\fP [\fB\-t\fP \fItable\fP] \fB\-E\fP \fIold-chain-name new-chain-name\fP
+.PP
+rule-specification = [\fImatches...\fP] [\fItarget\fP]
+.PP
+match = \fB\-m\fP \fImatchname\fP [\fIper-match-options\fP]
+.PP
+target = \fB\-j\fP \fItargetname\fP [\fIper\-target\-options\fP]
+.SH DESCRIPTION
+\fBIptables\fP and \fBip6tables\fP are used to set up, maintain, and inspect the
+tables of IPv4 and IPv6 packet
+filter rules in the Linux kernel.  Several different tables
+may be defined.  Each table contains a number of built-in
+chains and may also contain user-defined chains.
+.PP
+Each chain is a list of rules which can match a set of packets.  Each
+rule specifies what to do with a packet that matches.  This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+.SH TARGETS
+A firewall rule specifies criteria for a packet and a target.  If the
+packet does not match, the next rule in the chain is examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain, one of the targets
+described in \fBiptables\-extensions\fP(8), or one of the
+special values \fBACCEPT\fP, \fBDROP\fP or \fBRETURN\fP.
+.PP
+\fBACCEPT\fP means to let the packet through.
+\fBDROP\fP means to drop the packet on the floor.
+\fBRETURN\fP means stop traversing this chain and resume at the next
+rule in the
+previous (calling) chain.  If the end of a built-in chain is reached
+or a rule in a built-in chain with target \fBRETURN\fP
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+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
+\fB\-t\fP, \fB\-\-table\fP \fItable\fP
+This option specifies the packet matching table which the command
+should operate on.  If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+\fBfilter\fP:
+This is the default table (if no \-t option is passed). It contains
+the built-in chains \fBINPUT\fP (for packets destined to local sockets),
+\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).
+IPv6 NAT support is 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
+(for altering incoming packets before routing) and \fBOUTPUT\fP
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+\fBINPUT\fP (for packets coming into the box itself), \fBFORWARD\fP
+(for altering packets being routed through the box), and \fBPOSTROUTING\fP
+(for altering packets as they are about to go out).
+.TP
+\fBraw\fP:
+This table is used mainly for configuring exemptions from connection
+tracking in combination with the NOTRACK target.  It registers at the netfilter
+hooks with higher priority and is thus called before ip_conntrack, or any other
+IP tables.  It provides the following built-in chains: \fBPREROUTING\fP
+(for packets arriving via any network interface) \fBOUTPUT\fP
+(for packets generated by local processes)
+.TP
+\fBsecurity\fP:
+This table is used for Mandatory Access Control (MAC) networking rules, such
+as those enabled by the \fBSECMARK\fP and \fBCONNSECMARK\fP targets.
+Mandatory Access Control is implemented by Linux Security Modules such as
+SELinux.  The security table is called after the filter table, allowing any
+Discretionary Access Control (DAC) rules in the filter table to take effect
+before MAC rules.  This table provides the following built-in chains:
+\fBINPUT\fP (for packets coming into the box itself),
+\fBOUTPUT\fP (for altering locally-generated packets before routing), and
+\fBFORWARD\fP (for altering packets being routed through the box).
+.RE
+.SH OPTIONS
+The options that are recognized by
+\fBiptables\fP and \fBip6tables\fP can be divided into several different groups.
+.SS COMMANDS
+These options specify the desired action to perform. Only one of them
+can be specified on the command line unless otherwise stated
+below. For long versions of the command and option names, you
+need to use only enough letters to ensure that
+\fBiptables\fP can differentiate it from all other options.
+.TP
+\fB\-A\fP, \fB\-\-append\fP \fIchain rule-specification\fP
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+\fB\-C\fP, \fB\-\-check\fP \fIchain rule-specification\fP
+Check whether a rule matching the specification does exist in the
+selected chain. This command uses the same logic as \fB\-D\fP to
+find a matching entry, but does not alter the existing iptables
+configuration and uses its exit code to indicate success or failure.
+.TP
+\fB\-D\fP, \fB\-\-delete\fP \fIchain rule-specification\fP
+.ns
+.TP
+\fB\-D\fP, \fB\-\-delete\fP \fIchain rulenum\fP
+Delete one or more rules from the selected chain.  There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+\fB\-I\fP, \fB\-\-insert\fP \fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP
+Insert one or more rules in the selected chain as the given rule
+number.  So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain.  This is also the default if no rule number
+is specified.
+.TP
+\fB\-R\fP, \fB\-\-replace\fP \fIchain rulenum rule-specification\fP
+Replace a rule in the selected chain.  If the source and/or
+destination names resolve to multiple addresses, the command will
+fail.  Rules are numbered starting at 1.
+.TP
+\fB\-L\fP, \fB\-\-list\fP [\fIchain\fP]
+List all rules in the selected chain.  If no chain is selected, all
+chains are listed. Like every other iptables command, it applies to the
+specified table (filter is the default), so NAT rules get listed by
+.nf
+ iptables \-t nat \-n \-L
+.fi
+Please note that it is often used with the \fB\-n\fP
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the \fB\-Z\fP
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed.  The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ iptables \-L \-v
+.fi
+.TP
+\fB\-S\fP, \fB\-\-list\-rules\fP [\fIchain\fP]
+Print all rules in the selected chain.  If no chain is selected, all
+chains are printed like iptables-save. Like every other iptables command,
+it applies to the specified table (filter is the default).
+.TP
+\fB\-F\fP, \fB\-\-flush\fP [\fIchain\fP]
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+\fB\-Z\fP, \fB\-\-zero\fP [\fIchain\fP [\fIrulenum\fP]]
+Zero the packet and byte counters in all chains, or only the given chain,
+or only the given rule in a chain. It is legal to
+specify the
+\fB\-L\fP, \fB\-\-list\fP
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+\fB\-N\fP, \fB\-\-new\-chain\fP \fIchain\fP
+Create a new user-defined chain by the given name.  There must be no
+target of that name already.
+.TP
+\fB\-X\fP, \fB\-\-delete\-chain\fP [\fIchain\fP]
+Delete the optional user-defined chain specified.  There must be no references
+to the chain.  If there are, you must delete or replace the referring rules
+before the chain can be deleted.  The chain must be empty, i.e. not contain
+any rules.  If no argument is given, it will attempt to delete every
+non-builtin chain in the table.
+.TP
+\fB\-P\fP, \fB\-\-policy\fP \fIchain target\fP
+Set the policy for the chain to the given target.  See the section \fBTARGETS\fP
+for the legal targets.  Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+\fB\-E\fP, \fB\-\-rename\-chain\fP \fIold\-chain new\-chain\fP
+Rename the user specified chain to the user supplied name.  This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+\fB\-h\fP
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+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.
+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
+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.
+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,
+\fBicmp\fP, \fBicmpv6\fP,\fBesp\fP, \fBah\fP, \fBsctp\fP, \fBmh\fP or the special keyword "\fBall\fP",
+or it can be a numeric value, representing one of these protocols or a
+different one.  A protocol name from /etc/protocols is also allowed.
+A "!" argument before the protocol inverts the
+test.  The number zero is equivalent to \fBall\fP. "\fBall\fP"
+will match with all protocols and is taken as default when this
+option is omitted.
+Note that, in ip6tables, IPv6 extension headers except \fBesp\fP are not allowed.
+\fBesp\fP and \fBipv6\-nonext\fP
+can be used with Kernel version 2.6.11 or later.
+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.
+.TP
+[\fB!\fP] \fB\-s\fP, \fB\-\-source\fP \fIaddress\fP[\fB/\fP\fImask\fP][\fB,\fP\fI...\fP]
+Source specification. \fIAddress\fP
+can be either a network name, a hostname, a network IP address (with
+\fB/\fP\fImask\fP), or a plain IP address. Hostnames will
+be resolved once only, before the rule is submitted to the kernel.
+Please note that specifying any name to be resolved with a remote query such as
+DNS is a really bad idea.
+The \fImask\fP
+can be either an ipv4 network mask (for iptables) or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, an iptables mask of \fI24\fP is equivalent to \fI255.255.255.0\fP.
+A "!" argument before the address specification inverts the sense of
+the address. The flag \fB\-\-src\fP is an alias for this option.
+Multiple addresses can be specified, but this will \fBexpand to multiple
+rules\fP (when adding with \-A), or will cause multiple rules to be
+deleted (with \-D).
+.TP
+[\fB!\fP] \fB\-d\fP, \fB\-\-destination\fP \fIaddress\fP[\fB/\fP\fImask\fP][\fB,\fP\fI...\fP]
+Destination specification. 
+See the description of the \fB\-s\fP
+(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
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see \fBEXTENSIONS\fP
+below).  If this
+option is omitted in a rule (and \fB\-g\fP
+is not used), then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+\fB\-g\fP, \fB\-\-goto\fP \fIchain\fP
+This specifies that the processing should continue in a user
+specified chain. Unlike the \-\-jump option return will not continue
+processing in this chain but instead in the chain that called us via
+\-\-jump.
+.TP
+[\fB!\fP] \fB\-i\fP, \fB\-\-in\-interface\fP \fIname\fP
+Name of an interface via which a packet was received (only for
+packets entering the \fBINPUT\fP, \fBFORWARD\fP and \fBPREROUTING\fP
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+[\fB!\fP] \fB\-o\fP, \fB\-\-out\-interface\fP \fIname\fP
+Name of an interface via which a packet is going to be sent (for packets
+entering the \fBFORWARD\fP, \fBOUTPUT\fP and \fBPOSTROUTING\fP
+chains).  When the "!" argument is used before the interface name, the
+sense is inverted.  If the interface name ends in a "+", then any
+interface which begins with this name will match.  If this option is
+omitted, any interface name will match.
+.TP
+[\fB!\fP] \fB\-f\fP, \fB\-\-fragment\fP
+This means that the rule only refers to second and further IPv4 fragments
+of fragmented packets.  Since there is no way to tell the source or
+destination ports of such a packet (or ICMP type), such a packet will
+not match any rules which specify them.  When the "!" argument
+precedes the "\-f" flag, the rule will only match head fragments, or
+unfragmented packets. This option is IPv4 specific, it is not available
+in ip6tables.
+.TP
+\fB\-c\fP, \fB\-\-set\-counters\fP \fIpackets bytes\fP
+This enables the administrator to initialize the packet and byte
+counters of a rule (during \fBINSERT\fP, \fBAPPEND\fP, \fBREPLACE\fP
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Verbose output.  This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks.  The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+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. \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.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+\fB\-x\fP, \fB\-\-exact\fP
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M).  This option is
+only relevant for the \fB\-L\fP command.
+.TP
+\fB\-\-line\-numbers\fP
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+\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 AND TARGET EXTENSIONS
+.PP
+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
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs?  What's this? ;-)
+Well, you might want to have a look at http://bugzilla.netfilter.org/
+.SH COMPATIBILITY WITH IPCHAINS
+This \fBiptables\fP
+is very similar to ipchains by Rusty Russell.  The main difference is
+that the chains \fBINPUT\fP and \fBOUTPUT\fP
+are only traversed for packets coming into the local host and
+originating from the local host respectively.  Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that \fB\-i\fP refers to the input interface;
+\fB\-o\fP refers to the output interface, and both are available for packets
+entering the \fBFORWARD\fP chain.
+.PP
+The various forms of NAT have been separated out; \fBiptables\fP
+is a pure packet filter when using the default `filter' table, with
+optional extension modules.  This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously.  So the following options are handled differently:
+.nf
+ \-j MASQ
+ \-M \-S
+ \-M \-L
+.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),
+.PP
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell originally wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG and NFQUEUE target, the new libiptc, as well as the TTL, DSCP, ECN matches and targets.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Yasuyuki Kozakai,
+Jozsef Kadlecsik, Patrick McHardy, James Morris, Pablo Neira Ayuso,
+Harald Welte and Rusty Russell.
+.PP
+Man page originally written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
+.SH VERSION
+.PP
+This manual page applies to iptables/ip6tables @PACKAGE_AND_VERSION@.
diff --git a/iptables/iptables.c b/iptables/iptables.c
new file mode 100644
index 0000000..5cd2596
--- /dev/null
+++ b/iptables/iptables.c
@@ -0,0 +1,1955 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ * 		    Marc Boucher <marc+nf@mbsi.ca>
+ * 		    James Morris <jmorris@intercode.com.au>
+ * 		    Harald Welte <laforge@gnumonks.org>
+ * 		    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 as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <unistd.h>
+#include <iptables.h>
+#include <xtables.h>
+#include <fcntl.h>
+#include "xshared.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define CMD_NONE		0x0000U
+#define CMD_INSERT		0x0001U
+#define CMD_DELETE		0x0002U
+#define CMD_DELETE_NUM		0x0004U
+#define CMD_REPLACE		0x0008U
+#define CMD_APPEND		0x0010U
+#define CMD_LIST		0x0020U
+#define CMD_FLUSH		0x0040U
+#define CMD_ZERO		0x0080U
+#define CMD_NEW_CHAIN		0x0100U
+#define CMD_DELETE_CHAIN	0x0200U
+#define CMD_SET_POLICY		0x0400U
+#define CMD_RENAME_CHAIN	0x0800U
+#define CMD_LIST_RULES		0x1000U
+#define CMD_ZERO_NUM		0x2000U
+#define CMD_CHECK		0x4000U
+#define NUMBER_OF_CMD	16
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+				 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
+
+#define OPT_FRAGMENT    0x00800U
+#define NUMBER_OF_OPT	ARRAY_SIZE(optflags)
+static const char optflags[]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '0', 'c', 'f'};
+
+static struct option original_opts[] = {
+	{.name = "append",        .has_arg = 1, .val = 'A'},
+	{.name = "delete",        .has_arg = 1, .val = 'D'},
+	{.name = "check",         .has_arg = 1, .val = 'C'},
+	{.name = "insert",        .has_arg = 1, .val = 'I'},
+	{.name = "replace",       .has_arg = 1, .val = 'R'},
+	{.name = "list",          .has_arg = 2, .val = 'L'},
+	{.name = "list-rules",    .has_arg = 2, .val = 'S'},
+	{.name = "flush",         .has_arg = 2, .val = 'F'},
+	{.name = "zero",          .has_arg = 2, .val = 'Z'},
+	{.name = "new-chain",     .has_arg = 1, .val = 'N'},
+	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
+	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
+	{.name = "policy",        .has_arg = 1, .val = 'P'},
+	{.name = "source",        .has_arg = 1, .val = 's'},
+	{.name = "destination",   .has_arg = 1, .val = 'd'},
+	{.name = "src",           .has_arg = 1, .val = 's'}, /* synonym */
+	{.name = "dst",           .has_arg = 1, .val = 'd'}, /* synonym */
+	{.name = "protocol",      .has_arg = 1, .val = 'p'},
+	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
+	{.name = "jump",          .has_arg = 1, .val = 'j'},
+	{.name = "table",         .has_arg = 1, .val = 't'},
+	{.name = "match",         .has_arg = 1, .val = 'm'},
+	{.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'},
+	{.name = "help",          .has_arg = 2, .val = 'h'},
+	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
+	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
+	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
+	{.name = "goto",          .has_arg = 1, .val = 'g'},
+	{.name = "ipv4",          .has_arg = 0, .val = '4'},
+	{.name = "ipv6",          .has_arg = 0, .val = '6'},
+	{NULL},
+};
+
+void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
+
+struct xtables_globals iptables_globals = {
+	.option_offset = 0,
+	.program_version = IPTABLES_VERSION,
+	.orig_opts = original_opts,
+	.exit_err = iptables_exit_error,
+};
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o --line -c -f */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' ',' '},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' ','x','x'},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*ZERO_NUM*/  {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*CHECK*/     {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x',' '},
+};
+
+static const int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ XT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/*--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
+
+static void __attribute__((noreturn))
+exit_tryhelp(int status)
+{
+	if (line != -1)
+		fprintf(stderr, "Error occurred at line: %d\n", line);
+	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+			prog_name, prog_name);
+	xtables_free_opts(1);
+	exit(status);
+}
+
+static void
+exit_printhelp(const struct xtables_rule_match *matches)
+{
+	printf("%s v%s\n\n"
+"Usage: %s -[ACD] chain rule-specification [options]\n"
+"       %s -I chain [rulenum] rule-specification [options]\n"
+"       %s -R chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LS] [chain [rulenum]] [options]\n"
+"       %s -[FZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+	       prog_name, prog_vers, prog_name, prog_name,
+	       prog_name, prog_name, prog_name, prog_name,
+	       prog_name, prog_name, prog_name, prog_name);
+
+	printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain		Append to chain\n"
+"  --check   -C chain		Check for the existence of a rule\n"
+"  --delete  -D chain		Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"				Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"				Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"				Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain [rulenum]]\n"
+"				List the rules in a chain or all chains\n"
+"  --list-rules -S [chain [rulenum]]\n"
+"				Print the rules in a chain or all chains\n"
+"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain [rulenum]]\n"
+"				Zero counters in chain or all chains\n"
+"  --new     -N chain		Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]		Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"				Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"				Change chain name, (moving any references)\n"
+
+"Options:\n"
+"    --ipv4	-4		Nothing (line is ignored by ip6tables-restore)\n"
+"    --ipv6	-6		Error (line is ignored by iptables-restore)\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"
+"				destination specification\n"
+"[!] --in-interface -i input name[+]\n"
+"				network interface name ([+] for wildcard)\n"
+" --jump	-j target\n"
+"				target for rule (may load target extension)\n"
+#ifdef IPT_F_GOTO
+"  --goto      -g chain\n"
+"                              jump to chain with no return\n"
+#endif
+"  --match	-m match\n"
+"				extended match (may load extension)\n"
+"  --numeric	-n		numeric output of addresses and ports\n"
+"[!] --out-interface -o output name[+]\n"
+"				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"
+"  --modprobe=<command>		try to insert modules using this command\n"
+"  --set-counters PKTS BYTES	set the counter during insert/append\n"
+"[!] --version	-V		print package version.\n");
+
+	print_extension_helps(xtables_targets, matches);
+	exit(0);
+}
+
+void
+iptables_exit_error(enum xtables_exittype status, const char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	fprintf(stderr, "%s v%s: ", prog_name, prog_vers);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+	if (status == PARAMETER_PROBLEM)
+		exit_tryhelp(status);
+	if (status == VERSION_PROBLEM)
+		fprintf(stderr,
+			"Perhaps iptables or your kernel needs to be upgraded.\n");
+	/* On error paths, make sure that we don't leak memory */
+	xtables_free_opts(1);
+	exit(status);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+	int i, j, legal = 0;
+
+	/* Check that commands are valid with options.  Complicated by the
+	 * fact that if an option is legal with *any* command given, it is
+	 * legal overall (ie. -z and -l).
+	 */
+	for (i = 0; i < NUMBER_OF_OPT; i++) {
+		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+		for (j = 0; j < NUMBER_OF_CMD; j++) {
+			if (!(command & (1<<j)))
+				continue;
+
+			if (!(options & (1<<i))) {
+				if (commands_v_options[j][i] == '+')
+					xtables_error(PARAMETER_PROBLEM,
+						   "You need to supply the `-%c' "
+						   "option for this command\n",
+						   optflags[i]);
+			} else {
+				if (commands_v_options[j][i] != 'x')
+					legal = 1;
+				else if (legal == 0)
+					legal = -1;
+			}
+		}
+		if (legal == -1)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Illegal option `-%c' with this command\n",
+				   optflags[i]);
+	}
+}
+
+static char
+opt2char(int option)
+{
+	const char *ptr;
+	for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+	const char *ptr;
+	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+	return *ptr;
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd, const int othercmds, 
+	    int invert)
+{
+	if (invert)
+		xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
+	if (*cmd & (~othercmds))
+		xtables_error(PARAMETER_PROBLEM, "Cannot use -%c with -%c\n",
+			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+	*cmd |= newcmd;
+}
+
+/*
+ *	All functions starting with "parse" should succeed, otherwise
+ *	the program fails.
+ *	Most routines return pointers to static data that may change
+ *	between calls to the same or other routines with a few exceptions:
+ *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *	return global static data.
+*/
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+	unsigned int rulenum;
+
+	if (!xtables_strtoui(rule, NULL, &rulenum, 1, INT_MAX))
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid rule number `%s'", rule);
+
+	return rulenum;
+}
+
+static void
+parse_chain(const char *chainname)
+{
+	const char *ptr;
+
+	if (strlen(chainname) >= XT_EXTENSION_MAXNAMELEN)
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name `%s' too long (must be under %u chars)",
+			   chainname, XT_EXTENSION_MAXNAMELEN);
+
+	if (*chainname == '-' || *chainname == '!')
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name not allowed to start "
+			   "with `%c'\n", *chainname);
+
+	if (xtables_find_target(chainname, XTF_TRY_LOAD))
+		xtables_error(PARAMETER_PROBLEM,
+			   "chain name may not clash "
+			   "with target name\n");
+
+	for (ptr = chainname; *ptr; ptr++)
+		if (isspace(*ptr))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid chain name `%s'", chainname);
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+	const char *ptr;
+
+	if (strlen(targetname) < 1)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid target name (too short)");
+
+	if (strlen(targetname) >= XT_EXTENSION_MAXNAMELEN)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid target name `%s' (%u chars max)",
+			   targetname, XT_EXTENSION_MAXNAMELEN - 1);
+
+	for (ptr = targetname; *ptr; ptr++)
+		if (isspace(*ptr))
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid target name `%s'", targetname);
+	return targetname;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
+	   int invert)
+{
+	if (*options & option)
+		xtables_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+			   opt2char(option));
+	*options |= option;
+
+	if (invert) {
+		unsigned int i;
+		for (i = 0; 1 << i != option; i++);
+
+		if (!inverse_for_options[i])
+			xtables_error(PARAMETER_PROBLEM,
+				   "cannot have ! before -%c",
+				   opt2char(option));
+		*invflg |= inverse_for_options[i];
+	}
+}
+
+static void
+print_header(unsigned int format, const char *chain, struct xtc_handle *handle)
+{
+	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);
+			xtables_print_num(counters.pcnt, (format|FMT_NOTABLE));
+			fputs("packets, ", stdout);
+			xtables_print_num(counters.bcnt, (format|FMT_NOTABLE));
+			fputs("bytes", stdout);
+		}
+		printf(")\n");
+	} else {
+		unsigned int refs;
+		if (!iptc_get_references(&refs, chain, handle))
+			printf(" (ERROR obtaining refs)\n");
+		else
+			printf(" (%u references)\n", refs);
+	}
+
+	if (format & FMT_LINENUMBERS)
+		printf(FMT("%-4s ", "%s "), "num");
+	if (!(format & FMT_NOCOUNTS)) {
+		if (format & FMT_KILOMEGAGIGA) {
+			printf(FMT("%5s ","%s "), "pkts");
+			printf(FMT("%5s ","%s "), "bytes");
+		} else {
+			printf(FMT("%8s ","%s "), "pkts");
+			printf(FMT("%10s ","%s "), "bytes");
+		}
+	}
+	if (!(format & FMT_NOTARGET))
+		printf(FMT("%-9s ","%s "), "target");
+	fputs(" prot ", stdout);
+	if (format & FMT_OPTIONS)
+		fputs("opt", stdout);
+	if (format & FMT_VIA) {
+		printf(FMT(" %-6s ","%s "), "in");
+		printf(FMT("%-6s ","%s "), "out");
+	}
+	printf(FMT(" %-19s ","%s "), "source");
+	printf(FMT(" %-19s "," %s "), "destination");
+	printf("\n");
+}
+
+
+static int
+print_match(const struct xt_entry_match *m,
+	    const struct ipt_ip *ip,
+	    int numeric)
+{
+	const struct xtables_match *match =
+		xtables_find_match(m->u.user.name, XTF_TRY_LOAD, NULL);
+
+	if (match) {
+		if (match->print)
+			match->print(ip, m, numeric);
+		else
+			printf("%s ", match->name);
+	} else {
+		if (m->u.user.name[0])
+			printf("UNKNOWN match `%s' ", m->u.user.name);
+	}
+	/* Don't stop iterating. */
+	return 0;
+}
+
+/* e is called `fw' here for historical reasons */
+static void
+print_firewall(const struct ipt_entry *fw,
+	       const char *targname,
+	       unsigned int num,
+	       unsigned int format,
+	       struct xtc_handle *const handle)
+{
+	const struct xtables_target *target = NULL;
+	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(XT_STANDARD_TARGET,
+		         XTF_LOAD_MUST_SUCCEED);
+
+	t = ipt_get_target((struct ipt_entry *)fw);
+	flags = fw->ip.flags;
+
+	if (format & FMT_LINENUMBERS)
+		printf(FMT("%-4u ", "%u "), num);
+
+	if (!(format & FMT_NOCOUNTS)) {
+		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 & XT_INV_PROTO ? '!' : ' ', stdout);
+	{
+		const char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+		if (pname)
+			printf(FMT("%-5s", "%s "), pname);
+		else
+			printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+	}
+
+	if (format & FMT_OPTIONS) {
+		if (format & FMT_NOTABLE)
+			fputs("opt ", stdout);
+		fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+		fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+		fputc(' ', stdout);
+	}
+
+	if (format & FMT_VIA) {
+		char iface[IFNAMSIZ+2];
+
+		if (fw->ip.invflags & IPT_INV_VIA_IN) {
+			iface[0] = '!';
+			iface[1] = '\0';
+		}
+		else iface[0] = '\0';
+
+		if (fw->ip.iniface[0] != '\0') {
+			strcat(iface, fw->ip.iniface);
+		}
+		else if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+		printf(FMT(" %-6s ","in %s "), iface);
+
+		if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+			iface[0] = '!';
+			iface[1] = '\0';
+		}
+		else iface[0] = '\0';
+
+		if (fw->ip.outiface[0] != '\0') {
+			strcat(iface, fw->ip.outiface);
+		}
+		else if (format & FMT_NUMERIC) strcat(iface, "*");
+		else strcat(iface, "any");
+		printf(FMT("%-6s ","out %s "), iface);
+	}
+
+	fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+	if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+		printf(FMT("%-19s ","%s "), "anywhere");
+	else {
+		if (format & FMT_NUMERIC)
+			strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.src));
+		else
+			strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.src));
+		strcat(buf, xtables_ipmask_to_numeric(&fw->ip.smsk));
+		printf(FMT("%-19s ","%s "), buf);
+	}
+
+	fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+	if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+		printf(FMT("%-19s ","-> %s"), "anywhere");
+	else {
+		if (format & FMT_NUMERIC)
+			strcpy(buf, xtables_ipaddr_to_numeric(&fw->ip.dst));
+		else
+			strcpy(buf, xtables_ipaddr_to_anyname(&fw->ip.dst));
+		strcat(buf, xtables_ipmask_to_numeric(&fw->ip.dmsk));
+		printf(FMT("%-19s ","-> %s"), buf);
+	}
+
+	if (format & FMT_NOTABLE)
+		fputs("  ", stdout);
+
+#ifdef IPT_F_GOTO
+	if(fw->ip.flags & IPT_F_GOTO)
+		printf("[goto] ");
+#endif
+
+	IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+	if (target) {
+		if (target->print)
+			/* Print the target information. */
+			target->print(&fw->ip, t, format & FMT_NUMERIC);
+	} else if (t->u.target_size != sizeof(*t))
+		printf("[%u bytes of unknown target data] ",
+		       (unsigned int)(t->u.target_size - sizeof(*t)));
+
+	if (!(format & FMT_NONEWLINE))
+		fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+		    struct xtc_handle *const h)
+{
+	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 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[],
+	     int verbose,
+	     struct xtc_handle *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ip.src.s_addr = saddrs[i].s_addr;
+		fw->ip.smsk.s_addr = smasks[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ip.dst.s_addr = daddrs[j].s_addr;
+			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= iptc_append_entry(chain, fw, handle);
+		}
+	}
+
+	return ret;
+}
+
+static int
+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 xtc_handle *handle)
+{
+	fw->ip.src.s_addr = saddr->s_addr;
+	fw->ip.dst.s_addr = daddr->s_addr;
+	fw->ip.smsk.s_addr = smask->s_addr;
+	fw->ip.dmsk.s_addr = dmask->s_addr;
+
+	if (verbose)
+		print_firewall_line(fw, handle);
+	return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const xt_chainlabel chain,
+	     struct ipt_entry *fw,
+	     unsigned int rulenum,
+	     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[],
+	     int verbose,
+	     struct xtc_handle *handle)
+{
+	unsigned int i, j;
+	int ret = 1;
+
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ip.src.s_addr = saddrs[i].s_addr;
+		fw->ip.smsk.s_addr = smasks[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ip.dst.s_addr = daddrs[j].s_addr;
+			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+		}
+	}
+
+	return ret;
+}
+
+static unsigned char *
+make_delete_mask(const struct xtables_rule_match *matches,
+		 const struct xtables_target *target)
+{
+	/* Establish mask for comparison */
+	unsigned int size;
+	const struct xtables_rule_match *matchp;
+	unsigned char *mask, *mptr;
+
+	size = sizeof(struct ipt_entry);
+	for (matchp = matches; matchp; matchp = matchp->next)
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+
+	mask = xtables_calloc(1, size
+			 + XT_ALIGN(sizeof(struct xt_entry_target))
+			 + target->size);
+
+	memset(mask, 0xFF, sizeof(struct ipt_entry));
+	mptr = mask + sizeof(struct ipt_entry);
+
+	for (matchp = matches; matchp; matchp = matchp->next) {
+		memset(mptr, 0xFF,
+		       XT_ALIGN(sizeof(struct xt_entry_match))
+		       + matchp->match->userspacesize);
+		mptr += XT_ALIGN(sizeof(struct xt_entry_match)) + matchp->match->size;
+	}
+
+	memset(mptr, 0xFF,
+	       XT_ALIGN(sizeof(struct xt_entry_target))
+	       + target->userspacesize);
+
+	return mask;
+}
+
+static int
+delete_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[],
+	     int verbose,
+	     struct xtc_handle *handle,
+	     struct xtables_rule_match *matches,
+	     const struct xtables_target *target)
+{
+	unsigned int i, j;
+	int ret = 1;
+	unsigned char *mask;
+
+	mask = make_delete_mask(matches, target);
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ip.src.s_addr = saddrs[i].s_addr;
+		fw->ip.smsk.s_addr = smasks[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ip.dst.s_addr = daddrs[j].s_addr;
+			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= iptc_delete_entry(chain, fw, mask, handle);
+		}
+	}
+	free(mask);
+
+	return ret;
+}
+
+static int
+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 xtc_handle *handle,
+	    struct xtables_rule_match *matches,
+	    const struct xtables_target *target)
+{
+	unsigned int i, j;
+	int ret = 1;
+	unsigned char *mask;
+
+	mask = make_delete_mask(matches, target);
+	for (i = 0; i < nsaddrs; i++) {
+		fw->ip.src.s_addr = saddrs[i].s_addr;
+		fw->ip.smsk.s_addr = smasks[i].s_addr;
+		for (j = 0; j < ndaddrs; j++) {
+			fw->ip.dst.s_addr = daddrs[j].s_addr;
+			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
+			if (verbose)
+				print_firewall_line(fw, handle);
+			ret &= iptc_check_entry(chain, fw, mask, handle);
+		}
+	}
+
+	free(mask);
+	return ret;
+}
+
+int
+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;
+	char *chains;
+	unsigned int i, chaincount = 0;
+
+	chain = iptc_first_chain(handle);
+	while (chain) {
+		chaincount++;
+		chain = iptc_next_chain(handle);
+        }
+
+	chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount);
+	i = 0;
+	chain = iptc_first_chain(handle);
+	while (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(xt_chainlabel),
+				    handle) == 1)
+			continue;
+	        ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle);
+	}
+
+	free(chains);
+        return ret;
+}
+
+int
+flush_entries4(const xt_chainlabel chain, int verbose,
+	      struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain4(flush_entries4, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Flushing chain `%s'\n", chain);
+	return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain4(zero_entries, verbose, 1, handle);
+
+	if (verbose)
+		fprintf(stdout, "Zeroing chain `%s'\n", chain);
+	return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain4(const xt_chainlabel chain, int verbose,
+	     struct xtc_handle *handle)
+{
+	if (!chain)
+		return for_each_chain4(delete_chain4, verbose, 0, handle);
+
+	if (verbose)
+		fprintf(stdout, "Deleting chain `%s'\n", chain);
+	return iptc_delete_chain(chain, handle);
+}
+
+static int
+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;
+	const char *this;
+
+	format = FMT_OPTIONS;
+	if (!verbose)
+		format |= FMT_NOCOUNTS;
+	else
+		format |= FMT_VIA;
+
+	if (numeric)
+		format |= FMT_NUMERIC;
+
+	if (!expanded)
+		format |= FMT_KILOMEGAGIGA;
+
+	if (linenumbers)
+		format |= FMT_LINENUMBERS;
+
+	for (this = iptc_first_chain(handle);
+	     this;
+	     this = iptc_next_chain(handle)) {
+		const struct ipt_entry *i;
+		unsigned int num;
+
+		if (chain && strcmp(chain, this) != 0)
+			continue;
+
+		if (found) printf("\n");
+
+		if (!rulenum)
+			print_header(format, this, handle);
+		i = iptc_first_rule(this, handle);
+
+		num = 0;
+		while (i) {
+			num++;
+			if (!rulenum || num == rulenum)
+				print_firewall(i,
+					       iptc_get_target(i, handle),
+					       num,
+					       format,
+					       handle);
+			i = iptc_next_rule(i, handle);
+		}
+		found = 1;
+	}
+
+	errno = ENOENT;
+	return found;
+}
+
+static void print_proto(uint16_t proto, int invert)
+{
+	if (proto) {
+		unsigned int i;
+		const char *invertstr = invert ? " !" : "";
+
+		const struct protoent *pent = getprotobynumber(proto);
+		if (pent) {
+			printf("%s -p %s", invertstr, pent->p_name);
+			return;
+		}
+
+		for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
+			if (xtables_chain_protos[i].num == proto) {
+				printf("%s -p %s",
+				       invertstr, xtables_chain_protos[i].name);
+				return;
+			}
+
+		printf("%s -p %u", invertstr, proto);
+	}
+}
+
+#define IP_PARTS_NATIVE(n)			\
+(unsigned int)((n)>>24)&0xFF,			\
+(unsigned int)((n)>>16)&0xFF,			\
+(unsigned int)((n)>>8)&0xFF,			\
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+	    int invert)
+{
+	unsigned int i;
+
+	if (mask[0] == 0)
+		return;
+
+	printf("%s -%c ", invert ? " !" : "", letter);
+
+	for (i = 0; i < IFNAMSIZ; i++) {
+		if (mask[i] != 0) {
+			if (iface[i] != '\0')
+				printf("%c", iface[i]);
+		} else {
+			/* we can access iface[i-1] here, because
+			 * a few lines above we make sure that mask[0] != 0 */
+			if (iface[i-1] != '\0')
+				printf("+");
+			break;
+		}
+	}
+}
+
+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",
+			match->alias ? match->alias(e) : e->u.user.name);
+
+		/* some matches don't provide a save function */
+		if (match->save)
+			match->save(ip, e);
+	} else {
+		if (e->u.match_size) {
+			fprintf(stderr,
+				"Can't find library for match `%s'\n",
+				e->u.user.name);
+			exit(1);
+		}
+	}
+	return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(const char *prefix, uint32_t ip,
+		     uint32_t mask, int invert)
+{
+	uint32_t bits, hmask = ntohl(mask);
+	int i;
+
+	if (!mask && !ip && !invert)
+		return;
+
+	printf("%s %s %u.%u.%u.%u",
+		invert ? " !" : "",
+		prefix,
+		IP_PARTS(ip));
+
+	if (mask == 0xFFFFFFFFU) {
+		printf("/32");
+		return;
+	}
+
+	i    = 32;
+	bits = 0xFFFFFFFEU;
+	while (--i >= 0 && hmask != bits)
+		bits <<= 1;
+	if (i >= 0)
+		printf("/%u", i);
+	else
+		printf("/%u.%u.%u.%u", IP_PARTS(mask));
+}
+
+/* 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 xtc_handle *h, const char *chain, int counters)
+{
+	const struct xt_entry_target *t;
+	const char *target_name;
+
+	/* print counters for iptables-save */
+	if (counters > 0)
+		printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+	/* print chain name */
+	printf("-A %s", chain);
+
+	/* Print IP part. */
+	print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
+			e->ip.invflags & IPT_INV_SRCIP);
+
+	print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
+			e->ip.invflags & IPT_INV_DSTIP);
+
+	print_iface('i', e->ip.iniface, e->ip.iniface_mask,
+		    e->ip.invflags & IPT_INV_VIA_IN);
+
+	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 & XT_INV_PROTO);
+
+	if (e->ip.flags & IPT_F_FRAG)
+		printf("%s -f",
+		       e->ip.invflags & IPT_INV_FRAG ? " !" : "");
+
+	/* Print matchinfo part */
+	if (e->target_offset) {
+		IPT_MATCH_ITERATE(e, print_match_save, &e->ip);
+	}
+
+	/* print counters for iptables -R */
+	if (counters < 0)
+		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+	/* Print target name and targinfo part */
+	target_name = iptc_get_target(e, h);
+	t = ipt_get_target((struct ipt_entry *)e);
+	if (t->u.user.name[0]) {
+		const struct xtables_target *target =
+			xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
+
+		if (!target) {
+			fprintf(stderr, "Can't find library for target `%s'\n",
+				t->u.user.name);
+			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 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 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 xt_chainlabel chain, int rulenum, int counters,
+	     struct xtc_handle *handle)
+{
+	const char *this = NULL;
+	int found = 0;
+
+	if (counters)
+	    counters = -1;		/* iptables -c format */
+
+	/* Dump out chain names first,
+	 * thereby preventing dependency conflicts */
+	if (!rulenum) for (this = iptc_first_chain(handle);
+	     this;
+	     this = iptc_next_chain(handle)) {
+		if (chain && strcmp(this, chain) != 0)
+			continue;
+
+		if (iptc_builtin(this, handle)) {
+			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);
+			printf("\n");
+		} else {
+			printf("-N %s\n", this);
+		}
+	}
+
+	for (this = iptc_first_chain(handle);
+	     this;
+	     this = iptc_next_chain(handle)) {
+		const struct ipt_entry *e;
+		int num = 0;
+
+		if (chain && strcmp(this, chain) != 0)
+			continue;
+
+		/* Dump out rules */
+		e = iptc_first_rule(this, handle);
+		while(e) {
+			num++;
+			if (!rulenum || num == rulenum)
+			    print_rule4(e, handle, this, counters);
+			e = iptc_next_rule(e, handle);
+		}
+		found = 1;
+	}
+
+	errno = ENOENT;
+	return found;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+	       struct xtables_rule_match *matches,
+	       struct xt_entry_target *target)
+{
+	unsigned int size;
+	struct xtables_rule_match *matchp;
+	struct ipt_entry *e;
+
+	size = sizeof(struct ipt_entry);
+	for (matchp = matches; matchp; matchp = matchp->next)
+		size += matchp->match->m->u.match_size;
+
+	e = xtables_malloc(size + target->u.target_size);
+	*e = *fw;
+	e->target_offset = size;
+	e->next_offset = size + target->u.target_size;
+
+	size = 0;
+	for (matchp = matches; matchp; matchp = matchp->next) {
+		memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
+		size += matchp->match->m->u.match_size;
+	}
+	memcpy(e->elems + size, target, target->u.target_size);
+
+	return e;
+}
+
+static void command_jump(struct iptables_command_state *cs)
+{
+	size_t size;
+
+	set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags, cs->invert);
+	cs->jumpto = parse_target(optarg);
+	/* TRY_LOAD (may be chain name) */
+	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+
+	if (cs->target == NULL)
+		return;
+
+	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;
+	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;
+
+	xs_init_target(cs->target);
+
+	if (cs->target->x6_options != NULL)
+		opts = xtables_options_xfrm(iptables_globals.orig_opts, opts,
+					    cs->target->x6_options,
+					    &cs->target->option_offset);
+	else
+		opts = xtables_merge_options(iptables_globals.orig_opts, opts,
+					     cs->target->extra_opts,
+					     &cs->target->option_offset);
+	if (opts == NULL)
+		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+}
+
+static void command_match(struct iptables_command_state *cs)
+{
+	struct xtables_match *m;
+	size_t size;
+
+	if (cs->invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "unexpected ! flag before --match");
+
+	m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, &cs->matches);
+	size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
+	m->m = xtables_calloc(1, size);
+	m->m->u.match_size = size;
+	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;
+
+	xs_init_match(m);
+	if (m == m->next)
+		return;
+	/* Merge options for non-cloned matches */
+	if (m->x6_options != NULL)
+		opts = xtables_options_xfrm(iptables_globals.orig_opts, opts,
+					    m->x6_options, &m->option_offset);
+	else if (m->extra_opts != NULL)
+		opts = xtables_merge_options(iptables_globals.orig_opts, opts,
+					     m->extra_opts, &m->option_offset);
+	if (opts == NULL)
+		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+}
+
+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;
+	unsigned int nsaddrs = 0, ndaddrs = 0;
+	struct in_addr *saddrs = NULL, *smasks = NULL;
+	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;
+	unsigned int rulenum = 0, command = 0;
+	const char *pcnt = NULL, *bcnt = NULL;
+	int ret = 1;
+	struct xtables_match *m;
+	struct xtables_rule_match *matchp;
+	struct xtables_target *t;
+	unsigned long long cnt;
+
+	memset(&cs, 0, sizeof(cs));
+	cs.jumpto = "";
+	cs.argv = argv;
+
+	/* re-set optind to 0 in case do_command4 gets called
+	 * a second time */
+	optind = 0;
+
+	/* clear mflags in case do_command4 gets called a second time
+	 * (we clear the global list of all matches for security)*/
+	for (m = xtables_matches; m; m = m->next)
+		m->mflags = 0;
+
+	for (t = xtables_targets; t; t = t->next) {
+		t->tflags = 0;
+		t->used = 0;
+	}
+
+	/* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+	opterr = 0;
+
+	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:fbvwnt:m:xc:g:46",
+					   opts, NULL)) != -1) {
+		switch (cs.c) {
+			/*
+			 * Command selection
+			 */
+		case 'A':
+			add_command(&command, CMD_APPEND, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			break;
+
+		case 'C':
+			add_command(&command, CMD_CHECK, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			break;
+
+		case 'D':
+			add_command(&command, CMD_DELETE, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!') {
+				rulenum = parse_rulenumber(argv[optind++]);
+				command = CMD_DELETE_NUM;
+			}
+			break;
+
+		case 'R':
+			add_command(&command, CMD_REPLACE, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires a rule number",
+					   cmd2char(CMD_REPLACE));
+			break;
+
+		case 'I':
+			add_command(&command, CMD_INSERT, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			else rulenum = 1;
+			break;
+
+		case 'L':
+			add_command(&command, CMD_LIST,
+				    CMD_ZERO | CMD_ZERO_NUM, cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			break;
+
+		case 'S':
+			add_command(&command, CMD_LIST_RULES,
+				    CMD_ZERO|CMD_ZERO_NUM, cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				rulenum = parse_rulenumber(argv[optind++]);
+			break;
+
+		case 'F':
+			add_command(&command, CMD_FLUSH, CMD_NONE,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'Z':
+			add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				&& argv[optind][0] != '!')
+				chain = argv[optind++];
+			if (optind < argc && argv[optind][0] != '-'
+				&& argv[optind][0] != '!') {
+				rulenum = parse_rulenumber(argv[optind++]);
+				command = CMD_ZERO_NUM;
+			}
+			break;
+
+		case 'N':
+			parse_chain(optarg);
+			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			break;
+
+		case 'X':
+			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+				    cs.invert);
+			if (optarg) chain = optarg;
+			else if (optind < argc && argv[optind][0] != '-'
+				 && argv[optind][0] != '!')
+				chain = argv[optind++];
+			break;
+
+		case 'E':
+			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				newname = argv[optind++];
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires old-chain-name and "
+					   "new-chain-name",
+					    cmd2char(CMD_RENAME_CHAIN));
+			break;
+
+		case 'P':
+			add_command(&command, CMD_SET_POLICY, CMD_NONE,
+				    cs.invert);
+			chain = optarg;
+			if (optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				policy = argv[optind++];
+			else
+				xtables_error(PARAMETER_PROBLEM,
+					   "-%c requires a chain and a policy",
+					   cmd2char(CMD_SET_POLICY));
+			break;
+
+		case 'h':
+			if (!optarg)
+				optarg = argv[optind];
+
+			/* iptables -p icmp -h */
+			if (!cs.matches && cs.protocol)
+				xtables_find_match(cs.protocol,
+					XTF_TRY_LOAD, &cs.matches);
+
+			exit_printhelp(cs.matches);
+
+			/*
+			 * Option selection
+			 */
+		case 'p':
+			set_option(&cs.options, OPT_PROTOCOL, &cs.fw.ip.invflags,
+				   cs.invert);
+
+			/* Canonicalize into lower case */
+			for (cs.protocol = optarg; *cs.protocol; cs.protocol++)
+				*cs.protocol = tolower(*cs.protocol);
+
+			cs.protocol = optarg;
+			cs.fw.ip.proto = xtables_parse_protocol(cs.protocol);
+
+			if (cs.fw.ip.proto == 0
+			    && (cs.fw.ip.invflags & XT_INV_PROTO))
+				xtables_error(PARAMETER_PROBLEM,
+					   "rule would never match protocol");
+			break;
+
+		case 's':
+			set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags,
+				   cs.invert);
+			shostnetworkmask = optarg;
+			break;
+
+		case 'd':
+			set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags,
+				   cs.invert);
+			dhostnetworkmask = optarg;
+			break;
+
+#ifdef IPT_F_GOTO
+		case 'g':
+			set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags,
+				   cs.invert);
+			cs.fw.ip.flags |= IPT_F_GOTO;
+			cs.jumpto = parse_target(optarg);
+			break;
+#endif
+
+		case 'j':
+			command_jump(&cs);
+			break;
+
+
+		case 'i':
+			if (*optarg == '\0')
+				xtables_error(PARAMETER_PROBLEM,
+					"Empty interface is likely to be "
+					"undesired");
+			set_option(&cs.options, OPT_VIANAMEIN, &cs.fw.ip.invflags,
+				   cs.invert);
+			xtables_parse_interface(optarg,
+					cs.fw.ip.iniface,
+					cs.fw.ip.iniface_mask);
+			break;
+
+		case 'o':
+			if (*optarg == '\0')
+				xtables_error(PARAMETER_PROBLEM,
+					"Empty interface is likely to be "
+					"undesired");
+			set_option(&cs.options, OPT_VIANAMEOUT, &cs.fw.ip.invflags,
+				   cs.invert);
+			xtables_parse_interface(optarg,
+					cs.fw.ip.outiface,
+					cs.fw.ip.outiface_mask);
+			break;
+
+		case 'f':
+			set_option(&cs.options, OPT_FRAGMENT, &cs.fw.ip.invflags,
+				   cs.invert);
+			cs.fw.ip.flags |= IPT_F_FRAG;
+			break;
+
+		case 'v':
+			if (!verbose)
+				set_option(&cs.options, OPT_VERBOSE,
+					   &cs.fw.ip.invflags, cs.invert);
+			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;
+
+		case 'n':
+			set_option(&cs.options, OPT_NUMERIC, &cs.fw.ip.invflags,
+				   cs.invert);
+			break;
+
+		case 't':
+			if (cs.invert)
+				xtables_error(PARAMETER_PROBLEM,
+					   "unexpected ! flag before --table");
+			*table = optarg;
+			break;
+
+		case 'x':
+			set_option(&cs.options, OPT_EXPANDED, &cs.fw.ip.invflags,
+				   cs.invert);
+			break;
+
+		case 'V':
+			if (cs.invert)
+				printf("Not %s ;-)\n", prog_vers);
+			else
+				printf("%s v%s\n",
+				       prog_name, prog_vers);
+			exit(0);
+
+		case '0':
+			set_option(&cs.options, OPT_LINENUMBERS, &cs.fw.ip.invflags,
+				   cs.invert);
+			break;
+
+		case 'M':
+			xtables_modprobe_program = optarg;
+			break;
+
+		case 'c':
+
+			set_option(&cs.options, OPT_COUNTERS, &cs.fw.ip.invflags,
+				   cs.invert);
+			pcnt = optarg;
+			bcnt = strchr(pcnt + 1, ',');
+			if (bcnt)
+			    bcnt++;
+			if (!bcnt && optind < argc && argv[optind][0] != '-'
+			    && argv[optind][0] != '!')
+				bcnt = argv[optind++];
+			if (!bcnt)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c requires packet and byte counter",
+					opt2char(OPT_COUNTERS));
+
+			if (sscanf(pcnt, "%llu", &cnt) != 1)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c packet counter not numeric",
+					opt2char(OPT_COUNTERS));
+			cs.fw.counters.pcnt = cnt;
+
+			if (sscanf(bcnt, "%llu", &cnt) != 1)
+				xtables_error(PARAMETER_PROBLEM,
+					"-%c byte counter not numeric",
+					opt2char(OPT_COUNTERS));
+			cs.fw.counters.bcnt = cnt;
+			break;
+
+		case '4':
+			/* This is indeed the IPv4 iptables */
+			break;
+
+		case '6':
+			/* This is not the IPv6 ip6tables */
+			if (line != -1)
+				return 1; /* success: line ignored */
+			fprintf(stderr, "This is the IPv4 version of iptables.\n");
+			exit_tryhelp(2);
+
+		case 1: /* non option */
+			if (optarg[0] == '!' && optarg[1] == '\0') {
+				if (cs.invert)
+					xtables_error(PARAMETER_PROBLEM,
+						   "multiple consecutive ! not"
+						   " allowed");
+				cs.invert = TRUE;
+				optarg[0] = '\0';
+				continue;
+			}
+			fprintf(stderr, "Bad argument `%s'\n", optarg);
+			exit_tryhelp(2);
+
+		default:
+			if (command_default(&cs, &iptables_globals) == 1)
+				/* cf. ip6tables.c */
+				continue;
+			break;
+		}
+		cs.invert = FALSE;
+	}
+
+	if (strcmp(*table, "nat") == 0 &&
+	    ((policy != NULL && strcmp(policy, "DROP") == 0) ||
+	    (cs.jumpto != NULL && strcmp(cs.jumpto, "DROP") == 0)))
+		xtables_error(PARAMETER_PROBLEM,
+			"\nThe \"nat\" table is not intended for filtering, "
+		        "the use of DROP is therefore inhibited.\n\n");
+
+	for (matchp = cs.matches; matchp; matchp = matchp->next)
+		xtables_option_mfcall(matchp->match);
+	if (cs.target != NULL)
+		xtables_option_tfcall(cs.target);
+
+	/* Fix me: must put inverse options checking here --MN */
+
+	if (optind < argc)
+		xtables_error(PARAMETER_PROBLEM,
+			   "unknown arguments found on commandline");
+	if (!command)
+		xtables_error(PARAMETER_PROBLEM, "no command specified");
+	if (cs.invert)
+		xtables_error(PARAMETER_PROBLEM,
+			   "nothing appropriate following !");
+
+	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND | CMD_CHECK)) {
+		if (!(cs.options & OPT_DESTINATION))
+			dhostnetworkmask = "0.0.0.0/0";
+		if (!(cs.options & OPT_SOURCE))
+			shostnetworkmask = "0.0.0.0/0";
+	}
+
+	if (shostnetworkmask)
+		xtables_ipparse_multiple(shostnetworkmask, &saddrs,
+					 &smasks, &nsaddrs);
+
+	if (dhostnetworkmask)
+		xtables_ipparse_multiple(dhostnetworkmask, &daddrs,
+					 &dmasks, &ndaddrs);
+
+	if ((nsaddrs > 1 || ndaddrs > 1) &&
+	    (cs.fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+		xtables_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+			   " source or destination IP addresses");
+
+	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
+			   "specify a unique address");
+
+	generic_opt_check(command, cs.options);
+
+	/* 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);
+
+	/* try to insmod the module if iptc_init failed */
+	if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1)
+		*handle = iptc_init(*table);
+
+	if (!*handle)
+		xtables_error(VERSION_PROBLEM,
+			   "can't initialize iptables table `%s': %s",
+			   *table, iptc_strerror(errno));
+
+	if (command == CMD_APPEND
+	    || command == CMD_DELETE
+	    || command == CMD_CHECK
+	    || command == CMD_INSERT
+	    || command == CMD_REPLACE) {
+		if (strcmp(chain, "PREROUTING") == 0
+		    || strcmp(chain, "INPUT") == 0) {
+			/* -o not valid with incoming packets. */
+			if (cs.options & OPT_VIANAMEOUT)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEOUT),
+					   chain);
+		}
+
+		if (strcmp(chain, "POSTROUTING") == 0
+		    || strcmp(chain, "OUTPUT") == 0) {
+			/* -i not valid with outgoing packets */
+			if (cs.options & OPT_VIANAMEIN)
+				xtables_error(PARAMETER_PROBLEM,
+					   "Can't use -%c with %s\n",
+					   opt2char(OPT_VIANAMEIN),
+					   chain);
+		}
+
+		if (cs.target && iptc_is_chain(cs.jumpto, *handle)) {
+			fprintf(stderr,
+				"Warning: using chain %s, not extension\n",
+				cs.jumpto);
+
+			if (cs.target->t)
+				free(cs.target->t);
+
+			cs.target = NULL;
+		}
+
+		/* If they didn't specify a target, or it's a chain
+		   name, use standard. */
+		if (!cs.target
+		    && (strlen(cs.jumpto) == 0
+			|| iptc_is_chain(cs.jumpto, *handle))) {
+			size_t size;
+
+			cs.target = xtables_find_target(XT_STANDARD_TARGET,
+					 XTF_LOAD_MUST_SUCCEED);
+
+			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;
+			xs_init_target(cs.target);
+		}
+
+		if (!cs.target) {
+			/* it is no chain, and we can't load a plugin.
+			 * We cannot know if the plugin is corrupt, non
+			 * existant OR if the user just misspelled a
+			 * chain. */
+#ifdef IPT_F_GOTO
+			if (cs.fw.ip.flags & IPT_F_GOTO)
+				xtables_error(PARAMETER_PROBLEM,
+					   "goto '%s' is not a chain\n",
+					   cs.jumpto);
+#endif
+			xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
+		} else {
+			e = generate_entry(&cs.fw, cs.matches, cs.target->t);
+			free(cs.target->t);
+		}
+	}
+
+	switch (command) {
+	case CMD_APPEND:
+		ret = append_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		break;
+	case CMD_DELETE:
+		ret = delete_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle, cs.matches, cs.target);
+		break;
+	case CMD_DELETE_NUM:
+		ret = iptc_delete_num_entry(chain, rulenum - 1, *handle);
+		break;
+	case CMD_CHECK:
+		ret = check_entry(chain, e,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle, cs.matches, cs.target);
+		break;
+	case CMD_REPLACE:
+		ret = replace_entry(chain, e, rulenum - 1,
+				    saddrs, smasks, daddrs, dmasks,
+				    cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_INSERT:
+		ret = insert_entry(chain, e, rulenum - 1,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		break;
+	case CMD_FLUSH:
+		ret = flush_entries4(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_ZERO:
+		ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_ZERO_NUM:
+		ret = iptc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_LIST:
+	case CMD_LIST|CMD_ZERO:
+	case CMD_LIST|CMD_ZERO_NUM:
+		ret = list_entries(chain,
+				   rulenum,
+				   cs.options&OPT_VERBOSE,
+				   cs.options&OPT_NUMERIC,
+				   cs.options&OPT_EXPANDED,
+				   cs.options&OPT_LINENUMBERS,
+				   *handle);
+		if (ret && (command & CMD_ZERO))
+			ret = zero_entries(chain,
+					   cs.options&OPT_VERBOSE, *handle);
+		if (ret && (command & CMD_ZERO_NUM))
+			ret = iptc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_LIST_RULES:
+	case CMD_LIST_RULES|CMD_ZERO:
+	case CMD_LIST_RULES|CMD_ZERO_NUM:
+		ret = list_rules(chain,
+				   rulenum,
+				   cs.options&OPT_VERBOSE,
+				   *handle);
+		if (ret && (command & CMD_ZERO))
+			ret = zero_entries(chain,
+					   cs.options&OPT_VERBOSE, *handle);
+		if (ret && (command & CMD_ZERO_NUM))
+			ret = iptc_zero_counter(chain, rulenum, *handle);
+		break;
+	case CMD_NEW_CHAIN:
+		ret = iptc_create_chain(chain, *handle);
+		break;
+	case CMD_DELETE_CHAIN:
+		ret = delete_chain4(chain, cs.options&OPT_VERBOSE, *handle);
+		break;
+	case CMD_RENAME_CHAIN:
+		ret = iptc_rename_chain(chain, newname,	*handle);
+		break;
+	case CMD_SET_POLICY:
+		ret = iptc_set_policy(chain, policy, cs.options&OPT_COUNTERS ? &cs.fw.counters : NULL, *handle);
+		break;
+	default:
+		/* We should never reach this... */
+		exit_tryhelp(2);
+	}
+
+	if (verbose > 1)
+		dump_entries(*handle);
+
+	xtables_rule_matches_free(&cs.matches);
+
+	if (e != NULL) {
+		free(e);
+		e = NULL;
+	}
+
+	free(saddrs);
+	free(smasks);
+	free(daddrs);
+	free(dmasks);
+	xtables_free_opts(1);
+
+	return ret;
+}
diff --git a/iptables/iptables.xslt b/iptables/iptables.xslt
new file mode 100644
index 0000000..d6a432c
--- /dev/null
+++ b/iptables/iptables.xslt
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Converts from simple xml iptables format to iptables-save format  
+     Copyright 2006 UfoMechanic 
+     Author: azez@ufomechanic.net 
+     This code is distributed and licensed under the terms of GNU GPL v2
+     
+     This sample usage outputs roughly want goes in
+       iptables-save | iptables-xml -c | xsltproc iptables.xslt -
+     -->
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:output method = "text" />
+  <xsl:strip-space elements="*" />
+
+  <!-- output conditions of a rule but not an action -->
+  <xsl:template match="iptables-rules/table/chain/rule/conditions/*">
+    <!-- <match> is the psuedo module when a match module doesn't need to be loaded
+         and when -m does not need to be inserted -->
+    <xsl:if test="name() != 'match'">
+      <xsl:text> -m </xsl:text><xsl:value-of select="name()"/>
+    </xsl:if>
+    <xsl:apply-templates select="node()"/>
+  </xsl:template>
+
+  <!-- delete the actions or conditions containers, and process child nodes -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions|table/chain/rule/conditions">
+    <xsl:apply-templates select="*"/>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table/chain/rule/actions/goto">
+    <xsl:text> -g </xsl:text>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  <xsl:template match="iptables-rules/table/chain/rule/actions/call">
+    <xsl:text> -j </xsl:text>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  <!-- all other actions are module actions -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions/*">
+    <xsl:text> -j </xsl:text><xsl:value-of select="name()"/>
+    <xsl:apply-templates select="*"/>
+    <xsl:text>&#xA;</xsl:text>
+  </xsl:template>
+  
+  <!-- all child action nodes -->
+  <xsl:template match="iptables-rules/table/chain/rule/actions//*|iptables-rules/table/chain/rule/conditions//*" priority="0">
+    <xsl:if test="@invert=1"><xsl:text> !</xsl:text></xsl:if>
+    <xsl:text> -</xsl:text>
+    <!-- if length of name is 1 character, then only do 1 - not 2 -->
+    <xsl:if test="string-length(name())&gt;1">
+      <xsl:text>-</xsl:text>
+    </xsl:if>
+    <xsl:value-of select="name()"/>
+    <xsl:text> </xsl:text>
+    <xsl:apply-templates select="node()"/>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table/chain/rule/actions/call/*|iptables-rules/table/chain/rule/actions/goto/*">
+    <xsl:value-of select="name()"/>
+    <!-- I bet there are no child nodes, should we risk it? -->
+    <xsl:apply-templates select="node()"/>
+  </xsl:template>
+
+  <!-- output the head of the rule, and any conditions -->
+  <xsl:template name="rule-head">
+    <xsl:if test="string-length(@packet-count)+string-length(@byte-count)">
+      <xsl:call-template name="counters"><xsl:with-param name="node" select="."/></xsl:call-template>
+      <xsl:text> </xsl:text>
+    </xsl:if>
+    <xsl:text>-A </xsl:text><!-- a rule must be under a chain -->
+    <xsl:value-of select="../@name" />
+    <xsl:apply-templates select="conditions"/>
+  </xsl:template>
+
+  <!-- Output a single rule, perhaps as multiple rules if we have more than one action -->
+  <xsl:template match="iptables-rules/table/chain/rule">
+    <xsl:choose>
+      <xsl:when test="count(actions/*)&gt;0">
+        <xsl:for-each select="actions/*">
+          <!-- and a for-each to re-select the rule as the current node, to write the rule-head -->
+          <xsl:for-each select="../..">
+            <xsl:call-template name="rule-head"/>
+          </xsl:for-each>
+          <!-- now write the this action -->
+          <xsl:apply-templates select="."/>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- no need to loop if there are no actions, just output conditions -->
+        <xsl:call-template name="rule-head"/>
+        <xsl:text>&#xA;</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="iptables-rules/table">
+    <xsl:text># Generated by iptables.xslt&#xA;</xsl:text>
+    <xsl:text>*</xsl:text><xsl:value-of select="@name"/><xsl:text>&#xA;</xsl:text>
+    <!-- Loop through each chain and output the chain header -->
+    <xsl:for-each select="chain">
+      <xsl:text>:</xsl:text>
+      <xsl:value-of select="@name"/>
+      <xsl:text> </xsl:text>
+      <xsl:choose>
+        <xsl:when test="not(string-length(@policy))"><xsl:text>-</xsl:text></xsl:when>
+        <xsl:otherwise><xsl:value-of select="@policy"/></xsl:otherwise>
+      </xsl:choose>
+      <xsl:text> </xsl:text>
+      <xsl:call-template name="counters"><xsl:with-param name="node" select="."/></xsl:call-template>
+      <xsl:text>&#xA;</xsl:text>
+    </xsl:for-each>
+    <!-- Loop through each chain and output the rules -->
+    <xsl:apply-templates select="node()"/>
+    <xsl:text>COMMIT&#xA;# Completed&#xA;</xsl:text>
+  </xsl:template>
+  
+  <xsl:template name="counters">
+    <xsl:param name="node"/>
+    <xsl:text>[</xsl:text>
+    <xsl:if test="string-length($node/@packet-count)"><xsl:value-of select="$node/@packet-count"/></xsl:if>
+    <xsl:if test="string-length($node/@packet-count)=0">0</xsl:if>
+    <xsl:text>:</xsl:text>
+    <xsl:if test="string-length($node/@byte-count)"><xsl:value-of select="$node/@byte-count"/></xsl:if>
+    <xsl:if test="string-length($node/@byte-count)=0">0</xsl:if>
+    <xsl:text>]</xsl:text>
+  </xsl:template>  
+  
+  <!-- the bit that automatically recurses for us, NOTE: we use * not node(), we don't want to copy every white space text -->
+  <xsl:template match="@*|node()">
+    <xsl:copy>
+      <!-- with libxslt xsltproc we can't do @*|node() or the nodes may get processed before the attributes -->
+      <xsl:apply-templates select="@*"/>
+      <xsl:apply-templates select="node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:transform>
diff --git a/iptables/xshared.c b/iptables/xshared.c
new file mode 100644
index 0000000..6c9992e
--- /dev/null
+++ b/iptables/xshared.c
@@ -0,0 +1,271 @@
+#include <getopt.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdint.h>
+#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
+ * specified matches and targets.
+ */
+void print_extension_helps(const struct xtables_target *t,
+    const struct xtables_rule_match *m)
+{
+	for (; t != NULL; t = t->next) {
+		if (t->used) {
+			printf("\n");
+			if (t->help == NULL)
+				printf("%s does not take any options\n",
+				       t->name);
+			else
+				t->help();
+		}
+	}
+	for (; m != NULL; m = m->next) {
+		printf("\n");
+		if (m->match->help == NULL)
+			printf("%s does not take any options\n",
+			       m->match->name);
+		else
+			m->match->help();
+	}
+}
+
+const char *
+proto_to_name(uint8_t proto, int nolookup)
+{
+	unsigned int i;
+
+	if (proto && !nolookup) {
+		struct protoent *pent = getprotobynumber(proto);
+		if (pent)
+			return pent->p_name;
+	}
+
+	for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
+		if (xtables_chain_protos[i].num == proto)
+			return xtables_chain_protos[i].name;
+
+	return NULL;
+}
+
+static struct xtables_match *
+find_proto(const char *pname, enum xtables_tryload tryload,
+	   int nolookup, struct xtables_rule_match **matches)
+{
+	unsigned int proto;
+
+	if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) {
+		const char *protoname = proto_to_name(proto, nolookup);
+
+		if (protoname)
+			return xtables_find_match(protoname, tryload, matches);
+	} else
+		return xtables_find_match(pname, tryload, matches);
+
+	return NULL;
+}
+
+/*
+ * Some explanations (after four different bugs in 3 different releases): If
+ * we encounter a parameter, that has not been parsed yet, it's not an option
+ * of an explicitly loaded match or a target. However, we support implicit
+ * loading of the protocol match extension. '-p tcp' means 'l4 proto 6' and at
+ * the same time 'load tcp protocol match on demand if we specify --dport'.
+ *
+ * To make this work, we need to make sure:
+ * - the parameter has not been parsed by a match (m above)
+ * - a protocol has been specified
+ * - the protocol extension has not been loaded yet, or is loaded and unused
+ *   [think of ip6tables-restore!]
+ * - the protocol extension can be successively loaded
+ */
+static bool should_load_proto(struct iptables_command_state *cs)
+{
+	if (cs->protocol == NULL)
+		return false;
+	if (find_proto(cs->protocol, XTF_DONT_LOAD,
+	    cs->options & OPT_NUMERIC, NULL) == NULL)
+		return true;
+	return !cs->proto_used;
+}
+
+struct xtables_match *load_proto(struct iptables_command_state *cs)
+{
+	if (!should_load_proto(cs))
+		return NULL;
+	return find_proto(cs->protocol, XTF_TRY_LOAD,
+			  cs->options & OPT_NUMERIC, &cs->matches);
+}
+
+int command_default(struct iptables_command_state *cs,
+		    struct xtables_globals *gl)
+{
+	struct xtables_rule_match *matchp;
+	struct xtables_match *m;
+
+	if (cs->target != NULL &&
+	    (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
+	    cs->c >= cs->target->option_offset &&
+	    cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
+		xtables_option_tpcall(cs->c, cs->argv, cs->invert,
+				      cs->target, &cs->fw);
+		return 0;
+	}
+
+	for (matchp = cs->matches; matchp; matchp = matchp->next) {
+		m = matchp->match;
+
+		if (matchp->completed ||
+		    (m->x6_parse == NULL && m->parse == NULL))
+			continue;
+		if (cs->c < matchp->match->option_offset ||
+		    cs->c >= matchp->match->option_offset + XT_OPTION_OFFSET_SCALE)
+			continue;
+		xtables_option_mpcall(cs->c, cs->argv, cs->invert, m, &cs->fw);
+		return 0;
+	}
+
+	/* Try loading protocol */
+	m = load_proto(cs);
+	if (m != NULL) {
+		size_t size;
+
+		cs->proto_used = 1;
+
+		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;
+		xs_init_match(m);
+
+		if (m->x6_options != NULL)
+			gl->opts = xtables_options_xfrm(gl->orig_opts,
+							gl->opts,
+							m->x6_options,
+							&m->option_offset);
+		else
+			gl->opts = xtables_merge_options(gl->orig_opts,
+							 gl->opts,
+							 m->extra_opts,
+							 &m->option_offset);
+		if (gl->opts == NULL)
+			xtables_error(OTHER_PROBLEM, "can't alloc memory!");
+		optind--;
+		/* Indicate to rerun getopt *immediately* */
+ 		return 1;
+	}
+
+	if (cs->c == ':')
+		xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
+		              "requires an argument", cs->argv[optind-1]);
+	if (cs->c == '?')
+		xtables_error(PARAMETER_PROBLEM, "unknown option "
+			      "\"%s\"", cs->argv[optind-1]);
+	xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
+	return 0;
+}
+
+static mainfunc_t subcmd_get(const char *cmd, const struct subcommand *cb)
+{
+	for (; cb->name != NULL; ++cb)
+		if (strcmp(cb->name, cmd) == 0)
+			return cb->main;
+	return NULL;
+}
+
+int subcmd_main(int argc, char **argv, const struct subcommand *cb)
+{
+	const char *cmd = basename(*argv);
+	mainfunc_t f = subcmd_get(cmd, cb);
+
+	if (f == NULL && argc > 1) {
+		/*
+		 * Unable to find a main method for our command name?
+		 * Let's try again with the first argument!
+		 */
+		++argv;
+		--argc;
+		f = subcmd_get(*argv, cb);
+	}
+
+	/* now we should have a valid function pointer */
+	if (f != NULL)
+		return f(argc, argv);
+
+	fprintf(stderr, "ERROR: No valid subcommand given.\nValid subcommands:\n");
+	for (; cb->name != NULL; ++cb)
+		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
new file mode 100644
index 0000000..1e2b9b8
--- /dev/null
+++ b/iptables/xshared.h
@@ -0,0 +1,91 @@
+#ifndef IPTABLES_XSHARED_H
+#define IPTABLES_XSHARED_H 1
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+enum {
+	OPT_NONE        = 0,
+	OPT_NUMERIC     = 1 << 0,
+	OPT_SOURCE      = 1 << 1,
+	OPT_DESTINATION = 1 << 2,
+	OPT_PROTOCOL    = 1 << 3,
+	OPT_JUMP        = 1 << 4,
+	OPT_VERBOSE     = 1 << 5,
+	OPT_EXPANDED    = 1 << 6,
+	OPT_VIANAMEIN   = 1 << 7,
+	OPT_VIANAMEOUT  = 1 << 8,
+	OPT_LINENUMBERS = 1 << 9,
+	OPT_COUNTERS    = 1 << 10,
+};
+
+struct xtables_globals;
+struct xtables_rule_match;
+struct xtables_target;
+
+/**
+ * xtables_afinfo - protocol family dependent information
+ * @kmod:		kernel module basename (e.g. "ip_tables")
+ * @proc_exists:	file which exists in procfs when module already loaded
+ * @libprefix:		prefix of .so library name (e.g. "libipt_")
+ * @family:		nfproto family
+ * @ipproto:		used by setsockopt (e.g. IPPROTO_IP)
+ * @so_rev_match:	optname to check revision support of match
+ * @so_rev_target:	optname to check revision support of target
+ */
+struct xtables_afinfo {
+	const char *kmod;
+	const char *proc_exists;
+	const char *libprefix;
+	uint8_t family;
+	uint8_t ipproto;
+	int so_rev_match;
+	int so_rev_target;
+};
+
+struct iptables_command_state {
+	union {
+		struct ipt_entry fw;
+		struct ip6t_entry fw6;
+	};
+	int invert;
+	int c;
+	unsigned int options;
+	struct xtables_rule_match *matches;
+	struct xtables_target *target;
+	char *protocol;
+	int proto_used;
+	const char *jumpto;
+	char **argv;
+};
+
+typedef int (*mainfunc_t)(int, char **);
+
+struct subcommand {
+	const char *name;
+	mainfunc_t main;
+};
+
+enum {
+	XT_OPTION_OFFSET_SCALE = 256,
+};
+
+extern void print_extension_helps(const struct xtables_target *,
+	const struct xtables_rule_match *);
+extern const char *proto_to_name(uint8_t, int);
+extern int command_default(struct iptables_command_state *,
+	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;
+
+#endif /* IPTABLES_XSHARED_H */
diff --git a/iptables/xtables-multi.c b/iptables/xtables-multi.c
new file mode 100644
index 0000000..8014d5f
--- /dev/null
+++ b/iptables/xtables-multi.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xshared.h"
+
+#include "xtables-multi.h"
+
+#ifdef ENABLE_IPV4
+#include "iptables-multi.h"
+#endif
+
+#ifdef ENABLE_IPV6
+#include "ip6tables-multi.h"
+#endif
+
+static const struct subcommand multi_subcommands[] = {
+#ifdef ENABLE_IPV4
+	{"iptables",            iptables_main},
+	{"main4",               iptables_main},
+	{"iptables-save",       iptables_save_main},
+	{"save4",               iptables_save_main},
+	{"iptables-restore",    iptables_restore_main},
+	{"restore4",            iptables_restore_main},
+#endif
+	{"iptables-xml",        iptables_xml_main},
+	{"xml",                 iptables_xml_main},
+#ifdef ENABLE_IPV6
+	{"ip6tables",           ip6tables_main},
+	{"main6",               ip6tables_main},
+	{"ip6tables-save",      ip6tables_save_main},
+	{"save6",               ip6tables_save_main},
+	{"ip6tables-restore",   ip6tables_restore_main},
+	{"restore6",            ip6tables_restore_main},
+#endif
+	{NULL},
+};
+
+int main(int argc, char **argv)
+{
+	return subcmd_main(argc, argv, multi_subcommands);
+}
diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h
new file mode 100644
index 0000000..615724b
--- /dev/null
+++ b/iptables/xtables-multi.h
@@ -0,0 +1,6 @@
+#ifndef _XTABLES_MULTI_H
+#define _XTABLES_MULTI_H 1
+
+extern int iptables_xml_main(int, char **);
+
+#endif /* _XTABLES_MULTI_H */
diff --git a/iptables/xtables.pc.in b/iptables/xtables.pc.in
new file mode 100644
index 0000000..43f35d5
--- /dev/null
+++ b/iptables/xtables.pc.in
@@ -0,0 +1,13 @@
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+xtlibdir=@xtlibdir@
+includedir=@includedir@
+
+Name:		xtables
+Description:	Shared Xtables code for extensions and iproute2
+Version:	@PACKAGE_VERSION@
+Cflags:		-I${includedir}
+Libs:		-L${libdir} -lxtables
+Libs.private:	-ldl
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
new file mode 100644
index 0000000..9e3a2ca
--- /dev/null
+++ b/libipq/Makefile.am
@@ -0,0 +1,13 @@
+# -*- Makefile -*-
+
+AM_CFLAGS = ${regular_CFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include
+
+libipq_la_SOURCES = libipq.c
+lib_LTLIBRARIES   = libipq.la
+man_MANS         = ipq_create_handle.3 ipq_destroy_handle.3 ipq_errstr.3 \
+                   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/ipq_create_handle.3 b/libipq/ipq_create_handle.3
new file mode 100644
index 0000000..11ef95c
--- /dev/null
+++ b/libipq/ipq_create_handle.3
@@ -0,0 +1,82 @@
+.TH IPQ_CREATE_HANDLE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_create_handle, ipq_destroy_handle \(em create and destroy libipq handles.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "struct ipq_handle *ipq_create_handle(u_int32_t " flags ", u_int32_t " protocol ");"
+.br
+.BI "int ipq_destroy_handle(struct ipq_handle *" h );
+.SH DESCRIPTION
+The
+.B ipq_create_handle
+function initialises libipq for an application, attempts to bind to the
+Netlink socket used by ip_queue, and returns an opaque context handle.  It
+should be the first libipq function to be called by an application.  The
+handle returned should be used in all subsequent library calls which 
+require a handle parameter.
+.PP
+The
+.I flags
+parameter is not currently used and should be set to zero by the application
+for forward compatibility.
+.PP
+The
+.I protocol
+parameter is used to specify the protocol of the packets to be queued.
+Valid values are NFPROTO_IPV4 for IPv4 and NFPROTO_IPV6 for IPv6. Currently,
+only one protocol may be queued at a time for a handle.
+.PP
+The
+.B ipq_destroy_handle
+function frees up resources allocated by
+.BR ipq_create_handle ,
+and should be used when the handle is no longer required by the application.
+.SH RETURN VALUES
+On success,
+.B ipq_create_handle
+returns a pointer to a context handle.
+.br
+On failure, NULL is returned.
+.PP
+On success,
+.B ipq_destroy_handle
+returns zero.
+.br
+On failure, \-1 is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_destroy_handle.3 b/libipq/ipq_destroy_handle.3
new file mode 100644
index 0000000..29dcd98
--- /dev/null
+++ b/libipq/ipq_destroy_handle.3
@@ -0,0 +1 @@
+.so man3/ipq_create_handle.3
diff --git a/libipq/ipq_errstr.3 b/libipq/ipq_errstr.3
new file mode 100644
index 0000000..c8d67ce
--- /dev/null
+++ b/libipq/ipq_errstr.3
@@ -0,0 +1,64 @@
+.TH IPQ_ERRSTR 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_errstr, ipq_perror \(em libipq error handling routines
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "char *ipq_errstr(" void );
+.br
+.BI "void ipq_perror(const char *" s );
+.SH DESCRIPTION
+The
+.B ipq_errstr
+function returns a descriptive error message based on the current
+value of the internal
+.B ipq_errno
+variable.  All libipq API functions set this internal variable
+upon failure.
+.PP
+The
+.B ipq_perror
+function prints an error message to stderr corresponding to the
+current value of the internal
+.B ipq_error
+variable, and the global
+.B errno
+variable (if set).  The error message is prefixed with the string
+.I s
+as supplied by the application. If
+.I s
+is NULL, the error message is prefixed with the string "ERROR".
+.SH RETURN VALUE
+.B ipq_errstr
+returns an error message as outlined above.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_get_msgerr.3 b/libipq/ipq_get_msgerr.3
new file mode 100644
index 0000000..8a28be3
--- /dev/null
+++ b/libipq/ipq_get_msgerr.3
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_get_packet.3 b/libipq/ipq_get_packet.3
new file mode 100644
index 0000000..8a28be3
--- /dev/null
+++ b/libipq/ipq_get_packet.3
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_message_type.3 b/libipq/ipq_message_type.3
new file mode 100644
index 0000000..89d8817
--- /dev/null
+++ b/libipq/ipq_message_type.3
@@ -0,0 +1,134 @@
+.TH IPQ_MESSAGE_TYPE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_message_type, ipq_get_packet, ipq_getmsgerr \(em query queue messages
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_message_type(const unsigned char *" buf ");"
+.br
+.BI "ipq_packet_msg_t *ipq_get_packet(const unsigned char *" buf ");"
+.br
+.BI "int ipq_get_msgerr(const unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_message_type
+function returns the type of queue message returned to userspace
+via
+.BR ipq_read .
+.PP
+.B ipq_message_type
+should always be called following a successful call to
+.B ipq_read
+to determine whether the message is a packet message or an
+error message. The
+.I buf
+parameter should be the same data obtained from
+the previous call to
+.BR ipq_read .
+.PP
+.B ipq_message_type
+will return one of the following values:
+.TP
+.B NLMSG_ERROR
+An error message generated by the Netlink transport.
+.PP
+.TP
+.B IPQM_PACKET
+A packet message containing packet metadata and optional packet payload data.
+.PP
+The
+.B ipq_get_packet
+function should be called if
+.B ipq_message_type
+returns
+.BR IPQM_PACKET .
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The pointer returned by
+.B ipq_get_packet
+points to a packet message, which is declared as follows:
+.PP
+.RS
+.nf
+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 */
+	unsigned short 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;
+.fi
+.RE
+.PP
+Each of these fields may be read by the application.  If the queue mode
+is
+.B IPQ_COPY_PACKET
+and the
+.I data_len
+value is greater than zero, the packet payload contents may be accessed
+in the memory following the
+.B ipq_packet_msg_t
+structure to a range of
+.I data_len.
+.PP
+The
+.I packet_id
+field contains a packet identifier to be used when calling
+.BR ipq_set_verdict .
+.PP
+The
+.B ipq_get_msgerr
+function should be called if
+.B ipq_message_type
+returns
+.BR NLMSG_ERROR.
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The value returned by
+.B ipq_get_msgerr
+is set by higher level kernel code and corresponds to standard
+.B errno
+values.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_perror.3 b/libipq/ipq_perror.3
new file mode 100644
index 0000000..6efd53d
--- /dev/null
+++ b/libipq/ipq_perror.3
@@ -0,0 +1 @@
+.so man3/ipq_errstr.3
diff --git a/libipq/ipq_read.3 b/libipq/ipq_read.3
new file mode 100644
index 0000000..26ab9f9
--- /dev/null
+++ b/libipq/ipq_read.3
@@ -0,0 +1,104 @@
+.TH IPQ_READ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_read \(em read queue messages from ip_queue and read into supplied buffer
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "ssize_t ipq_read(const struct ipq_handle *" h ", unsigned char *" buf ", size_t " len ", int " timeout ");"
+.SH DESCRIPTION
+The
+.B ipq_read
+function reads a queue message from the kernel and copies it to
+the memory pointed to by 
+.I buf
+to a maximum length of
+. IR len .
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The caller is responsible for ensuring that the memory pointed to by
+.I buf
+is large enough to contain
+.I len
+bytes.
+.PP
+The
+.I timeout
+parameter may be used to set a timeout for the operation, specified in microseconds.
+This is implemented internally by the library via the
+.BR select
+system call.  A value of zero provides normal, backwards-compatible blocking behaviour
+with no timeout.  A negative value causes the function to return immediately.
+.PP
+Data returned via
+.I buf
+should not be accessed directly.  Use the 
+.BR ipq_message_type ,
+.BR ipq_get_packet ", and"
+.BR ipq_get_msgerr
+functions to access the queue message in the buffer.
+.SH RETURN VALUE
+On failure, \-1 is returned.
+.br
+On success, a non-zero positive value is returned when no timeout
+value is specified.
+.br
+On success with a timeout value specified, zero is returned if no data
+was available to read, or if a non-blocked signal was caught.  In the
+latter case, the global
+.B errno
+value will be set to 
+.BR EINTR .
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+While the
+.B ipq_read
+function may return successfully, the queue message copied to the buffer
+may itself be an error message from a higher level kernel component.  Use
+.B ipq_message_type
+to determine if it is an error message, and
+.B ipq_get_msgerr
+to access the value of the message.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the timeout feature, which appeared in the 1.2.4 release of iptables.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3),
+.BR select (2).
+
diff --git a/libipq/ipq_set_mode.3 b/libipq/ipq_set_mode.3
new file mode 100644
index 0000000..0edd3c0
--- /dev/null
+++ b/libipq/ipq_set_mode.3
@@ -0,0 +1,105 @@
+.TH IPQ_SET_MODE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_set_mode \(em set the ip_queue queuing mode
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_mode(const struct ipq_handle *" h ", u_int8_t " mode ", size_t " range );
+.SH DESCRIPTION
+The
+.B ipq_set_mode
+function sends a message to the kernel ip_queue module, specifying whether
+packet metadata only, or packet payloads as well as metadata should be copied to
+userspace.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I mode
+parameter must be one of:
+.TP
+.B IPQ_COPY_META
+Copy only packet metadata to userspace.
+.TP
+.B IPQ_COPY_PACKET
+Copy packet metadata and packet payloads to userspace.
+.PP
+The
+.I range
+parameter is used to specify how many bytes of the payload to copy
+to userspace.  It is only valid for
+.B IPQ_COPY_PACKET
+mode and is otherwise ignored.  The maximum useful value for
+.I range
+is 65535 (greater values will be clamped to this by ip_queue).
+.PP
+.B ipq_set_mode
+is usually used immediately following
+.B ipq_create_handle
+to enable the flow of packets to userspace.
+.PP
+Note that as the underlying Netlink messaging transport is connectionless,
+the ip_queue module does not know that a userspace application is ready to
+communicate until it receives a message such as this.
+.SH RETURN VALUE
+On failure, \-1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+A relatively common failure may occur if the ip_queue module is not loaded.
+In this case, the following code excerpt:
+.PP
+.RS
+.nf
+status = ipq_set_mode(h, IPQ_COPY_META, 0);
+if (status < 0) {
+	ipq_perror("myapp");
+	ipq_destroy_handle(h);
+	exit(1);
+}
+.RE
+.fi
+.PP
+would generate the following output:
+.PP
+.I myapp: Failed to send netlink message: Connection refused
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR libipq (3),
+.BR iptables (8).
diff --git a/libipq/ipq_set_verdict.3 b/libipq/ipq_set_verdict.3
new file mode 100644
index 0000000..7771ed6
--- /dev/null
+++ b/libipq/ipq_set_verdict.3
@@ -0,0 +1,100 @@
+.TH IPQ_SET_VERDICT 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ipq_set_verdict \(em issue verdict and optionally modified packet to kernel
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_verdict(const struct ipq_handle *" h ", ipq_id_t " id ", unsigned int " verdict ", size_t " data_len ", unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_set_verdict
+function issues a verdict on a packet previously obtained with
+.BR ipq_read ,
+specifing the intended disposition of the packet, and optionally
+supplying a modified version of the payload data.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I id
+parameter is the packet identifier obtained via
+.BR ipq_get_packet .
+.PP
+The
+.I verdict
+parameter must be one of:
+.TP
+.B NF_ACCEPT
+Accept the packet and continue traversal within the kernel.
+.br
+.TP
+.B NF_DROP
+Drop the packet.
+.TP
+\fBNF_QUEUE\fP
+Requeue the packet.
+.PP
+\fBNF_STOLEN\fP and \fBNF_REPEAT\fP are kernel-internal constants and should
+not be used from userspace as their exact side effects have not been
+investigated.
+.PP
+The
+.I data_len
+parameter is the length of the data pointed to
+by
+.IR buf ,
+the optional replacement payload data.
+.PP
+If simply setting a verdict without modifying the payload data, use zero
+for
+.I data_len
+and NULL for
+.IR buf .
+.PP
+The application is responsible for recalculating any packet checksums
+when modifying packets.
+.SH RETURN VALUE
+On failure, \-1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
+
diff --git a/libipq/libipq.3 b/libipq/libipq.3
new file mode 100644
index 0000000..611fcdf
--- /dev/null
+++ b/libipq/libipq.3
@@ -0,0 +1,277 @@
+.TH LIBIPQ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+libipq \(em iptables userspace packet queuing library.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.SH DESCRIPTION
+libipq is a development library for iptables userspace packet queuing.
+.SS Userspace Packet Queuing
+Netfilter provides a mechanism for passing packets out of the stack for
+queueing to userspace, then receiving these packets back into the kernel
+with a verdict specifying what to do with the packets (such as ACCEPT
+or DROP).  These packets may also be modified in userspace prior to
+reinjection back into the kernel.
+.PP
+For each supported protocol, a kernel module called a
+.I queue handler
+may register with Netfilter to perform the mechanics of passing
+packets to and from userspace.
+.PP
+The standard queue handler for IPv4 is ip_queue.  It is provided as an
+experimental module with 2.4 kernels, and uses a Netlink socket for
+kernel/userspace communication.
+.PP
+Once ip_queue is loaded, IP packets may be selected with iptables
+and queued for userspace processing via the QUEUE target.  For example,
+running the following commands:
+.PP
+	# modprobe iptable_filter
+.br	
+	# modprobe ip_queue
+.br	
+	# iptables \-A OUTPUT \-p icmp \-j QUEUE
+.PP
+will cause any locally generated ICMP packets (e.g. ping output) to
+be sent to the ip_queue module, which will then attempt to deliver the
+packets to a userspace application.  If no userspace application is waiting,
+the packets will be dropped
+.PP
+An application may receive and process these packets via libipq.
+.PP
+.PP
+.SS Libipq Overview
+Libipq provides an API for communicating with ip_queue.  The following is
+an overview of API usage, refer to individual man pages for more details
+on each function.
+.PP
+.B Initialisation
+.br
+To initialise the library, call
+.BR ipq_create_handle (3).
+This will attempt to bind to the Netlink socket used by ip_queue and
+return an opaque context handle for subsequent library calls.
+.PP
+.B Setting the Queue Mode
+.br
+.BR ipq_set_mode (3)
+allows the application to specify whether packet metadata, or packet
+payloads as well as metadata are copied to userspace.  It is also used to
+initially notify ip_queue that an application is ready to receive queue
+messages.
+.PP
+.B Receiving Packets from the Queue
+.br
+.BR ipq_read (3)
+waits for queue messages to arrive from ip_queue and copies
+them into a supplied buffer.
+Queue messages may be
+.I packet messages
+or
+.I error messages.
+.PP
+The type of packet may be determined with
+.BR ipq_message_type (3).
+.PP
+If it's a packet message, the metadata and optional payload may be retrieved with
+.BR ipq_get_packet (3).
+.PP
+To retrieve the value of an error message, use
+.BR ipq_get_msgerr (3).
+.PP
+.B Issuing Verdicts on Packets
+.br
+To issue a verdict on a packet, and optionally return a modified version
+of the packet to the kernel, call
+.BR ipq_set_verdict (3).
+.PP
+.B Error Handling
+.br
+An error string corresponding to the current value of the internal error
+variable
+.B ipq_errno
+may be obtained with
+.BR ipq_errstr (3).
+.PP
+For simple applications, calling
+.BR ipq_perror (3)
+will print the same message as
+.BR ipq_errstr (3),
+as well as the string corresponding to the global
+.B errno
+value (if set) to stderr.
+.PP
+.B Cleaning Up
+.br
+To free up the Netlink socket and destroy resources associated with
+the context handle, call
+.BR ipq_destroy_handle (3).
+.SH SUMMARY
+.TP 4
+.BR ipq_create_handle (3)
+Initialise library, return context handle.
+.TP
+.BR ipq_set_mode (3)
+Set the queue mode, to copy either packet metadata, or payloads
+as well as metadata to userspace.
+.TP
+.BR ipq_read (3)
+Wait for a queue message to arrive from ip_queue and read it into
+a buffer.
+.TP
+.BR ipq_message_type (3)
+Determine message type in the buffer.
+.TP
+.BR ipq_get_packet (3)
+Retrieve a packet message from the buffer.
+.TP
+.BR ipq_get_msgerr (3)
+Retrieve an error message from the buffer.
+.TP
+.BR ipq_set_verdict (3)
+Set a verdict on a packet, optionally replacing its contents.
+.TP
+.BR ipq_errstr (3)
+Return an error message corresponding to the internal ipq_errno variable.
+.TP
+.BR ipq_perror (3)
+Helper function to print error messages to stderr.
+.TP
+.BR ipq_destroy_handle (3)
+Destroy context handle and associated resources.
+.SH EXAMPLE
+The following is an example of a simple application which receives
+packets and issues NF_ACCEPT verdicts on each packet.
+.RS
+.nf
+/*
+ * This code is GPL.
+ */
+#include <linux/netfilter.h>
+#include <libipq.h>
+#include <stdio.h>
+
+#define BUFSIZE 2048 
+
+static void die(struct ipq_handle *h)
+{
+	ipq_perror("passer");
+	ipq_destroy_handle(h);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	int status;
+	unsigned char buf[BUFSIZE];
+	struct ipq_handle *h;
+	
+	h = ipq_create_handle(0, NFPROTO_IPV4);
+	if (!h)
+		die(h);
+		
+	status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
+	if (status < 0)
+		die(h);
+		
+	do{
+		status = ipq_read(h, buf, BUFSIZE, 0);
+		if (status < 0)
+			die(h);
+			
+		switch (ipq_message_type(buf)) {
+			case NLMSG_ERROR:
+				fprintf(stderr, "Received error message %d\\n",
+				        ipq_get_msgerr(buf));
+				break;
+				
+			case IPQM_PACKET: {
+				ipq_packet_msg_t *m = ipq_get_packet(buf);
+				
+				status = ipq_set_verdict(h, m->packet_id,
+				                         NF_ACCEPT, 0, NULL);
+				if (status < 0)
+					die(h);
+				break;
+			}
+			
+			default:
+				fprintf(stderr, "Unknown message type!\\n");
+				break;
+		}
+	} while (1);
+	
+	ipq_destroy_handle(h);
+	return 0;
+}
+.RE
+.fi
+.PP
+Pointers to more libipq application examples may be found in The
+Netfilter FAQ.
+.SH DIAGNOSTICS
+For information about monitoring and tuning ip_queue, refer to the
+Linux 2.4 Packet Filtering HOWTO.
+.PP
+If an application modifies a packet, it needs to also update any
+checksums for the packet.  Typically, the kernel will silently discard
+modified packets with invalid checksums. 
+.SH SECURITY
+Processes require CAP_NET_ADMIN capabilty to access the kernel ip_queue
+module.  Such processes can potentially access and modify any IP packets
+received, generated or forwarded by the kernel.
+.SH TODO
+Per-handle
+.B ipq_errno
+values.
+.SH BUGS
+Probably.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the
+.B ipq_read
+timeout feature, which appeared in the 1.2.4 release of iptables.
+.PP
+Fernando Anton added support for IPv6.
+.SH SEE ALSO
+.BR iptables (8),
+.BR ipq_create_handle (3),
+.BR ipq_destroy_handle (3),
+.BR ipq_errstr (3),
+.BR ipq_get_msgerr (3),
+.BR ipq_get_packet (3),
+.BR ipq_message_type (3),
+.BR ipq_perror (3),
+.BR ipq_read (3),
+.BR ipq_set_mode (3),
+.BR ipq_set_verdict (3).
+.PP
+The Netfilter home page at http://netfilter.samba.org/
+which has links to The Networking Concepts HOWTO, The Linux 2.4 Packet
+Filtering HOWTO, The Linux 2.4 NAT HOWTO, The Netfilter Hacking HOWTO,
+The Netfilter FAQ and many other useful resources.
+
diff --git a/libipq/libipq.c b/libipq/libipq.c
new file mode 100644
index 0000000..fb65971
--- /dev/null
+++ b/libipq/libipq.c
@@ -0,0 +1,379 @@
+/*
+ * libipq.c
+ *
+ * IPQ userspace library.
+ *
+ * Please note that this library is still developmental, and there may
+ * be some API changes.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * 07-11-2001 Modified by Fernando Anton to add support for IPv6.
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <libipq/libipq.h>
+#include <netinet/in.h>
+#include <linux/netfilter.h>
+
+/****************************************************************************
+ *
+ * Private interface
+ *
+ ****************************************************************************/
+
+enum {
+	IPQ_ERR_NONE = 0,
+	IPQ_ERR_IMPL,
+	IPQ_ERR_HANDLE,
+	IPQ_ERR_SOCKET,
+	IPQ_ERR_BIND,
+	IPQ_ERR_BUFFER,
+	IPQ_ERR_RECV,
+	IPQ_ERR_NLEOF,
+	IPQ_ERR_ADDRLEN,
+	IPQ_ERR_STRUNC,
+	IPQ_ERR_RTRUNC,
+	IPQ_ERR_NLRECV,
+	IPQ_ERR_SEND,
+	IPQ_ERR_SUPP,
+	IPQ_ERR_RECVBUF,
+	IPQ_ERR_TIMEOUT,
+        IPQ_ERR_PROTOCOL
+};
+#define IPQ_MAXERR IPQ_ERR_PROTOCOL
+
+struct ipq_errmap_t {
+	int errcode;
+	char *message;
+} ipq_errmap[] = {
+	{ IPQ_ERR_NONE, "Unknown error" },
+	{ IPQ_ERR_IMPL, "Implementation error" },
+	{ IPQ_ERR_HANDLE, "Unable to create netlink handle" },
+	{ IPQ_ERR_SOCKET, "Unable to create netlink socket" },
+	{ IPQ_ERR_BIND, "Unable to bind netlink socket" },
+	{ IPQ_ERR_BUFFER, "Unable to allocate buffer" },
+	{ IPQ_ERR_RECV, "Failed to receive netlink message" },
+	{ IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
+	{ IPQ_ERR_ADDRLEN, "Invalid peer address length" },
+	{ IPQ_ERR_STRUNC, "Sent message truncated" },
+	{ IPQ_ERR_RTRUNC, "Received message truncated" },
+	{ IPQ_ERR_NLRECV, "Received error from netlink" },
+	{ IPQ_ERR_SEND, "Failed to send netlink message" },
+	{ IPQ_ERR_SUPP, "Operation not supported" },
+	{ IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
+	{ IPQ_ERR_TIMEOUT, "Timeout"},
+	{ IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
+};
+
+static int ipq_errno = IPQ_ERR_NONE;
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len);
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout);
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags);
+
+static char *ipq_strerror(int errcode);
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len)
+{
+	int status = sendto(h->fd, msg, len, 0,
+	                    (struct sockaddr *)&h->peer, sizeof(h->peer));
+	if (status < 0)
+		ipq_errno = IPQ_ERR_SEND;
+	return status;
+}
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags)
+{
+	int status = sendmsg(h->fd, msg, flags);
+	if (status < 0)
+		ipq_errno = IPQ_ERR_SEND;
+	return status;
+}
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout)
+{
+	unsigned int addrlen;
+	int status;
+	struct nlmsghdr *nlh;
+
+	if (len < sizeof(struct nlmsgerr)) {
+		ipq_errno = IPQ_ERR_RECVBUF;
+		return -1;
+	}
+	addrlen = sizeof(h->peer);
+
+	if (timeout != 0) {
+		int ret;
+		struct timeval tv;
+		fd_set read_fds;
+		
+		if (timeout < 0) {
+			/* non-block non-timeout */
+			tv.tv_sec = 0;
+			tv.tv_usec = 0;
+		} else {
+			tv.tv_sec = timeout / 1000000;
+			tv.tv_usec = timeout % 1000000;
+		}
+
+		FD_ZERO(&read_fds);
+		FD_SET(h->fd, &read_fds);
+		ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
+		if (ret < 0) {
+			if (errno == EINTR) {
+				return 0;
+			} else {
+				ipq_errno = IPQ_ERR_RECV;
+				return -1;
+			}
+		}
+		if (!FD_ISSET(h->fd, &read_fds)) {
+			ipq_errno = IPQ_ERR_TIMEOUT;
+			return 0;
+		}
+	}
+	status = recvfrom(h->fd, buf, len, 0,
+	                      (struct sockaddr *)&h->peer, &addrlen);
+	if (status < 0) {
+		ipq_errno = IPQ_ERR_RECV;
+		return status;
+	}
+	if (addrlen != sizeof(h->peer)) {
+		ipq_errno = IPQ_ERR_RECV;
+		return -1;
+	}
+	if (h->peer.nl_pid != 0) {
+		ipq_errno = IPQ_ERR_RECV;
+		return -1;
+	}
+	if (status == 0) {
+		ipq_errno = IPQ_ERR_NLEOF;
+		return -1;
+	}
+	nlh = (struct nlmsghdr *)buf;
+	if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
+		ipq_errno = IPQ_ERR_RTRUNC;
+		return -1;
+	}
+	return status;
+}
+
+static char *ipq_strerror(int errcode)
+{
+	if (errcode < 0 || errcode > IPQ_MAXERR)
+		errcode = IPQ_ERR_IMPL;
+	return ipq_errmap[errcode].message;
+}
+
+/****************************************************************************
+ *
+ * Public interface
+ *
+ ****************************************************************************/
+
+/*
+ * Create and initialise an ipq handle.
+ */
+struct ipq_handle *ipq_create_handle(uint32_t flags, uint32_t protocol)
+{
+	int status;
+	struct ipq_handle *h;
+
+	h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
+	if (h == NULL) {
+		ipq_errno = IPQ_ERR_HANDLE;
+		return NULL;
+	}
+	
+	memset(h, 0, sizeof(struct ipq_handle));
+	
+        if (protocol == NFPROTO_IPV4)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
+        else if (protocol == NFPROTO_IPV6)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
+        else {
+		ipq_errno = IPQ_ERR_PROTOCOL;
+		free(h);
+		return NULL;
+        }
+        
+	if (h->fd == -1) {
+		ipq_errno = IPQ_ERR_SOCKET;
+		free(h);
+		return NULL;
+	}
+	memset(&h->local, 0, sizeof(struct sockaddr_nl));
+	h->local.nl_family = AF_NETLINK;
+	h->local.nl_pid = getpid();
+	h->local.nl_groups = 0;
+	status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
+	if (status == -1) {
+		ipq_errno = IPQ_ERR_BIND;
+		close(h->fd);
+		free(h);
+		return NULL;
+	}
+	memset(&h->peer, 0, sizeof(struct sockaddr_nl));
+	h->peer.nl_family = AF_NETLINK;
+	h->peer.nl_pid = 0;
+	h->peer.nl_groups = 0;
+	return h;
+}
+
+/*
+ * No error condition is checked here at this stage, but it may happen
+ * if/when reliable messaging is implemented.
+ */
+int ipq_destroy_handle(struct ipq_handle *h)
+{
+	if (h) {
+		close(h->fd);
+		free(h);
+	}
+	return 0;
+}
+
+int ipq_set_mode(const struct ipq_handle *h,
+                 uint8_t mode, size_t range)
+{
+	struct {
+		struct nlmsghdr nlh;
+		ipq_peer_msg_t pm;
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
+	req.nlh.nlmsg_flags = NLM_F_REQUEST;
+	req.nlh.nlmsg_type = IPQM_MODE;
+	req.nlh.nlmsg_pid = h->local.nl_pid;
+	req.pm.msg.mode.value = mode;
+	req.pm.msg.mode.range = range;
+	return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
+}
+
+/*
+ * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
+ *
+ */
+ssize_t ipq_read(const struct ipq_handle *h,
+                 unsigned char *buf, size_t len, int timeout)
+{
+	return ipq_netlink_recvfrom(h, buf, len, timeout);
+}
+
+int ipq_message_type(const unsigned char *buf)
+{
+	return ((struct nlmsghdr*)buf)->nlmsg_type;
+}
+
+int ipq_get_msgerr(const unsigned char *buf)
+{
+	struct nlmsghdr *h = (struct nlmsghdr *)buf;
+	struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+	return -err->error;
+}
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
+{
+	return NLMSG_DATA((struct nlmsghdr *)(buf));
+}
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf)
+{
+	unsigned char nvecs;
+	size_t tlen;
+	struct nlmsghdr nlh;
+	ipq_peer_msg_t pm;
+	struct iovec iov[3];
+	struct msghdr msg;
+
+	memset(&nlh, 0, sizeof(nlh));
+	nlh.nlmsg_flags = NLM_F_REQUEST;
+	nlh.nlmsg_type = IPQM_VERDICT;
+	nlh.nlmsg_pid = h->local.nl_pid;
+	memset(&pm, 0, sizeof(pm));
+	pm.msg.verdict.value = verdict;
+	pm.msg.verdict.id = id;
+	pm.msg.verdict.data_len = data_len;
+	iov[0].iov_base = &nlh;
+	iov[0].iov_len = sizeof(nlh);
+	iov[1].iov_base = &pm;
+	iov[1].iov_len = sizeof(pm);
+	tlen = sizeof(nlh) + sizeof(pm);
+	nvecs = 2;
+	if (data_len && buf) {
+		iov[2].iov_base = buf;
+		iov[2].iov_len = data_len;
+		tlen += data_len;
+		nvecs++;
+	}
+	msg.msg_name = (void *)&h->peer;
+	msg.msg_namelen = sizeof(h->peer);
+	msg.msg_iov = iov;
+	msg.msg_iovlen = nvecs;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
+	nlh.nlmsg_len = tlen;
+	return ipq_netlink_sendmsg(h, &msg, 0);
+}
+
+/* Not implemented yet */
+int ipq_ctl(const struct ipq_handle *h, int request, ...)
+{
+	return 1;
+}
+
+char *ipq_errstr(void)
+{
+	return ipq_strerror(ipq_errno);
+}
+
+void ipq_perror(const char *s)
+{
+	if (s)
+		fputs(s, stderr);
+	else
+		fputs("ERROR", stderr);
+	if (ipq_errno)
+		fprintf(stderr, ": %s", ipq_errstr());
+	if (errno)
+		fprintf(stderr, ": %s", strerror(errno));
+	fputc('\n', stderr);
+}
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
new file mode 100644
index 0000000..49ca83d
--- /dev/null
+++ b/libiptc/.gitignore
@@ -0,0 +1 @@
+/*.pc
diff --git a/libiptc/Makefile.am b/libiptc/Makefile.am
new file mode 100644
index 0000000..f789d34
--- /dev/null
+++ b/libiptc/Makefile.am
@@ -0,0 +1,15 @@
+# -*- Makefile -*-
+
+AM_CFLAGS        = ${regular_CFLAGS}
+AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CPPFLAGS}
+
+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 1:0:1
+libip6tc_la_SOURCES = libip6tc.c
+libip6tc_la_LDFLAGS = -version-info 1:0:1 ${libiptc_LDFLAGS2}
diff --git a/libiptc/libip4tc.c b/libiptc/libip4tc.c
new file mode 100644
index 0000000..dd59951
--- /dev/null
+++ b/libiptc/libip4tc.c
@@ -0,0 +1,485 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION	4
+#define IP_OFFSET	0x1FFF
+
+#define HOOK_PRE_ROUTING	NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN		NF_IP_LOCAL_IN
+#define HOOK_FORWARD		NF_IP_FORWARD
+#define HOOK_LOCAL_OUT		NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING	NF_IP_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET	struct xt_entry_target
+#define STRUCT_ENTRY		struct ipt_entry
+#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 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 ENTRY_ITERATE		IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN	XT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN	XT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET		ipt_get_target
+
+#define ERROR_TARGET		XT_ERROR_TARGET
+#define NUMHOOKS		NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL		xt_chainlabel
+
+#define TC_DUMP_ENTRIES		dump_entries
+#define TC_IS_CHAIN		iptc_is_chain
+#define TC_FIRST_CHAIN		iptc_first_chain
+#define TC_NEXT_CHAIN		iptc_next_chain
+#define TC_FIRST_RULE		iptc_first_rule
+#define TC_NEXT_RULE		iptc_next_rule
+#define TC_GET_TARGET		iptc_get_target
+#define TC_BUILTIN		iptc_builtin
+#define TC_GET_POLICY		iptc_get_policy
+#define TC_INSERT_ENTRY		iptc_insert_entry
+#define TC_REPLACE_ENTRY	iptc_replace_entry
+#define TC_APPEND_ENTRY		iptc_append_entry
+#define TC_CHECK_ENTRY		iptc_check_entry
+#define TC_DELETE_ENTRY		iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY	iptc_delete_num_entry
+#define TC_FLUSH_ENTRIES	iptc_flush_entries
+#define TC_ZERO_ENTRIES		iptc_zero_entries
+#define TC_READ_COUNTER		iptc_read_counter
+#define TC_ZERO_COUNTER		iptc_zero_counter
+#define TC_SET_COUNTER		iptc_set_counter
+#define TC_CREATE_CHAIN		iptc_create_chain
+#define TC_GET_REFERENCES	iptc_get_references
+#define TC_DELETE_CHAIN		iptc_delete_chain
+#define TC_RENAME_CHAIN		iptc_rename_chain
+#define TC_SET_POLICY		iptc_set_policy
+#define TC_GET_RAW_SOCKET	iptc_get_raw_socket
+#define TC_INIT			iptc_init
+#define TC_FREE			iptc_free
+#define TC_COMMIT		iptc_commit
+#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
+
+#define SO_SET_REPLACE		IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS	IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO		IPT_SO_GET_INFO
+#define SO_GET_ENTRIES		IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION		IPT_SO_GET_VERSION
+
+#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			XT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n)			\
+(unsigned int)((n)>>24)&0xFF,			\
+(unsigned int)((n)>>16)&0xFF,			\
+(unsigned int)((n)>>8)&0xFF,			\
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+static int
+dump_entry(struct ipt_entry *e, struct xtc_handle *const handle)
+{
+	size_t i;
+	STRUCT_ENTRY_TARGET *t;
+
+	printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+	       iptcb_entry2offset(handle, e));
+	printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+	       IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+	printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+	       IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+	printf("Interface: `%s'/", e->ip.iniface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+	printf("to `%s'/", e->ip.outiface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+	printf("\nProtocol: %u\n", e->ip.proto);
+	printf("Flags: %02X\n", e->ip.flags);
+	printf("Invflags: %02X\n", e->ip.invflags);
+	printf("Counters: %llu packets, %llu bytes\n",
+	       (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+	printf("Cache: %08X\n", e->nfcache);
+
+	IPT_MATCH_ITERATE(e, print_match);
+
+	t = GET_TARGET(e);
+	printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+	if (strcmp(t->u.user.name, 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 == -NF_QUEUE-1 ? "NF_QUEUE"
+			       : pos == RETURN ? "RETURN"
+			       : "UNKNOWN");
+		else
+			printf("verdict=%u\n", pos);
+	} else if (strcmp(t->u.user.name, XT_ERROR_TARGET) == 0)
+		printf("error=`%s'\n", t->data);
+
+	printf("\n");
+	return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+	unsigned int i;
+	unsigned char *mptr;
+
+	/* Always compare head structures: ignore mask here. */
+	if (a->ip.src.s_addr != b->ip.src.s_addr
+	    || a->ip.dst.s_addr != b->ip.dst.s_addr
+	    || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+	    || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+	    || a->ip.proto != b->ip.proto
+	    || a->ip.flags != b->ip.flags
+	    || a->ip.invflags != b->ip.invflags)
+		return NULL;
+
+	for (i = 0; i < IFNAMSIZ; i++) {
+		if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+			return NULL;
+		if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+		    != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+			return NULL;
+		if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+			return NULL;
+		if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+		    != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+			return NULL;
+	}
+
+	if (a->target_offset != b->target_offset
+	    || a->next_offset != b->next_offset)
+		return NULL;
+
+	mptr = matchmask + sizeof(STRUCT_ENTRY);
+	if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+		return NULL;
+	mptr += XT_ALIGN(sizeof(struct xt_entry_target));
+
+	return mptr;
+}
+
+#if 0
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ip)/sizeof(uint32_t); i++)
+		if (((uint32_t *)ip)[i])
+			return 0;
+
+	return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+	assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+	assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+	(*off) += m->u.match_size;
+	return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+	    unsigned int user_offset, int *was_return,
+	    struct xtc_handle *h)
+{
+	unsigned int toff;
+	STRUCT_STANDARD_TARGET *t;
+
+	assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+	assert(e->next_offset >= e->target_offset
+	       + sizeof(STRUCT_ENTRY_TARGET));
+	toff = sizeof(STRUCT_ENTRY);
+	IPT_MATCH_ITERATE(e, check_match, &toff);
+
+	assert(toff == e->target_offset);
+
+	t = (STRUCT_STANDARD_TARGET *)
+		GET_TARGET((STRUCT_ENTRY *)e);
+	/* next_offset will have to be multiple of entry alignment. */
+	assert(e->next_offset == ALIGN(e->next_offset));
+	assert(e->target_offset == ALIGN(e->target_offset));
+	assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+	assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+	if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+		assert(t->target.u.target_size
+		       == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+		assert(t->verdict == -NF_DROP-1
+		       || t->verdict == -NF_ACCEPT-1
+		       || t->verdict == RETURN
+		       || t->verdict < (int)h->entries->size);
+
+		if (t->verdict >= 0) {
+			STRUCT_ENTRY *te = get_entry(h, t->verdict);
+			int idx;
+
+			idx = iptcb_entry2index(h, te);
+			assert(strcmp(GET_TARGET(te)->u.user.name,
+				      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, XT_ERROR_TARGET)
+			       == 0);
+		}
+
+		if (t->verdict == RETURN
+		    && unconditional(&e->ip)
+		    && e->target_offset == sizeof(*e))
+			*was_return = 1;
+		else
+			*was_return = 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)));
+
+		/* If this is in user area, previous must have been return */
+		if (*off > user_offset)
+			assert(*was_return);
+
+		*was_return = 0;
+	}
+	else *was_return = 0;
+
+	if (*off == user_offset)
+		assert(strcmp(t->target.u.user.name, XT_ERROR_TARGET) == 0);
+
+	(*off) += e->next_offset;
+	(*i)++;
+	return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(struct xtc_handle *h, unsigned int line)
+{
+	unsigned int i, n;
+	unsigned int user_offset; /* Offset of first user chain */
+	int was_return;
+
+	assert(h->changed == 0 || h->changed == 1);
+	if (strcmp(h->info.name, "filter") == 0) {
+		assert(h->info.valid_hooks
+		       == (1 << NF_IP_LOCAL_IN
+			   | 1 << NF_IP_FORWARD
+			   | 1 << NF_IP_LOCAL_OUT));
+
+		/* Hooks should be first three */
+		assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+		n = get_chain_end(h, 0);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+		n = get_chain_end(h, n);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+	} else if (strcmp(h->info.name, "nat") == 0) {
+		assert((h->info.valid_hooks
+		        == (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_POST_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)) ||
+		       (h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_IN
+			    | 1 << NF_IP_POST_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)));
+
+		assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+		n = get_chain_end(h, n);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+			user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+		}
+
+	} else if (strcmp(h->info.name, "mangle") == 0) {
+		/* This code is getting ugly because linux < 2.4.18-pre6 had
+		 * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+		 * */
+		assert((h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_OUT)) || 
+		       (h->info.valid_hooks
+			== (1 << NF_IP_PRE_ROUTING
+			    | 1 << NF_IP_LOCAL_IN
+			    | 1 << NF_IP_FORWARD
+			    | 1 << NF_IP_LOCAL_OUT
+			    | 1 << NF_IP_POST_ROUTING)));
+
+		/* Hooks should be first five */
+		assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+			n = get_chain_end(h, n);
+		}
+
+		if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+			n = get_chain_end(h, n);
+		}
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+			user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+		}
+	} else if (strcmp(h->info.name, "raw") == 0) {
+		assert(h->info.valid_hooks
+		       == (1 << NF_IP_PRE_ROUTING
+			   | 1 << NF_IP_LOCAL_OUT));
+
+		/* Hooks should be first three */
+		assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, n);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+		user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+	} else {
+		fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+		abort();
+	}
+
+	/* User chain == end of last builtin + policy entry */
+	user_offset = get_chain_end(h, user_offset);
+	user_offset += get_entry(h, user_offset)->next_offset;
+
+	/* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+	for (i = 0; i < NUMHOOKS; i++) {
+		STRUCT_ENTRY *e;
+		STRUCT_STANDARD_TARGET *t;
+
+		if (!(h->info.valid_hooks & (1 << i)))
+			continue;
+		assert(h->info.underflow[i]
+		       == get_chain_end(h, h->info.hook_entry[i]));
+
+		e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+		assert(unconditional(&e->ip));
+		assert(e->target_offset == sizeof(*e));
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+		assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+		assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+		assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+		assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+		/* Hooks and underflows must be valid entries */
+		entry2index(h, get_entry(h, h->info.hook_entry[i]));
+		entry2index(h, get_entry(h, h->info.underflow[i]));
+	}
+
+	assert(h->info.size
+	       >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+					 +sizeof(STRUCT_STANDARD_TARGET)));
+
+	assert(h->entries.size
+	       >= (h->new_number
+		   * (sizeof(STRUCT_ENTRY)
+		      + sizeof(STRUCT_STANDARD_TARGET))));
+	assert(strcmp(h->info.name, h->entries.name) == 0);
+
+	i = 0; n = 0;
+	was_return = 0;
+	/* Check all the entries. */
+	ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+		      check_entry, &i, &n, user_offset, &was_return, h);
+
+	assert(i == h->new_number);
+	assert(n == h->entries.size);
+
+	/* Final entry must be error node */
+	assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+		      ->u.user.name,
+		      ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
+
+#endif
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
new file mode 100644
index 0000000..ca01bcb
--- /dev/null
+++ b/libiptc/libip6tc.c
@@ -0,0 +1,436 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING	NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN		NF_IP6_LOCAL_IN
+#define HOOK_FORWARD		NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT		NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING	NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET	struct xt_entry_target
+#define STRUCT_ENTRY		struct ip6t_entry
+#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 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 ENTRY_ITERATE		IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN	XT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN	XT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET		ip6t_get_target
+
+#define ERROR_TARGET		XT_ERROR_TARGET
+#define NUMHOOKS		NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL		xt_chainlabel
+
+#define TC_DUMP_ENTRIES		dump_entries6
+#define TC_IS_CHAIN		ip6tc_is_chain
+#define TC_FIRST_CHAIN		ip6tc_first_chain
+#define TC_NEXT_CHAIN		ip6tc_next_chain
+#define TC_FIRST_RULE		ip6tc_first_rule
+#define TC_NEXT_RULE		ip6tc_next_rule
+#define TC_GET_TARGET		ip6tc_get_target
+#define TC_BUILTIN		ip6tc_builtin
+#define TC_GET_POLICY		ip6tc_get_policy
+#define TC_INSERT_ENTRY		ip6tc_insert_entry
+#define TC_REPLACE_ENTRY	ip6tc_replace_entry
+#define TC_APPEND_ENTRY		ip6tc_append_entry
+#define TC_CHECK_ENTRY		ip6tc_check_entry
+#define TC_DELETE_ENTRY		ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY	ip6tc_delete_num_entry
+#define TC_FLUSH_ENTRIES	ip6tc_flush_entries
+#define TC_ZERO_ENTRIES		ip6tc_zero_entries
+#define TC_ZERO_COUNTER		ip6tc_zero_counter
+#define TC_READ_COUNTER		ip6tc_read_counter
+#define TC_SET_COUNTER		ip6tc_set_counter
+#define TC_CREATE_CHAIN		ip6tc_create_chain
+#define TC_GET_REFERENCES	ip6tc_get_references
+#define TC_DELETE_CHAIN		ip6tc_delete_chain
+#define TC_RENAME_CHAIN		ip6tc_rename_chain
+#define TC_SET_POLICY		ip6tc_set_policy
+#define TC_GET_RAW_SOCKET	ip6tc_get_raw_socket
+#define TC_INIT			ip6tc_init
+#define TC_FREE			ip6tc_free
+#define TC_COMMIT		ip6tc_commit
+#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
+
+#define SO_SET_REPLACE		IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS	IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO		IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES		IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION		IP6T_SO_GET_VERSION
+
+#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			XT_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->s6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+static int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+	int l, i;
+	for (l = 0; l < 128; l++) {
+		if (BIT6(a, l) == 0)
+			break;
+	}
+	for (i = l + 1; i < 128; i++) {
+		if (BIT6(a, i) == 1)
+			return -1;
+	}
+	return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, struct xtc_handle *const handle)
+{
+	size_t i;
+	char buf[40];
+	int len;
+	struct xt_entry_target *t;
+	
+	printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e),
+	       iptcb_entry2offset(handle, e));
+	puts("SRC IP: ");
+	inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+	puts(buf);
+	putchar('/');
+	len = ipv6_prefix_length(&e->ipv6.smsk);
+	if (len != -1)
+		printf("%d", len);
+	else {
+		inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+		puts(buf);
+	}
+	putchar('\n');
+	
+	puts("DST IP: ");
+	inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+	puts(buf);
+	putchar('/');
+	len = ipv6_prefix_length(&e->ipv6.dmsk);
+	if (len != -1)
+		printf("%d", len);
+	else {
+		inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+		puts(buf);
+	}
+	putchar('\n');
+	
+	printf("Interface: `%s'/", e->ipv6.iniface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+	printf("to `%s'/", e->ipv6.outiface);
+	for (i = 0; i < IFNAMSIZ; i++)
+		printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+	printf("\nProtocol: %u\n", e->ipv6.proto);
+	if (e->ipv6.flags & IP6T_F_TOS)
+		printf("TOS: %u\n", e->ipv6.tos);
+	printf("Flags: %02X\n", e->ipv6.flags);
+	printf("Invflags: %02X\n", e->ipv6.invflags);
+	printf("Counters: %llu packets, %llu bytes\n",
+	       (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+	printf("Cache: %08X\n", e->nfcache);
+	
+	IP6T_MATCH_ITERATE(e, print_match);
+
+	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, 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 == XT_RETURN ? "RETURN"
+			       : "UNKNOWN");
+		else
+			printf("verdict=%u\n", pos);
+	} else if (strcmp(t->u.user.name, XT_ERROR_TARGET) == 0)
+		printf("error=`%s'\n", t->data);
+
+	printf("\n");
+	return 0;
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+	unsigned char *matchmask)
+{
+	unsigned int i;
+	unsigned char *mptr;
+
+	/* Always compare head structures: ignore mask here. */
+	if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+	    || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+	    || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+	    || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+	    || a->ipv6.proto != b->ipv6.proto
+	    || a->ipv6.tos != b->ipv6.tos
+	    || a->ipv6.flags != b->ipv6.flags
+	    || a->ipv6.invflags != b->ipv6.invflags)
+		return NULL;
+
+	for (i = 0; i < IFNAMSIZ; i++) {
+		if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+			return NULL;
+		if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+		    != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+			return NULL;
+		if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+			return NULL;
+		if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+		    != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+			return NULL;
+	}
+
+	if (a->target_offset != b->target_offset
+	    || a->next_offset != b->next_offset)
+		return NULL;
+
+	mptr = matchmask + sizeof(STRUCT_ENTRY);
+	if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+		return NULL;
+	mptr += XT_ALIGN(sizeof(struct xt_entry_target));
+
+	return mptr;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(*ipv6); i++)
+		if (((char *)ipv6)[i])
+			break;
+
+	return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(struct xtc_handle *h, unsigned int line)
+{
+	unsigned int i, n;
+	unsigned int user_offset; /* Offset of first user chain */
+	int was_return;
+
+	assert(h->changed == 0 || h->changed == 1);
+	if (strcmp(h->info.name, "filter") == 0) {
+		assert(h->info.valid_hooks
+		       == (1 << NF_IP6_LOCAL_IN
+			   | 1 << NF_IP6_FORWARD
+			   | 1 << NF_IP6_LOCAL_OUT));
+
+		/* Hooks should be first three */
+		assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+		n = get_chain_end(h, 0);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+		n = get_chain_end(h, n);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+		user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+	} else if (strcmp(h->info.name, "nat") == 0) {
+		assert((h->info.valid_hooks
+			== (1 << NF_IP6_PRE_ROUTING
+			    | 1 << NF_IP6_LOCAL_OUT
+			    | 1 << NF_IP6_POST_ROUTING)) ||
+		       (h->info.valid_hooks
+			== (1 << NF_IP6_PRE_ROUTING
+			    | 1 << NF_IP6_LOCAL_IN
+			    | 1 << NF_IP6_LOCAL_OUT
+			    | 1 << NF_IP6_POST_ROUTING)));
+
+		assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+		n = get_chain_end(h, n);
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+			user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+		}
+
+	} else if (strcmp(h->info.name, "mangle") == 0) {
+		/* This code is getting ugly because linux < 2.4.18-pre6 had
+		 * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+		 * */
+		assert((h->info.valid_hooks
+			== (1 << NF_IP6_PRE_ROUTING
+			    | 1 << NF_IP6_LOCAL_OUT)) ||
+		       (h->info.valid_hooks
+			== (1 << NF_IP6_PRE_ROUTING
+			    | 1 << NF_IP6_LOCAL_IN
+			    | 1 << NF_IP6_FORWARD
+			    | 1 << NF_IP6_LOCAL_OUT
+			    | 1 << NF_IP6_POST_ROUTING)));
+
+		/* Hooks should be first five */
+		assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, 0);
+
+		if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+			n = get_chain_end(h, n);
+		}
+
+		if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+			n = get_chain_end(h, n);
+		}
+
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+		user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+		if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+			n = get_chain_end(h, n);
+			n += get_entry(h, n)->next_offset;
+			assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+			user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+		}
+	} else if (strcmp(h->info.name, "raw") == 0) {
+		assert(h->info.valid_hooks
+		       == (1 << NF_IP6_PRE_ROUTING
+			   | 1 << NF_IP6_LOCAL_OUT));
+
+		/* Hooks should be first three */
+		assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+		n = get_chain_end(h, n);
+		n += get_entry(h, n)->next_offset;
+		assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+		user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+	} else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+		abort();
+	}
+
+	/* User chain == end of last builtin + policy entry */
+	user_offset = get_chain_end(h, user_offset);
+	user_offset += get_entry(h, user_offset)->next_offset;
+
+	/* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+	for (i = 0; i < NUMHOOKS; i++) {
+		STRUCT_ENTRY *e;
+		STRUCT_STANDARD_TARGET *t;
+
+		if (!(h->info.valid_hooks & (1 << i)))
+			continue;
+		assert(h->info.underflow[i]
+		       == get_chain_end(h, h->info.hook_entry[i]));
+
+		e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+		assert(unconditional(&e->ipv6));
+		assert(e->target_offset == sizeof(*e));
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+		printf("target_size=%u, align=%u\n",
+			t->target.u.target_size, ALIGN(sizeof(*t)));
+		assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+		assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+		assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+		assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+		/* Hooks and underflows must be valid entries */
+		iptcb_entry2index(h, get_entry(h, h->info.hook_entry[i]));
+		iptcb_entry2index(h, get_entry(h, h->info.underflow[i]));
+	}
+
+	assert(h->info.size
+	       >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+					 +sizeof(STRUCT_STANDARD_TARGET)));
+
+	assert(h->entries.size
+	       >= (h->new_number
+		   * (sizeof(STRUCT_ENTRY)
+		      + sizeof(STRUCT_STANDARD_TARGET))));
+	assert(strcmp(h->info.name, h->entries.name) == 0);
+
+	i = 0; n = 0;
+	was_return = 0;
+
+#if 0
+	/* Check all the entries. */
+	ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+		      check_entry, &i, &n, user_offset, &was_return, h);
+
+	assert(i == h->new_number);
+	assert(n == h->entries.size);
+
+	/* Final entry must be error node */
+	assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+		      ->u.user.name,
+		      ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
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
new file mode 100644
index 0000000..f0f7815
--- /dev/null
+++ b/libiptc/libiptc.c
@@ -0,0 +1,2756 @@
+/* Library which manipulates firewall rules.  Version $Revision$ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details).
+ * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *	- Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ * 	- performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ * 	  don't rebuild the chain cache after every operation, instead fix it
+ * 	  up after a ruleset change.
+ * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
+ * 	- further performance work: total reimplementation of libiptc.
+ * 	- libiptc now has a real internal (linked-list) represntation of the
+ * 	  ruleset and a parser/compiler from/to this internal representation
+ * 	- again sponsored by Astaro AG (http://www.astaro.com/)
+ *
+ * 2008-Jan+Jul: Jesper Dangaard Brouer <hawk@comx.dk>
+ * 	- performance work: speedup chain list "name" searching.
+ * 	- 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"
+
+//#define IPTC_DEBUG2 1
+
+#ifdef IPTC_DEBUG2
+#include <fcntl.h>
+#define DEBUGP(x, args...)	fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
+#define DEBUGP_C(x, args...)	fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...)
+#define DEBUGP_C(x, args...)
+#endif
+
+#ifdef DEBUG
+#define debug(x, args...)	fprintf(stderr, x, ## args)
+#else
+#define debug(x, args...)
+#endif
+
+static void *iptc_fn = NULL;
+
+static const char *hooknames[] = {
+	[HOOK_PRE_ROUTING]	= "PREROUTING",
+	[HOOK_LOCAL_IN]		= "INPUT",
+	[HOOK_FORWARD]		= "FORWARD",
+	[HOOK_LOCAL_OUT]	= "OUTPUT",
+	[HOOK_POST_ROUTING]	= "POSTROUTING",
+};
+
+/* Convenience structures */
+struct chain_head;
+struct rule_head;
+
+struct counter_map
+{
+	enum {
+		COUNTER_MAP_NOMAP,
+		COUNTER_MAP_NORMAL_MAP,
+		COUNTER_MAP_ZEROED,
+		COUNTER_MAP_SET
+	} maptype;
+	unsigned int mappos;
+};
+
+enum iptcc_rule_type {
+	IPTCC_R_STANDARD,		/* standard target (ACCEPT, ...) */
+	IPTCC_R_MODULE,			/* extension module (SNAT, ...) */
+	IPTCC_R_FALLTHROUGH,		/* fallthrough rule */
+	IPTCC_R_JUMP,			/* jump to other chain */
+};
+
+struct rule_head
+{
+	struct list_head list;
+	struct chain_head *chain;
+	struct counter_map counter_map;
+
+	unsigned int index;		/* index (needed for counter_map) */
+	unsigned int offset;		/* offset in rule blob */
+
+	enum iptcc_rule_type type;
+	struct chain_head *jump;	/* jump target, if IPTCC_R_JUMP */
+
+	unsigned int size;		/* size of entry data */
+	STRUCT_ENTRY entry[0];
+};
+
+struct chain_head
+{
+	struct list_head list;
+	char name[TABLE_MAXNAMELEN];
+	unsigned int hooknum;		/* hook number+1 if builtin */
+	unsigned int references;	/* how many jumps reference us */
+	int verdict;			/* verdict if builtin */
+
+	STRUCT_COUNTERS counters;	/* per-chain counters */
+	struct counter_map counter_map;
+
+	unsigned int num_rules;		/* number of rules in list */
+	struct list_head rules;		/* list of rules */
+
+	unsigned int index;		/* index (needed for jump resolval) */
+	unsigned int head_offset;	/* offset in rule blob */
+	unsigned int foot_index;	/* index (needed for counter_map) */
+	unsigned int foot_offset;	/* offset in rule blob */
+};
+
+struct xtc_handle {
+	int sockfd;
+	int changed;			 /* Have changes been made? */
+
+	struct list_head chains;
+
+	struct chain_head *chain_iterator_cur;
+	struct rule_head *rule_iterator_cur;
+
+	unsigned int num_chains;         /* number of user defined chains */
+
+	struct chain_head **chain_index;   /* array for fast chain list access*/
+	unsigned int        chain_index_sz;/* size of chain index array */
+
+	int sorted_offsets; /* if chains are received sorted from kernel,
+			     * then the offsets are also sorted. Says if its
+			     * possible to bsearch offsets using chain_index.
+			     */
+
+	STRUCT_GETINFO info;
+	STRUCT_GET_ENTRIES *entries;
+};
+
+enum bsearch_type {
+	BSEARCH_NAME,	/* Binary search after chain name */
+	BSEARCH_OFFSET,	/* Binary search based on offset */
+};
+
+/* allocate a new chain head for the cache */
+static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
+{
+	struct chain_head *c = malloc(sizeof(*c));
+	if (!c)
+		return NULL;
+	memset(c, 0, sizeof(*c));
+
+	strncpy(c->name, name, TABLE_MAXNAMELEN);
+	c->hooknum = hooknum;
+	INIT_LIST_HEAD(&c->rules);
+
+	return c;
+}
+
+/* allocate and initialize a new rule for the cache */
+static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
+{
+	struct rule_head *r = malloc(sizeof(*r)+size);
+	if (!r)
+		return NULL;
+	memset(r, 0, sizeof(*r));
+
+	r->chain = c;
+	r->size = size;
+
+	return r;
+}
+
+/* notify us that the ruleset has been modified by the user */
+static inline void
+set_changed(struct xtc_handle *h)
+{
+	h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(struct xtc_handle *h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+
+/**********************************************************************
+ * iptc blob utility functions (iptcb_*)
+ **********************************************************************/
+
+static inline int
+iptcb_get_number(const STRUCT_ENTRY *i,
+	   const STRUCT_ENTRY *seek,
+	   unsigned int *pos)
+{
+	if (i == seek)
+		return 1;
+	(*pos)++;
+	return 0;
+}
+
+static inline int
+iptcb_get_entry_n(STRUCT_ENTRY *i,
+	    unsigned int number,
+	    unsigned int *pos,
+	    STRUCT_ENTRY **pe)
+{
+	if (*pos == number) {
+		*pe = i;
+		return 1;
+	}
+	(*pos)++;
+	return 0;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_get_entry(struct xtc_handle *h, unsigned int offset)
+{
+	return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
+}
+
+static unsigned int
+iptcb_entry2index(struct xtc_handle *const h, const STRUCT_ENTRY *seek)
+{
+	unsigned int pos = 0;
+
+	if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+			  iptcb_get_number, seek, &pos) == 0) {
+		fprintf(stderr, "ERROR: offset %u not an entry!\n",
+			(unsigned int)((char *)seek - (char *)h->entries->entrytable));
+		abort();
+	}
+	return pos;
+}
+
+static inline STRUCT_ENTRY *
+iptcb_offset2entry(struct xtc_handle *h, unsigned int offset)
+{
+	return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
+}
+
+
+static inline unsigned long
+iptcb_entry2offset(struct xtc_handle *const h, const STRUCT_ENTRY *e)
+{
+	return (void *)e - (void *)h->entries->entrytable;
+}
+
+static inline unsigned int
+iptcb_offset2index(struct xtc_handle *const h, unsigned int offset)
+{
+	return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, struct xtc_handle *h)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUMHOOKS; i++) {
+		if ((h->info.valid_hooks & (1 << i))
+		    && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
+			return i+1;
+	}
+	return 0;
+}
+
+
+/**********************************************************************
+ * Chain index (cache utility) functions
+ **********************************************************************
+ * The chain index is an array with pointers into the chain list, with
+ * CHAIN_INDEX_BUCKET_LEN spacing.  This facilitates the ability to
+ * speedup chain list searching, by find a more optimal starting
+ * points when searching the linked list.
+ *
+ * The starting point can be found fast by using a binary search of
+ * the chain index. Thus, reducing the previous search complexity of
+ * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN.
+ *
+ * A nice property of the chain index, is that the "bucket" list
+ * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will
+ * change this). Oppose to hashing, where the "bucket" list length can
+ * vary a lot.
+ */
+#ifndef CHAIN_INDEX_BUCKET_LEN
+#define CHAIN_INDEX_BUCKET_LEN 40
+#endif
+
+/* Another nice property of the chain index is that inserting/creating
+ * chains in chain list don't change the correctness of the chain
+ * index, it only causes longer lists in the buckets.
+ *
+ * To mitigate the performance penalty of longer bucket lists and the
+ * penalty of rebuilding, the chain index is rebuild only when
+ * CHAIN_INDEX_INSERT_MAX chains has been added.
+ */
+#ifndef CHAIN_INDEX_INSERT_MAX
+#define CHAIN_INDEX_INSERT_MAX 355
+#endif
+
+static inline unsigned int iptcc_is_builtin(struct chain_head *c);
+
+/* Use binary search in the chain index array, to find a chain_head
+ * pointer closest to the place of the searched name element.
+ *
+ * Notes that, binary search (obviously) requires that the chain list
+ * is sorted by name.
+ *
+ * The not so obvious: The chain index array, is actually both sorted
+ * by name and offset, at the same time!.  This is only true because,
+ * chain are stored sorted in the kernel (as we pushed it in sorted).
+ *
+ */
+static struct list_head *
+__iptcc_bsearch_chain_index(const char *name, unsigned int offset,
+			    unsigned int *idx, struct xtc_handle *handle,
+			    enum bsearch_type type)
+{
+	unsigned int pos, end;
+	int res;
+
+	struct list_head *list_pos;
+	list_pos=&handle->chains;
+
+	/* Check for empty array, e.g. no user defined chains */
+	if (handle->chain_index_sz == 0) {
+		debug("WARNING: handle->chain_index_sz == 0\n");
+		return list_pos;
+	}
+
+	/* Init */
+	end = handle->chain_index_sz;
+	pos = end / 2;
+
+	debug("bsearch Find chain:%s (pos:%d end:%d) (offset:%d)\n",
+	      name, pos, end, offset);
+
+	/* Loop */
+ loop:
+	if (!handle->chain_index[pos]) {
+		fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos);
+		return &handle->chains; /* Be safe, return orig start pos */
+	}
+
+	debug("bsearch Index[%d] name:%s ",
+	      pos, handle->chain_index[pos]->name);
+
+	/* Support for different compare functions */
+	switch (type) {
+	case BSEARCH_NAME:
+		res = strcmp(name, handle->chain_index[pos]->name);
+		break;
+	case BSEARCH_OFFSET:
+		debug("head_offset:[%d] foot_offset:[%d] ",
+		      handle->chain_index[pos]->head_offset,
+		      handle->chain_index[pos]->foot_offset);
+		res = offset - handle->chain_index[pos]->head_offset;
+		break;
+	default:
+		fprintf(stderr, "ERROR: %d not a valid bsearch type\n",
+			type);
+		abort();
+		break;
+	}
+	debug("res:%d ", res);
+
+
+	list_pos = &handle->chain_index[pos]->list;
+	*idx = pos;
+
+	if (res == 0) { /* Found element, by direct hit */
+		debug("[found] Direct hit pos:%d end:%d\n", pos, end);
+		return list_pos;
+	} else if (res < 0) { /* Too far, jump back */
+		end = pos;
+		pos = pos / 2;
+
+		/* Exit case: First element of array */
+		if (end == 0) {
+			debug("[found] Reached first array elem (end%d)\n",end);
+			return list_pos;
+		}
+		debug("jump back to pos:%d (end:%d)\n", pos, end);
+		goto loop;
+	} else { /* res > 0; Not far enough, jump forward */
+
+		/* Exit case: Last element of array */
+		if (pos == handle->chain_index_sz-1) {
+			debug("[found] Last array elem (end:%d)\n", end);
+			return list_pos;
+		}
+
+		/* Exit case: Next index less, thus elem in this list section */
+		switch (type) {
+		case BSEARCH_NAME:
+			res = strcmp(name, handle->chain_index[pos+1]->name);
+			break;
+		case BSEARCH_OFFSET:
+			res = offset - handle->chain_index[pos+1]->head_offset;
+			break;
+		}
+
+		if (res < 0) {
+			debug("[found] closest list (end:%d)\n", end);
+			return list_pos;
+		}
+
+		pos = (pos+end)/2;
+		debug("jump forward to pos:%d (end:%d)\n", pos, end);
+		goto loop;
+	}
+}
+
+/* Wrapper for string chain name based bsearch */
+static struct list_head *
+iptcc_bsearch_chain_index(const char *name, unsigned int *idx,
+			  struct xtc_handle *handle)
+{
+	return __iptcc_bsearch_chain_index(name, 0, idx, handle, BSEARCH_NAME);
+}
+
+
+/* Wrapper for offset chain based bsearch */
+static struct list_head *
+iptcc_bsearch_chain_offset(unsigned int offset, unsigned int *idx,
+			  struct xtc_handle *handle)
+{
+	struct list_head *pos;
+
+	/* If chains were not received sorted from kernel, then the
+	 * offset bsearch is not possible.
+	 */
+	if (!handle->sorted_offsets)
+		pos = handle->chains.next;
+	else
+		pos = __iptcc_bsearch_chain_index(NULL, offset, idx, handle,
+						  BSEARCH_OFFSET);
+	return pos;
+}
+
+
+#ifdef DEBUG
+/* Trivial linear search of chain index. Function used for verifying
+   the output of bsearch function */
+static struct list_head *
+iptcc_linearly_search_chain_index(const char *name, struct xtc_handle *handle)
+{
+	unsigned int i=0;
+	int res=0;
+
+	struct list_head *list_pos;
+	list_pos = &handle->chains;
+
+	if (handle->chain_index_sz)
+		list_pos = &handle->chain_index[0]->list;
+
+	/* Linearly walk of chain index array */
+
+	for (i=0; i < handle->chain_index_sz; i++) {
+		if (handle->chain_index[i]) {
+			res = strcmp(handle->chain_index[i]->name, name);
+			if (res > 0)
+				break; // One step too far
+			list_pos = &handle->chain_index[i]->list;
+			if (res == 0)
+				break; // Direct hit
+		}
+	}
+
+	return list_pos;
+}
+#endif
+
+static int iptcc_chain_index_alloc(struct xtc_handle *h)
+{
+	unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
+	unsigned int array_elems;
+	unsigned int array_mem;
+
+	/* Allocate memory for the chain index array */
+	array_elems = (h->num_chains / list_length) +
+                      (h->num_chains % list_length ? 1 : 0);
+	array_mem   = sizeof(h->chain_index) * array_elems;
+
+	debug("Alloc Chain index, elems:%d mem:%d bytes\n",
+	      array_elems, array_mem);
+
+	h->chain_index = malloc(array_mem);
+	if (h->chain_index == NULL && array_mem > 0) {
+		h->chain_index_sz = 0;
+		return -ENOMEM;
+	}
+	memset(h->chain_index, 0, array_mem);
+	h->chain_index_sz = array_elems;
+
+	return 1;
+}
+
+static void iptcc_chain_index_free(struct xtc_handle *h)
+{
+	h->chain_index_sz = 0;
+	free(h->chain_index);
+}
+
+
+#ifdef DEBUG
+static void iptcc_chain_index_dump(struct xtc_handle *h)
+{
+	unsigned int i = 0;
+
+	/* Dump: contents of chain index array */
+	for (i=0; i < h->chain_index_sz; i++) {
+		if (h->chain_index[i]) {
+			fprintf(stderr, "Chain index[%d].name: %s\n",
+				i, h->chain_index[i]->name);
+		}
+	}
+}
+#endif
+
+/* Build the chain index */
+static int iptcc_chain_index_build(struct xtc_handle *h)
+{
+	unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
+	unsigned int chains = 0;
+	unsigned int cindex = 0;
+	struct chain_head *c;
+
+	/* Build up the chain index array here */
+	debug("Building chain index\n");
+
+	debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n",
+		h->num_chains, list_length, h->chain_index_sz);
+
+	if (h->chain_index_sz == 0)
+		return 0;
+
+	list_for_each_entry(c, &h->chains, list) {
+
+		/* Issue: The index array needs to start after the
+		 * builtin chains, as they are not sorted */
+		if (!iptcc_is_builtin(c)) {
+			cindex=chains / list_length;
+
+			/* Safe guard, break out on array limit, this
+			 * is useful if chains are added and array is
+			 * rebuild, without realloc of memory. */
+			if (cindex >= h->chain_index_sz)
+				break;
+
+			if ((chains % list_length)== 0) {
+				debug("\nIndex[%d] Chains:", cindex);
+				h->chain_index[cindex] = c;
+			}
+			chains++;
+		}
+		debug("%s, ", c->name);
+	}
+	debug("\n");
+
+	return 1;
+}
+
+static int iptcc_chain_index_rebuild(struct xtc_handle *h)
+{
+	debug("REBUILD chain index array\n");
+	iptcc_chain_index_free(h);
+	if ((iptcc_chain_index_alloc(h)) < 0)
+		return -ENOMEM;
+	iptcc_chain_index_build(h);
+	return 1;
+}
+
+/* Delete chain (pointer) from index array.  Removing an element from
+ * the chain list only affects the chain index array, if the chain
+ * index points-to/uses that list pointer.
+ *
+ * There are different strategies, the simple and safe is to rebuild
+ * the chain index every time.  The more advanced is to update the
+ * array index to point to the next element, but that requires some
+ * house keeping and boundry checks.  The advanced is implemented, as
+ * the simple approach behaves badly when all chains are deleted
+ * because list_for_each processing will always hit the first chain
+ * index, thus causing a rebuild for every chain.
+ */
+static int iptcc_chain_index_delete_chain(struct chain_head *c, struct xtc_handle *h)
+{
+	struct list_head *index_ptr, *next;
+	struct chain_head *c2;
+	unsigned int idx, idx2;
+
+	index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h);
+
+	debug("Del chain[%s] c->list:%p index_ptr:%p\n",
+	      c->name, &c->list, index_ptr);
+
+	/* Save the next pointer */
+	next = c->list.next;
+	list_del(&c->list);
+
+	if (index_ptr == &c->list) { /* Chain used as index ptr */
+
+		/* See if its possible to avoid a rebuild, by shifting
+		 * to next pointer.  Its possible if the next pointer
+		 * is located in the same index bucket.
+		 */
+		c2         = list_entry(next, struct chain_head, list);
+		iptcc_bsearch_chain_index(c2->name, &idx2, h);
+		if (idx != idx2) {
+			/* Rebuild needed */
+			return iptcc_chain_index_rebuild(h);
+		} else {
+			/* Avoiding rebuild */
+			debug("Update cindex[%d] with next ptr name:[%s]\n",
+			      idx, c2->name);
+			h->chain_index[idx]=c2;
+			return 0;
+		}
+	}
+	return 0;
+}
+
+
+/**********************************************************************
+ * iptc cache utility functions (iptcc_*)
+ **********************************************************************/
+
+/* Is the given chain builtin (1) or user-defined (0) */
+static inline unsigned int iptcc_is_builtin(struct chain_head *c)
+{
+	return (c->hooknum ? 1 : 0);
+}
+
+/* Get a specific rule within a chain */
+static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
+					    unsigned int rulenum)
+{
+	struct rule_head *r;
+	unsigned int num = 0;
+
+	list_for_each_entry(r, &c->rules, list) {
+		num++;
+		if (num == rulenum)
+			return r;
+	}
+	return NULL;
+}
+
+/* Get a specific rule within a chain backwards */
+static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
+					    unsigned int rulenum)
+{
+	struct rule_head *r;
+	unsigned int num = 0;
+
+	list_for_each_entry_reverse(r, &c->rules, list) {
+		num++;
+		if (num == rulenum)
+			return r;
+	}
+	return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_chain_by_offset(struct xtc_handle *handle, unsigned int offset)
+{
+	struct list_head *pos;
+	struct list_head *list_start_pos;
+	unsigned int i;
+
+	if (list_empty(&handle->chains))
+		return NULL;
+
+	/* Find a smart place to start the search */
+  	list_start_pos = iptcc_bsearch_chain_offset(offset, &i, handle);
+
+	/* Note that iptcc_bsearch_chain_offset() skips builtin
+	 * chains, but this function is only used for finding jump
+	 * targets, and a buildin chain is not a valid jump target */
+
+	debug("Offset:[%u] starting search at index:[%u]\n", offset, i);
+//	list_for_each(pos, &handle->chains) {
+	list_for_each(pos, list_start_pos->prev) {
+		struct chain_head *c = list_entry(pos, struct chain_head, list);
+		debug(".");
+		if (offset >= c->head_offset && offset <= c->foot_offset) {
+			debug("Offset search found chain:[%s]\n", c->name);
+			return c;
+		}
+	}
+
+	return NULL;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+iptcc_find_label(const char *name, struct xtc_handle *handle)
+{
+	struct list_head *pos;
+	struct list_head *list_start_pos;
+	unsigned int i=0;
+	int res;
+
+	if (list_empty(&handle->chains))
+		return NULL;
+
+	/* First look at builtin chains */
+	list_for_each(pos, &handle->chains) {
+		struct chain_head *c = list_entry(pos, struct chain_head, list);
+		if (!iptcc_is_builtin(c))
+			break;
+		if (!strcmp(c->name, name))
+			return c;
+	}
+
+	/* Find a smart place to start the search via chain index */
+  	//list_start_pos = iptcc_linearly_search_chain_index(name, handle);
+  	list_start_pos = iptcc_bsearch_chain_index(name, &i, handle);
+
+	/* Handel if bsearch bails out early */
+	if (list_start_pos == &handle->chains) {
+		list_start_pos = pos;
+	}
+#ifdef DEBUG
+	else {
+		/* Verify result of bsearch against linearly index search */
+		struct list_head *test_pos;
+		struct chain_head *test_c, *tmp_c;
+		test_pos = iptcc_linearly_search_chain_index(name, handle);
+		if (list_start_pos != test_pos) {
+			debug("BUG in chain_index search\n");
+			test_c=list_entry(test_pos,      struct chain_head,list);
+			tmp_c =list_entry(list_start_pos,struct chain_head,list);
+			debug("Verify search found:\n");
+			debug(" Chain:%s\n", test_c->name);
+			debug("BSearch found:\n");
+			debug(" Chain:%s\n", tmp_c->name);
+			exit(42);
+		}
+	}
+#endif
+
+	/* Initial/special case, no user defined chains */
+	if (handle->num_chains == 0)
+		return NULL;
+
+	/* Start searching through the chain list */
+	list_for_each(pos, list_start_pos->prev) {
+		struct chain_head *c = list_entry(pos, struct chain_head, list);
+		res = strcmp(c->name, name);
+		debug("List search name:%s == %s res:%d\n", name, c->name, res);
+		if (res==0)
+			return c;
+
+		/* We can stop earlier as we know list is sorted */
+		if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/
+			debug(" Not in list, walked too far, sorted list\n");
+			return NULL;
+		}
+
+		/* Stop on wrap around, if list head is reached */
+		if (pos == &handle->chains) {
+			debug("Stop, list head reached\n");
+			return NULL;
+		}
+	}
+
+	debug("List search NOT found name:%s\n", name);
+	return NULL;
+}
+
+/* called when rule is to be removed from cache */
+static void iptcc_delete_rule(struct rule_head *r)
+{
+	DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
+	/* clean up reference count of called chain */
+	if (r->type == IPTCC_R_JUMP
+	    && r->jump)
+		r->jump->references--;
+
+	list_del(&r->list);
+	free(r);
+}
+
+
+/**********************************************************************
+ * RULESET PARSER (blob -> cache)
+ **********************************************************************/
+
+/* Delete policy rule of previous chain, since cache doesn't contain
+ * chain policy rules.
+ * WARNING: This function has ugly design and relies on a lot of context, only
+ * to be called from specific places within the parser */
+static int __iptcc_p_del_policy(struct xtc_handle *h, unsigned int num)
+{
+	const unsigned char *data;
+
+	if (h->chain_iterator_cur) {
+		/* policy rule is last rule */
+		struct rule_head *pr = (struct rule_head *)
+			h->chain_iterator_cur->rules.prev;
+
+		/* save verdict */
+		data = GET_TARGET(pr->entry)->data;
+		h->chain_iterator_cur->verdict = *(const int *)data;
+
+		/* save counter and counter_map information */
+		h->chain_iterator_cur->counter_map.maptype =
+						COUNTER_MAP_ZEROED;
+		h->chain_iterator_cur->counter_map.mappos = num-1;
+		memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
+			sizeof(h->chain_iterator_cur->counters));
+
+		/* foot_offset points to verdict rule */
+		h->chain_iterator_cur->foot_index = num;
+		h->chain_iterator_cur->foot_offset = pr->offset;
+
+		/* delete rule from cache */
+		iptcc_delete_rule(pr);
+		h->chain_iterator_cur->num_rules--;
+
+		return 1;
+	}
+	return 0;
+}
+
+/* alphabetically insert a chain into the list */
+static void iptc_insert_chain(struct xtc_handle *h, struct chain_head *c)
+{
+	struct chain_head *tmp;
+	struct list_head  *list_start_pos;
+	unsigned int i=1;
+
+	/* Find a smart place to start the insert search */
+  	list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h);
+
+	/* Handle the case, where chain.name is smaller than index[0] */
+	if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) {
+		h->chain_index[0] = c; /* Update chain index head */
+		list_start_pos = h->chains.next;
+		debug("Update chain_index[0] with %s\n", c->name);
+	}
+
+	/* Handel if bsearch bails out early */
+	if (list_start_pos == &h->chains) {
+		list_start_pos = h->chains.next;
+	}
+
+	/* sort only user defined chains */
+	if (!c->hooknum) {
+		list_for_each_entry(tmp, list_start_pos->prev, list) {
+			if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
+				list_add(&c->list, tmp->list.prev);
+				return;
+			}
+
+			/* Stop if list head is reached */
+			if (&tmp->list == &h->chains) {
+				debug("Insert, list head reached add to tail\n");
+				break;
+			}
+		}
+	}
+
+	/* survived till end of list: add at tail */
+	list_add_tail(&c->list, &h->chains);
+}
+
+/* Another ugly helper function split out of cache_add_entry to make it less
+ * spaghetti code */
+static void __iptcc_p_add_chain(struct xtc_handle *h, struct chain_head *c,
+				unsigned int offset, unsigned int *num)
+{
+	struct list_head  *tail = h->chains.prev;
+	struct chain_head *ctail;
+
+	__iptcc_p_del_policy(h, *num);
+
+	c->head_offset = offset;
+	c->index = *num;
+
+	/* Chains from kernel are already sorted, as they are inserted
+	 * sorted. But there exists an issue when shifting to 1.4.0
+	 * from an older version, as old versions allow last created
+	 * chain to be unsorted.
+	 */
+	if (iptcc_is_builtin(c)) /* Only user defined chains are sorted*/
+		list_add_tail(&c->list, &h->chains);
+	else {
+		ctail = list_entry(tail, struct chain_head, list);
+
+		if (strcmp(c->name, ctail->name) > 0 ||
+		    iptcc_is_builtin(ctail))
+			list_add_tail(&c->list, &h->chains);/* Already sorted*/
+		else {
+			iptc_insert_chain(h, c);/* Was not sorted */
+
+			/* Notice, if chains were not received sorted
+			 * from kernel, then an offset bsearch is no
+			 * longer valid.
+			 */
+			h->sorted_offsets = 0;
+
+			debug("NOTICE: chain:[%s] was NOT sorted(ctail:%s)\n",
+			      c->name, ctail->name);
+		}
+	}
+
+	h->chain_iterator_cur = c;
+}
+
+/* main parser function: add an entry from the blob to the cache */
+static int cache_add_entry(STRUCT_ENTRY *e,
+			   struct xtc_handle *h,
+			   STRUCT_ENTRY **prev,
+			   unsigned int *num)
+{
+	unsigned int builtin;
+	unsigned int offset = (char *)e - (char *)h->entries->entrytable;
+
+	DEBUGP("entering...");
+
+	/* Last entry ("policy rule"). End it.*/
+	if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
+		/* This is the ERROR node at the end of the chain */
+		DEBUGP_C("%u:%u: end of table:\n", *num, offset);
+
+		__iptcc_p_del_policy(h, *num);
+
+		h->chain_iterator_cur = NULL;
+		goto out_inc;
+	}
+
+	/* We know this is the start of a new chain if it's an ERROR
+	 * target, or a hook entry point */
+
+	if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+		struct chain_head *c =
+			iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
+		DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
+			(char *)c->name, c);
+		if (!c) {
+			errno = -ENOMEM;
+			return -1;
+		}
+		h->num_chains++; /* New user defined chain */
+
+		__iptcc_p_add_chain(h, c, offset, num);
+
+	} else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
+		struct chain_head *c =
+			iptcc_alloc_chain_head((char *)hooknames[builtin-1],
+						builtin);
+		DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
+			*num, offset, c, &c->rules);
+		if (!c) {
+			errno = -ENOMEM;
+			return -1;
+		}
+
+		c->hooknum = builtin;
+
+		__iptcc_p_add_chain(h, c, offset, num);
+
+		/* FIXME: this is ugly. */
+		goto new_rule;
+	} else {
+		/* has to be normal rule */
+		struct rule_head *r;
+new_rule:
+
+		if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
+					   e->next_offset))) {
+			errno = ENOMEM;
+			return -1;
+		}
+		DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
+
+		r->index = *num;
+		r->offset = offset;
+		memcpy(r->entry, e, e->next_offset);
+		r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+		r->counter_map.mappos = r->index;
+
+		/* handling of jumps, etc. */
+		if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
+			STRUCT_STANDARD_TARGET *t;
+
+			t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+			if (t->target.u.target_size
+			    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+				errno = EINVAL;
+				free(r);
+				return -1;
+			}
+
+			if (t->verdict < 0) {
+				DEBUGP_C("standard, verdict=%d\n", t->verdict);
+				r->type = IPTCC_R_STANDARD;
+			} else if (t->verdict == r->offset+e->next_offset) {
+				DEBUGP_C("fallthrough\n");
+				r->type = IPTCC_R_FALLTHROUGH;
+			} else {
+				DEBUGP_C("jump, target=%u\n", t->verdict);
+				r->type = IPTCC_R_JUMP;
+				/* Jump target fixup has to be deferred
+				 * until second pass, since we migh not
+				 * yet have parsed the target */
+			}
+		} else {
+			DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
+			r->type = IPTCC_R_MODULE;
+		}
+
+		list_add_tail(&r->list, &h->chain_iterator_cur->rules);
+		h->chain_iterator_cur->num_rules++;
+	}
+out_inc:
+	(*num)++;
+	return 0;
+}
+
+
+/* parse an iptables blob into it's pieces */
+static int parse_table(struct xtc_handle *h)
+{
+	STRUCT_ENTRY *prev;
+	unsigned int num = 0;
+	struct chain_head *c;
+
+	/* Assume that chains offsets are sorted, this verified during
+	   parsing of ruleset (in __iptcc_p_add_chain())*/
+	h->sorted_offsets = 1;
+
+	/* First pass: over ruleset blob */
+	ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
+			cache_add_entry, h, &prev, &num);
+
+	/* Build the chain index, used for chain list search speedup */
+	if ((iptcc_chain_index_alloc(h)) < 0)
+		return -ENOMEM;
+	iptcc_chain_index_build(h);
+
+	/* Second pass: fixup parsed data from first pass */
+	list_for_each_entry(c, &h->chains, list) {
+		struct rule_head *r;
+		list_for_each_entry(r, &c->rules, list) {
+			struct chain_head *lc;
+			STRUCT_STANDARD_TARGET *t;
+
+			if (r->type != IPTCC_R_JUMP)
+				continue;
+
+			t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+			lc = iptcc_find_chain_by_offset(h, t->verdict);
+			if (!lc)
+				return -1;
+			r->jump = lc;
+			lc->references++;
+		}
+	}
+
+	return 1;
+}
+
+
+/**********************************************************************
+ * RULESET COMPILATION (cache -> blob)
+ **********************************************************************/
+
+/* Convenience structures */
+struct iptcb_chain_start{
+	STRUCT_ENTRY e;
+	struct xt_error_target name;
+};
+#define IPTCB_CHAIN_START_SIZE	(sizeof(STRUCT_ENTRY) +			\
+				 ALIGN(sizeof(struct xt_error_target)))
+
+struct iptcb_chain_foot {
+	STRUCT_ENTRY e;
+	STRUCT_STANDARD_TARGET target;
+};
+#define IPTCB_CHAIN_FOOT_SIZE	(sizeof(STRUCT_ENTRY) +			\
+				 ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
+
+struct iptcb_chain_error {
+	STRUCT_ENTRY entry;
+	struct xt_error_target target;
+};
+#define IPTCB_CHAIN_ERROR_SIZE	(sizeof(STRUCT_ENTRY) +			\
+				 ALIGN(sizeof(struct xt_error_target)))
+
+
+
+/* compile rule from cache into blob */
+static inline int iptcc_compile_rule (struct xtc_handle *h, STRUCT_REPLACE *repl, struct rule_head *r)
+{
+	/* handle jumps */
+	if (r->type == IPTCC_R_JUMP) {
+		STRUCT_STANDARD_TARGET *t;
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+		/* memset for memcmp convenience on delete/replace */
+		memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+		strcpy(t->target.u.user.name, STANDARD_TARGET);
+		/* Jumps can only happen to builtin chains, so we
+		 * can safely assume that they always have a header */
+		t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
+	} else if (r->type == IPTCC_R_FALLTHROUGH) {
+		STRUCT_STANDARD_TARGET *t;
+		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
+		t->verdict = r->offset + r->size;
+	}
+
+	/* copy entry from cache to blob */
+	memcpy((char *)repl->entries+r->offset, r->entry, r->size);
+
+	return 1;
+}
+
+/* compile chain from cache into blob */
+static int iptcc_compile_chain(struct xtc_handle *h, STRUCT_REPLACE *repl, struct chain_head *c)
+{
+	int ret;
+	struct rule_head *r;
+	struct iptcb_chain_start *head;
+	struct iptcb_chain_foot *foot;
+
+	/* only user-defined chains have heaer */
+	if (!iptcc_is_builtin(c)) {
+		/* put chain header in place */
+		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.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;
+	}
+
+	/* iterate over rules */
+	list_for_each_entry(r, &c->rules, list) {
+		ret = iptcc_compile_rule(h, repl, r);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* put chain footer in place */
+	foot = (void *)repl->entries + c->foot_offset;
+	foot->e.target_offset = sizeof(STRUCT_ENTRY);
+	foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
+	strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
+	foot->target.target.u.target_size =
+				ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+	/* builtin targets have verdict, others return */
+	if (iptcc_is_builtin(c))
+		foot->target.verdict = c->verdict;
+	else
+		foot->target.verdict = RETURN;
+	/* set policy-counters */
+	memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
+
+	return 0;
+}
+
+/* calculate offset and number for every rule in the cache */
+static int iptcc_compile_chain_offsets(struct xtc_handle *h, struct chain_head *c,
+				       unsigned int *offset, unsigned int *num)
+{
+	struct rule_head *r;
+
+	c->head_offset = *offset;
+	DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
+
+	if (!iptcc_is_builtin(c))  {
+		/* Chain has header */
+		*offset += sizeof(STRUCT_ENTRY)
+			     + ALIGN(sizeof(struct xt_error_target));
+		(*num)++;
+	}
+
+	list_for_each_entry(r, &c->rules, list) {
+		DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
+		r->offset = *offset;
+		r->index = *num;
+		*offset += r->size;
+		(*num)++;
+	}
+
+	DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
+		*offset, *num);
+	c->foot_offset = *offset;
+	c->foot_index = *num;
+	*offset += sizeof(STRUCT_ENTRY)
+		   + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+	(*num)++;
+
+	return 1;
+}
+
+/* put the pieces back together again */
+static int iptcc_compile_table_prep(struct xtc_handle *h, unsigned int *size)
+{
+	struct chain_head *c;
+	unsigned int offset = 0, num = 0;
+	int ret = 0;
+
+	/* First pass: calculate offset for every rule */
+	list_for_each_entry(c, &h->chains, list) {
+		ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Append one error rule at end of chain */
+	num++;
+	offset += sizeof(STRUCT_ENTRY)
+		  + ALIGN(sizeof(struct xt_error_target));
+
+	/* ruleset size is now in offset */
+	*size = offset;
+	return num;
+}
+
+static int iptcc_compile_table(struct xtc_handle *h, STRUCT_REPLACE *repl)
+{
+	struct chain_head *c;
+	struct iptcb_chain_error *error;
+
+	/* Second pass: copy from cache to offsets, fill in jumps */
+	list_for_each_entry(c, &h->chains, list) {
+		int ret = iptcc_compile_chain(h, repl, c);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Append error rule at end of chain */
+	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.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;
+}
+
+/**********************************************************************
+ * EXTERNAL API (operates on cache only)
+ **********************************************************************/
+
+/* Allocate handle of given size */
+static struct xtc_handle *
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+	struct xtc_handle *h;
+
+	h = malloc(sizeof(*h));
+	if (!h) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	memset(h, 0, sizeof(*h));
+	INIT_LIST_HEAD(&h->chains);
+	strcpy(h->info.name, tablename);
+
+	h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
+	if (!h->entries)
+		goto out_free_handle;
+
+	strcpy(h->entries->name, tablename);
+	h->entries->size = size;
+
+	return h;
+
+out_free_handle:
+	free(h);
+
+	return NULL;
+}
+
+
+struct xtc_handle *
+TC_INIT(const char *tablename)
+{
+	struct xtc_handle *h;
+	STRUCT_GETINFO info;
+	unsigned int tmp;
+	socklen_t s;
+	int sockfd;
+
+retry:
+	iptc_fn = TC_INIT;
+
+	if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+	if (sockfd < 0)
+		return NULL;
+
+	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);
+	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
+		close(sockfd);
+		return NULL;
+	}
+
+	DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
+		info.valid_hooks, info.num_entries, info.size);
+
+	if ((h = alloc_handle(info.name, info.size, info.num_entries))
+	    == NULL) {
+		close(sockfd);
+		return NULL;
+	}
+
+	/* Initialize current state */
+	h->sockfd = sockfd;
+	h->info = info;
+
+	h->entries->size = h->info.size;
+
+	tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+	if (getsockopt(h->sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
+		       &tmp) < 0)
+		goto error;
+
+#ifdef IPTC_DEBUG2
+	{
+		int fd = open("/tmp/libiptc-so_get_entries.blob",
+				O_CREAT|O_WRONLY);
+		if (fd >= 0) {
+			write(fd, h->entries, tmp);
+			close(fd);
+		}
+	}
+#endif
+
+	if (parse_table(h) < 0)
+		goto error;
+
+	CHECK(h);
+	return h;
+error:
+	TC_FREE(h);
+	/* A different process changed the ruleset size, retry */
+	if (errno == EAGAIN)
+		goto retry;
+	return NULL;
+}
+
+void
+TC_FREE(struct xtc_handle *h)
+{
+	struct chain_head *c, *tmp;
+
+	iptc_fn = TC_FREE;
+	close(h->sockfd);
+
+	list_for_each_entry_safe(c, tmp, &h->chains, list) {
+		struct rule_head *r, *rtmp;
+
+		list_for_each_entry_safe(r, rtmp, &c->rules, list) {
+			free(r);
+		}
+
+		free(c);
+	}
+
+	iptcc_chain_index_free(h);
+
+	free(h->entries);
+	free(h);
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+	printf("Match name: `%s'\n", m->u.user.name);
+	return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, struct xtc_handle *const handle);
+
+void
+TC_DUMP_ENTRIES(struct xtc_handle *const handle)
+{
+	iptc_fn = TC_DUMP_ENTRIES;
+	CHECK(handle);
+
+	printf("libiptc v%s. %u bytes.\n",
+	       XTABLES_VERSION, handle->entries->size);
+	printf("Table `%s'\n", handle->info.name);
+	printf("Hooks: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
+	       handle->info.hook_entry[HOOK_PRE_ROUTING],
+	       handle->info.hook_entry[HOOK_LOCAL_IN],
+	       handle->info.hook_entry[HOOK_FORWARD],
+	       handle->info.hook_entry[HOOK_LOCAL_OUT],
+	       handle->info.hook_entry[HOOK_POST_ROUTING]);
+	printf("Underflows: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
+	       handle->info.underflow[HOOK_PRE_ROUTING],
+	       handle->info.underflow[HOOK_LOCAL_IN],
+	       handle->info.underflow[HOOK_FORWARD],
+	       handle->info.underflow[HOOK_LOCAL_OUT],
+	       handle->info.underflow[HOOK_POST_ROUTING]);
+
+	ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
+		      dump_entry, handle);
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, struct xtc_handle *const handle)
+{
+	iptc_fn = TC_IS_CHAIN;
+	return iptcc_find_label(chain, handle) != NULL;
+}
+
+static void iptcc_chain_iterator_advance(struct xtc_handle *handle)
+{
+	struct chain_head *c = handle->chain_iterator_cur;
+
+	if (c->list.next == &handle->chains)
+		handle->chain_iterator_cur = NULL;
+	else
+		handle->chain_iterator_cur =
+			list_entry(c->list.next, struct chain_head, list);
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(struct xtc_handle *handle)
+{
+	struct chain_head *c = list_entry(handle->chains.next,
+					  struct chain_head, list);
+
+	iptc_fn = TC_FIRST_CHAIN;
+
+
+	if (list_empty(&handle->chains)) {
+		DEBUGP(": no chains\n");
+		return NULL;
+	}
+
+	handle->chain_iterator_cur = c;
+	iptcc_chain_iterator_advance(handle);
+
+	DEBUGP(": returning `%s'\n", c->name);
+	return c->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(struct xtc_handle *handle)
+{
+	struct chain_head *c = handle->chain_iterator_cur;
+
+	iptc_fn = TC_NEXT_CHAIN;
+
+	if (!c) {
+		DEBUGP(": no more chains\n");
+		return NULL;
+	}
+
+	iptcc_chain_iterator_advance(handle);
+
+	DEBUGP(": returning `%s'\n", c->name);
+	return c->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_FIRST_RULE;
+
+	DEBUGP("first rule(%s): ", chain);
+
+	c = iptcc_find_label(chain, handle);
+	if (!c) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	/* Empty chain: single return/policy rule */
+	if (list_empty(&c->rules)) {
+		DEBUGP_C("no rules, returning NULL\n");
+		return NULL;
+	}
+
+	r = list_entry(c->rules.next, struct rule_head, list);
+	handle->rule_iterator_cur = r;
+	DEBUGP_C("%p\n", r);
+
+	return r->entry;
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, struct xtc_handle *handle)
+{
+	struct rule_head *r;
+
+	iptc_fn = TC_NEXT_RULE;
+	DEBUGP("rule_iterator_cur=%p...", handle->rule_iterator_cur);
+
+	if (handle->rule_iterator_cur == NULL) {
+		DEBUGP_C("returning NULL\n");
+		return NULL;
+	}
+
+	r = list_entry(handle->rule_iterator_cur->list.next,
+			struct rule_head, list);
+
+	iptc_fn = TC_NEXT_RULE;
+
+	DEBUGP_C("next=%p, head=%p...", &r->list,
+		&handle->rule_iterator_cur->chain->rules);
+
+	if (&r->list == &handle->rule_iterator_cur->chain->rules) {
+		handle->rule_iterator_cur = NULL;
+		DEBUGP_C("finished, returning NULL\n");
+		return NULL;
+	}
+
+	handle->rule_iterator_cur = r;
+
+	/* NOTE: prev is without any influence ! */
+	DEBUGP_C("returning rule %p\n", r);
+	return r->entry;
+}
+
+/* Returns a pointer to the target name of this position. */
+static const char *standard_target_map(int verdict)
+{
+	switch (verdict) {
+		case RETURN:
+			return LABEL_RETURN;
+			break;
+		case -NF_ACCEPT-1:
+			return LABEL_ACCEPT;
+			break;
+		case -NF_DROP-1:
+			return LABEL_DROP;
+			break;
+		case -NF_QUEUE-1:
+			return LABEL_QUEUE;
+			break;
+		default:
+			fprintf(stderr, "ERROR: %d not a valid target)\n",
+				verdict);
+			abort();
+			break;
+	}
+	/* not reached */
+	return NULL;
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
+			  struct xtc_handle *handle)
+{
+	STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+	struct rule_head *r = container_of(e, struct rule_head, entry[0]);
+	const unsigned char *data;
+
+	iptc_fn = TC_GET_TARGET;
+
+	switch(r->type) {
+		int spos;
+		case IPTCC_R_FALLTHROUGH:
+			return "";
+			break;
+		case IPTCC_R_JUMP:
+			DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
+			return r->jump->name;
+			break;
+		case IPTCC_R_STANDARD:
+			data = GET_TARGET(e)->data;
+			spos = *(const int *)data;
+			DEBUGP("r=%p, spos=%d'\n", r, spos);
+			return standard_target_map(spos);
+			break;
+		case IPTCC_R_MODULE:
+			return GET_TARGET(e)->u.user.name;
+			break;
+	}
+	return NULL;
+}
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, struct xtc_handle *const handle)
+{
+	struct chain_head *c;
+
+	iptc_fn = TC_BUILTIN;
+
+	c = iptcc_find_label(chain, handle);
+	if (!c) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	return iptcc_is_builtin(c);
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+	      STRUCT_COUNTERS *counters,
+	      struct xtc_handle *handle)
+{
+	struct chain_head *c;
+
+	iptc_fn = TC_GET_POLICY;
+
+	DEBUGP("called for chain %s\n", chain);
+
+	c = iptcc_find_label(chain, handle);
+	if (!c) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	if (!iptcc_is_builtin(c))
+		return NULL;
+
+	*counters = c->counters;
+
+	return standard_target_map(c->verdict);
+}
+
+static int
+iptcc_standard_map(struct rule_head *r, int verdict)
+{
+	STRUCT_ENTRY *e = r->entry;
+	STRUCT_STANDARD_TARGET *t;
+
+	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+	if (t->target.u.target_size
+	    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+		errno = EINVAL;
+		return 0;
+	}
+	/* memset for memcmp convenience on delete/replace */
+	memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+	strcpy(t->target.u.user.name, STANDARD_TARGET);
+	t->verdict = verdict;
+
+	r->type = IPTCC_R_STANDARD;
+
+	return 1;
+}
+
+static int
+iptcc_map_target(struct xtc_handle *const handle,
+	   struct rule_head *r)
+{
+	STRUCT_ENTRY *e = r->entry;
+	STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+	/* Maybe it's empty (=> fall through) */
+	if (strcmp(t->u.user.name, "") == 0) {
+		r->type = IPTCC_R_FALLTHROUGH;
+		return 1;
+	}
+	/* Maybe it's a standard target name... */
+	else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+		return iptcc_standard_map(r, -NF_ACCEPT - 1);
+	else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+		return iptcc_standard_map(r, -NF_DROP - 1);
+	else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+		return iptcc_standard_map(r, -NF_QUEUE - 1);
+	else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+		return iptcc_standard_map(r, RETURN);
+	else if (TC_BUILTIN(t->u.user.name, handle)) {
+		/* Can't jump to builtins. */
+		errno = EINVAL;
+		return 0;
+	} else {
+		/* Maybe it's an existing chain name. */
+		struct chain_head *c;
+		DEBUGP("trying to find chain `%s': ", t->u.user.name);
+
+		c = iptcc_find_label(t->u.user.name, handle);
+		if (c) {
+			DEBUGP_C("found!\n");
+			r->type = IPTCC_R_JUMP;
+			r->jump = c;
+			c->references++;
+			return 1;
+		}
+		DEBUGP_C("not found :(\n");
+	}
+
+	/* Must be a module?  If not, kernel will reject... */
+	/* memset to all 0 for your memcmp convenience: don't clear version */
+	memset(t->u.user.name + strlen(t->u.user.name),
+	       0,
+	       FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
+	r->type = IPTCC_R_MODULE;
+	set_changed(handle);
+	return 1;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+		const STRUCT_ENTRY *e,
+		unsigned int rulenum,
+		struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+	struct list_head *prev;
+
+	iptc_fn = TC_INSERT_ENTRY;
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	/* first rulenum index = 0
+	   first c->num_rules index = 1 */
+	if (rulenum > c->num_rules) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	/* If we are inserting at the end just take advantage of the
+	   double linked list, insert will happen before the entry
+	   prev points to. */
+	if (rulenum == c->num_rules) {
+		prev = &c->rules;
+	} else if (rulenum + 1 <= c->num_rules/2) {
+		r = iptcc_get_rule_num(c, rulenum + 1);
+		prev = &r->list;
+	} else {
+		r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+		prev = &r->list;
+	}
+
+	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	memcpy(r->entry, e, e->next_offset);
+	r->counter_map.maptype = COUNTER_MAP_SET;
+
+	if (!iptcc_map_target(handle, r)) {
+		free(r);
+		return 0;
+	}
+
+	list_add_tail(&r->list, prev);
+	c->num_rules++;
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+		 const STRUCT_ENTRY *e,
+		 unsigned int rulenum,
+		 struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r, *old;
+
+	iptc_fn = TC_REPLACE_ENTRY;
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (rulenum >= c->num_rules) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	/* Take advantage of the double linked list if possible. */
+	if (rulenum + 1 <= c->num_rules/2) {
+		old = iptcc_get_rule_num(c, rulenum + 1);
+	} else {
+		old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+	}
+
+	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	memcpy(r->entry, e, e->next_offset);
+	r->counter_map.maptype = COUNTER_MAP_SET;
+
+	if (!iptcc_map_target(handle, r)) {
+		free(r);
+		return 0;
+	}
+
+	list_add(&r->list, &old->list);
+	iptcc_delete_rule(old);
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+		const STRUCT_ENTRY *e,
+		struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_APPEND_ENTRY;
+	if (!(c = iptcc_find_label(chain, handle))) {
+		DEBUGP("unable to find chain `%s'\n", chain);
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
+		DEBUGP("unable to allocate rule for chain `%s'\n", chain);
+		errno = ENOMEM;
+		return 0;
+	}
+
+	memcpy(r->entry, e, e->next_offset);
+	r->counter_map.maptype = COUNTER_MAP_SET;
+
+	if (!iptcc_map_target(handle, r)) {
+		DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+		free(r);
+		return 0;
+	}
+
+	list_add_tail(&r->list, &c->rules);
+	c->num_rules++;
+
+	set_changed(handle);
+
+	return 1;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+		const unsigned char *a_elems,
+		const unsigned char *b_elems,
+		unsigned char **maskptr)
+{
+	const STRUCT_ENTRY_MATCH *b;
+	unsigned int i;
+
+	/* Offset of b is the same as a. */
+	b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+	if (a->u.match_size != b->u.match_size)
+		return 1;
+
+	if (strcmp(a->u.user.name, b->u.user.name) != 0)
+		return 1;
+
+	*maskptr += ALIGN(sizeof(*a));
+
+	for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+		if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+			return 1;
+	*maskptr += i;
+	return 0;
+}
+
+static inline int
+target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
+{
+	unsigned int i;
+	STRUCT_ENTRY_TARGET *ta, *tb;
+
+	if (a->type != b->type)
+		return 0;
+
+	ta = GET_TARGET(a->entry);
+	tb = GET_TARGET(b->entry);
+
+	switch (a->type) {
+	case IPTCC_R_FALLTHROUGH:
+		return 1;
+	case IPTCC_R_JUMP:
+		return a->jump == b->jump;
+	case IPTCC_R_STANDARD:
+		return ((STRUCT_STANDARD_TARGET *)ta)->verdict
+			== ((STRUCT_STANDARD_TARGET *)tb)->verdict;
+	case IPTCC_R_MODULE:
+		if (ta->u.target_size != tb->u.target_size)
+			return 0;
+		if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+			return 0;
+
+		for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
+			if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
+				return 0;
+		return 1;
+	default:
+		fprintf(stderr, "ERROR: bad type %i\n", a->type);
+		abort();
+	}
+}
+
+static unsigned char *
+is_same(const STRUCT_ENTRY *a,
+	const STRUCT_ENTRY *b,
+	unsigned char *matchmask);
+
+
+/* find the first rule in `chain' which matches `fw' and remove it unless dry_run is set */
+static int delete_entry(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
+			unsigned char *matchmask, struct xtc_handle *handle,
+			bool dry_run)
+{
+	struct chain_head *c;
+	struct rule_head *r, *i;
+
+	iptc_fn = TC_DELETE_ENTRY;
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	/* Create a rule_head from origfw. */
+	r = iptcc_alloc_rule(c, origfw->next_offset);
+	if (!r) {
+		errno = ENOMEM;
+		return 0;
+	}
+
+	memcpy(r->entry, origfw, origfw->next_offset);
+	r->counter_map.maptype = COUNTER_MAP_NOMAP;
+	if (!iptcc_map_target(handle, r)) {
+		DEBUGP("unable to map target of rule for chain `%s'\n", chain);
+		free(r);
+		return 0;
+	} else {
+		/* iptcc_map_target increment target chain references
+		 * since this is a fake rule only used for matching
+		 * the chain references count is decremented again.
+		 */
+		if (r->type == IPTCC_R_JUMP
+		    && r->jump)
+			r->jump->references--;
+	}
+
+	list_for_each_entry(i, &c->rules, list) {
+		unsigned char *mask;
+
+		mask = is_same(r->entry, i->entry, matchmask);
+		if (!mask)
+			continue;
+
+		if (!target_same(r, i, mask))
+			continue;
+
+		/* if we are just doing a dry run, we simply skip the rest */
+		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
+		 * pointer will then point to real next node */
+		if (i == handle->rule_iterator_cur) {
+			handle->rule_iterator_cur =
+				list_entry(handle->rule_iterator_cur->list.prev,
+					   struct rule_head, list);
+		}
+
+		c->num_rules--;
+		iptcc_delete_rule(i);
+
+		set_changed(handle);
+		free(r);
+		return 1;
+	}
+
+	free(r);
+	errno = ENOENT;
+	return 0;
+}
+
+/* check whether a specified rule is present */
+int TC_CHECK_ENTRY(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
+		   unsigned char *matchmask, struct xtc_handle *handle)
+{
+	/* do a dry-run delete to find out whether a matching rule exists */
+	return delete_entry(chain, origfw, matchmask, handle, true);
+}
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,	const STRUCT_ENTRY *origfw,
+		    unsigned char *matchmask, struct xtc_handle *handle)
+{
+	return delete_entry(chain, origfw, matchmask, handle, false);
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+		    unsigned int rulenum,
+		    struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_DELETE_NUM_ENTRY;
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (rulenum >= c->num_rules) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	/* Take advantage of the double linked list if possible. */
+	if (rulenum + 1 <= c->num_rules/2) {
+		r = iptcc_get_rule_num(c, rulenum + 1);
+	} else {
+		r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
+	}
+
+	/* If we are about to delete the rule that is the current
+	 * iterator, move rule iterator back.  next pointer will then
+	 * point to real next node */
+	if (r == handle->rule_iterator_cur) {
+		handle->rule_iterator_cur =
+			list_entry(handle->rule_iterator_cur->list.prev,
+				   struct rule_head, list);
+	}
+
+	c->num_rules--;
+	iptcc_delete_rule(r);
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r, *tmp;
+
+	iptc_fn = TC_FLUSH_ENTRIES;
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	list_for_each_entry_safe(r, tmp, &c->rules, list) {
+		iptcc_delete_rule(r);
+	}
+
+	c->num_rules = 0;
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_ZERO_ENTRIES;
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+		c->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+	list_for_each_entry(r, &c->rules, list) {
+		if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+			r->counter_map.maptype = COUNTER_MAP_ZEROED;
+	}
+
+	set_changed(handle);
+
+	return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+		unsigned int rulenum,
+		struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_READ_COUNTER;
+	CHECK(*handle);
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return NULL;
+	}
+
+	if (!(r = iptcc_get_rule_num(c, rulenum))) {
+		errno = E2BIG;
+		return NULL;
+	}
+
+	return &r->entry[0].counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+		unsigned int rulenum,
+		struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+
+	iptc_fn = TC_ZERO_COUNTER;
+	CHECK(handle);
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (!(r = iptcc_get_rule_num(c, rulenum))) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+		r->counter_map.maptype = COUNTER_MAP_ZEROED;
+
+	set_changed(handle);
+
+	return 1;
+}
+
+int
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+	       unsigned int rulenum,
+	       STRUCT_COUNTERS *counters,
+	       struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	struct rule_head *r;
+	STRUCT_ENTRY *e;
+
+	iptc_fn = TC_SET_COUNTER;
+	CHECK(handle);
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (!(r = iptcc_get_rule_num(c, rulenum))) {
+		errno = E2BIG;
+		return 0;
+	}
+
+	e = r->entry;
+	r->counter_map.maptype = COUNTER_MAP_SET;
+
+	memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
+{
+	static struct chain_head *c;
+	int capacity;
+	int exceeded;
+
+	iptc_fn = TC_CREATE_CHAIN;
+
+	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+	if (iptcc_find_label(chain, handle)
+	    || strcmp(chain, LABEL_DROP) == 0
+	    || strcmp(chain, LABEL_ACCEPT) == 0
+	    || strcmp(chain, LABEL_QUEUE) == 0
+	    || strcmp(chain, LABEL_RETURN) == 0) {
+		DEBUGP("Chain `%s' already exists\n", chain);
+		errno = EEXIST;
+		return 0;
+	}
+
+	if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+		DEBUGP("Chain name `%s' too long\n", chain);
+		errno = EINVAL;
+		return 0;
+	}
+
+	c = iptcc_alloc_chain_head(chain, 0);
+	if (!c) {
+		DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
+		errno = ENOMEM;
+		return 0;
+
+	}
+	handle->num_chains++; /* New user defined chain */
+
+	DEBUGP("Creating chain `%s'\n", chain);
+	iptc_insert_chain(handle, c); /* Insert sorted */
+
+	/* Inserting chains don't change the correctness of the chain
+	 * index (except if its smaller than index[0], but that
+	 * handled by iptc_insert_chain).  It only causes longer lists
+	 * in the buckets. Thus, only rebuild chain index when the
+	 * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains.
+	 */
+	capacity = handle->chain_index_sz * CHAIN_INDEX_BUCKET_LEN;
+	exceeded = handle->num_chains - capacity;
+	if (exceeded > CHAIN_INDEX_INSERT_MAX) {
+		debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n",
+		      capacity, exceeded, handle->num_chains);
+		iptcc_chain_index_rebuild(handle);
+	}
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+		  struct xtc_handle *handle)
+{
+	struct chain_head *c;
+
+	iptc_fn = TC_GET_REFERENCES;
+	if (!(c = iptcc_find_label(chain, handle))) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	*ref = c->references;
+
+	return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
+{
+	unsigned int references;
+	struct chain_head *c;
+
+	iptc_fn = TC_DELETE_CHAIN;
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		DEBUGP("cannot find chain `%s'\n", chain);
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (TC_BUILTIN(chain, handle)) {
+		DEBUGP("cannot remove builtin chain `%s'\n", chain);
+		errno = EINVAL;
+		return 0;
+	}
+
+	if (!TC_GET_REFERENCES(&references, chain, handle)) {
+		DEBUGP("cannot get references on chain `%s'\n", chain);
+		return 0;
+	}
+
+	if (references > 0) {
+		DEBUGP("chain `%s' still has references\n", chain);
+		errno = EMLINK;
+		return 0;
+	}
+
+	if (c->num_rules) {
+		DEBUGP("chain `%s' is not empty\n", chain);
+		errno = ENOTEMPTY;
+		return 0;
+	}
+
+	/* If we are about to delete the chain that is the current
+	 * iterator, move chain iterator forward. */
+	if (c == handle->chain_iterator_cur)
+		iptcc_chain_iterator_advance(handle);
+
+	handle->num_chains--; /* One user defined chain deleted */
+
+	//list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */
+	iptcc_chain_index_delete_chain(c, handle);
+	free(c);
+
+	DEBUGP("chain `%s' deleted\n", chain);
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+		    const IPT_CHAINLABEL newname,
+		    struct xtc_handle *handle)
+{
+	struct chain_head *c;
+	iptc_fn = TC_RENAME_CHAIN;
+
+	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+	if (iptcc_find_label(newname, handle)
+	    || strcmp(newname, LABEL_DROP) == 0
+	    || strcmp(newname, LABEL_ACCEPT) == 0
+	    || strcmp(newname, LABEL_QUEUE) == 0
+	    || strcmp(newname, LABEL_RETURN) == 0) {
+		errno = EEXIST;
+		return 0;
+	}
+
+	if (!(c = iptcc_find_label(oldname, handle))
+	    || TC_BUILTIN(oldname, handle)) {
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* This only unlinks "c" from the list, thus no free(c) */
+	iptcc_chain_index_delete_chain(c, handle);
+
+	/* Change the name of the chain */
+	strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+
+	/* Insert sorted into to list again */
+	iptc_insert_chain(handle, c);
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+	      const IPT_CHAINLABEL policy,
+	      STRUCT_COUNTERS *counters,
+	      struct xtc_handle *handle)
+{
+	struct chain_head *c;
+
+	iptc_fn = TC_SET_POLICY;
+
+	if (!(c = iptcc_find_label(chain, handle))) {
+		DEBUGP("cannot find chain `%s'\n", chain);
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (!iptcc_is_builtin(c)) {
+		DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
+		errno = ENOENT;
+		return 0;
+	}
+
+	if (strcmp(policy, LABEL_ACCEPT) == 0)
+		c->verdict = -NF_ACCEPT - 1;
+	else if (strcmp(policy, LABEL_DROP) == 0)
+		c->verdict = -NF_DROP - 1;
+	else {
+		errno = EINVAL;
+		return 0;
+	}
+
+	if (counters) {
+		/* set byte and packet counters */
+		memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
+		c->counter_map.maptype = COUNTER_MAP_SET;
+	} else {
+		c->counter_map.maptype = COUNTER_MAP_NOMAP;
+	}
+
+	set_changed(handle);
+
+	return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+		  const STRUCT_COUNTERS *a,
+		  const STRUCT_COUNTERS *b)
+{
+	answer->pcnt = a->pcnt - b->pcnt;
+	answer->bcnt = a->bcnt - b->bcnt;
+}
+
+
+static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx)
+{
+	newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0});
+	DEBUGP_C("NOMAP => zero\n");
+}
+
+static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
+				STRUCT_REPLACE *repl, unsigned int idx,
+				unsigned int mappos)
+{
+	/* Original read: X.
+	 * Atomic read on replacement: X + Y.
+	 * Currently in kernel: Z.
+	 * Want in kernel: X + Y + Z.
+	 * => Add in X + Y
+	 * => Add in replacement read.
+	 */
+	newcounters->counters[idx] = repl->counters[mappos];
+	DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
+}
+
+static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
+				STRUCT_REPLACE *repl, unsigned int idx,
+				unsigned int mappos, STRUCT_COUNTERS *counters)
+{
+	/* Original read: X.
+	 * Atomic read on replacement: X + Y.
+	 * Currently in kernel: Z.
+	 * Want in kernel: Y + Z.
+	 * => Add in Y.
+	 * => Add in (replacement read - original read).
+	 */
+	subtract_counters(&newcounters->counters[idx],
+			  &repl->counters[mappos],
+			  counters);
+	DEBUGP_C("ZEROED => mappos %u\n", mappos);
+}
+
+static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
+                             unsigned int idx, STRUCT_COUNTERS *counters)
+{
+	/* Want to set counter (iptables-restore) */
+
+	memcpy(&newcounters->counters[idx], counters,
+		sizeof(STRUCT_COUNTERS));
+
+	DEBUGP_C("SET\n");
+}
+
+
+int
+TC_COMMIT(struct xtc_handle *handle)
+{
+	/* Replace, then map back the counters. */
+	STRUCT_REPLACE *repl;
+	STRUCT_COUNTERS_INFO *newcounters;
+	struct chain_head *c;
+	int ret;
+	size_t counterlen;
+	int new_number;
+	unsigned int new_size;
+
+	iptc_fn = TC_COMMIT;
+	CHECK(*handle);
+
+	/* Don't commit if nothing changed. */
+	if (!handle->changed)
+		goto finished;
+
+	new_number = iptcc_compile_table_prep(handle, &new_size);
+	if (new_number < 0) {
+		errno = ENOMEM;
+		goto out_zero;
+	}
+
+	repl = malloc(sizeof(*repl) + new_size);
+	if (!repl) {
+		errno = ENOMEM;
+		goto out_zero;
+	}
+	memset(repl, 0, sizeof(*repl) + new_size);
+
+#if 0
+	TC_DUMP_ENTRIES(*handle);
+#endif
+
+	counterlen = sizeof(STRUCT_COUNTERS_INFO)
+			+ sizeof(STRUCT_COUNTERS) * new_number;
+
+	/* These are the old counters we will get from kernel */
+	repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+				* handle->info.num_entries);
+	if (!repl->counters) {
+		errno = ENOMEM;
+		goto out_free_repl;
+	}
+	/* These are the counters we're going to put back, later. */
+	newcounters = malloc(counterlen);
+	if (!newcounters) {
+		errno = ENOMEM;
+		goto out_free_repl_counters;
+	}
+	memset(newcounters, 0, counterlen);
+
+	strcpy(repl->name, handle->info.name);
+	repl->num_entries = new_number;
+	repl->size = new_size;
+
+	repl->num_counters = handle->info.num_entries;
+	repl->valid_hooks  = handle->info.valid_hooks;
+
+	DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
+		repl->num_entries, repl->size, repl->num_counters);
+
+	ret = iptcc_compile_table(handle, repl);
+	if (ret < 0) {
+		errno = ret;
+		goto out_free_newcounters;
+	}
+
+
+#ifdef IPTC_DEBUG2
+	{
+		int fd = open("/tmp/libiptc-so_set_replace.blob",
+				O_CREAT|O_WRONLY);
+		if (fd >= 0) {
+			write(fd, repl, sizeof(*repl) + repl->size);
+			close(fd);
+		}
+	}
+#endif
+
+	ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+			 sizeof(*repl) + repl->size);
+	if (ret < 0)
+		goto out_free_newcounters;
+
+	/* Put counters back. */
+	strcpy(newcounters->name, handle->info.name);
+	newcounters->num_counters = new_number;
+
+	list_for_each_entry(c, &handle->chains, list) {
+		struct rule_head *r;
+
+		/* Builtin chains have their own counters */
+		if (iptcc_is_builtin(c)) {
+			DEBUGP("counter for chain-index %u: ", c->foot_index);
+			switch(c->counter_map.maptype) {
+			case COUNTER_MAP_NOMAP:
+				counters_nomap(newcounters, c->foot_index);
+				break;
+			case COUNTER_MAP_NORMAL_MAP:
+				counters_normal_map(newcounters, repl,
+						    c->foot_index,
+						    c->counter_map.mappos);
+				break;
+			case COUNTER_MAP_ZEROED:
+				counters_map_zeroed(newcounters, repl,
+						    c->foot_index,
+						    c->counter_map.mappos,
+						    &c->counters);
+				break;
+			case COUNTER_MAP_SET:
+				counters_map_set(newcounters, c->foot_index,
+						 &c->counters);
+				break;
+			}
+		}
+
+		list_for_each_entry(r, &c->rules, list) {
+			DEBUGP("counter for index %u: ", r->index);
+			switch (r->counter_map.maptype) {
+			case COUNTER_MAP_NOMAP:
+				counters_nomap(newcounters, r->index);
+				break;
+
+			case COUNTER_MAP_NORMAL_MAP:
+				counters_normal_map(newcounters, repl,
+						    r->index,
+						    r->counter_map.mappos);
+				break;
+
+			case COUNTER_MAP_ZEROED:
+				counters_map_zeroed(newcounters, repl,
+						    r->index,
+						    r->counter_map.mappos,
+						    &r->entry->counters);
+				break;
+
+			case COUNTER_MAP_SET:
+				counters_map_set(newcounters, r->index,
+						 &r->entry->counters);
+				break;
+			}
+		}
+	}
+
+#ifdef IPTC_DEBUG2
+	{
+		int fd = open("/tmp/libiptc-so_set_add_counters.blob",
+				O_CREAT|O_WRONLY);
+		if (fd >= 0) {
+			write(fd, newcounters, counterlen);
+			close(fd);
+		}
+	}
+#endif
+
+	ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+			 newcounters, counterlen);
+	if (ret < 0)
+		goto out_free_newcounters;
+
+	free(repl->counters);
+	free(repl);
+	free(newcounters);
+
+finished:
+	return 1;
+
+out_free_newcounters:
+	free(newcounters);
+out_free_repl_counters:
+	free(repl->counters);
+out_free_repl:
+	free(repl);
+out_zero:
+	return 0;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+	unsigned int i;
+	struct table_struct {
+		void *fn;
+		int err;
+		const char *message;
+	} table [] =
+	  { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+	    { TC_INIT, EINVAL, "Module is wrong version" },
+	    { TC_INIT, ENOENT,
+		    "Table does not exist (do you need to insmod?)" },
+	    { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+	    { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+	    { TC_DELETE_CHAIN, EMLINK,
+	      "Can't delete chain with references left" },
+	    { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+	    { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+	    { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+	    { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+	    { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+	    { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+	    /* ENOENT for DELETE probably means no matching rule */
+	    { TC_DELETE_ENTRY, ENOENT,
+	      "Bad rule (does a matching rule exist in that chain?)" },
+	    { TC_SET_POLICY, ENOENT,
+	      "Bad built-in chain name" },
+	    { TC_SET_POLICY, EINVAL,
+	      "Bad policy name" },
+
+	    { NULL, 0, "Incompatible with this kernel" },
+	    { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+	    { NULL, ENOMEM, "Memory allocation problem" },
+	    { NULL, ENOENT, "No chain/target/match by that name" },
+	  };
+
+	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+		if ((!table[i].fn || table[i].fn == iptc_fn)
+		    && table[i].err == err)
+			return table[i].message;
+	}
+
+	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
new file mode 100644
index 0000000..0264bf0
--- /dev/null
+++ b/libiptc/libiptc.pc.in
@@ -0,0 +1,10 @@
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name:		libiptc
+Description:	iptables v4/v6 ruleset ADT and kernel interface
+Version:	@PACKAGE_VERSION@
+Requires:	libip4tc libip6tc
diff --git a/libiptc/linux_list.h b/libiptc/linux_list.h
new file mode 100644
index 0000000..abdcf88
--- /dev/null
+++ b/libiptc/linux_list.h
@@ -0,0 +1,723 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({	type __dummy; \
+	typeof(x) __dummy2; \
+	(void)(&__dummy == &__dummy2); \
+	1; \
+})
+
+#define prefetch(x)		1
+
+/* empty define to make this work in userspace -HW */
+#define smp_wmb()
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+		struct list_head * prev, struct list_head * next)
+{
+	new->next = next;
+	new->prev = prev;
+	smp_wmb();
+	next->prev = new;
+	prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+	__list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+					struct list_head *head)
+{
+	__list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry.  Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+        	pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ *			list_for_each_entry_continue
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue -	iterate over list of given type
+ *			continuing after existing point
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head);					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu	-	iterate over an rcu-protected list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu	-	iterate over an rcu-protected list safe
+ *					against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu	-	iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = list_entry(pos->member.next, typeof(*pos), member),	\
+		     ({ smp_read_barrier_depends(); 0;}),		\
+		     prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu	-	iterate over an rcu-protected list
+ *			continuing after existing point.
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+	for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+        	(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (n->pprev)  {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+					struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	n->pprev = &h->first;
+	smp_wmb();
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	next->next = n->next;
+	n->next = next;
+	next->pprev = &n->next;
+
+	if(next->next)
+		next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+	     pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry	- iterate over list of given type
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)			 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)		 \
+	for (pos = (pos)->next;						 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)			 \
+	for (; pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @n:		another &struct hlist_node to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) 		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ n = pos->next; 1; }) && 				 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos:	the type * to use as a loop counter.
+ * @pos:	the &struct hlist_node to use as a loop counter.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member)		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/libiptc/linux_stddef.h b/libiptc/linux_stddef.h
new file mode 100644
index 0000000..56416f1
--- /dev/null
+++ b/libiptc/linux_stddef.h
@@ -0,0 +1,39 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#undef NULL
+#if defined(__cplusplus)
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({	type __dummy; \
+	typeof(x) __dummy2; \
+	(void)(&__dummy == &__dummy2); \
+	1; \
+})
+
+
+#endif
diff --git a/libxtables/Makefile.am b/libxtables/Makefile.am
new file mode 100644
index 0000000..c5795fe
--- /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
+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/libxtables/xtables.c b/libxtables/xtables.c
new file mode 100644
index 0000000..fb60c01
--- /dev/null
+++ b/libxtables/xtables.c
@@ -0,0 +1,1961 @@
+/*
+ * (C) 2000-2006 by the netfilter coreteam <coreteam@netfilter.org>:
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	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>
+#include <inttypes.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#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 */
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <libiptc/libxtc.h>
+
+#ifndef NO_SHARED_LIBS
+#include <dlfcn.h>
+#endif
+#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
+#	define IPT_SO_GET_REVISION_MATCH	(IPT_BASE_CTL + 2)
+#	define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
+#endif
+#ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
+#	define IP6T_SO_GET_REVISION_MATCH	68
+#	define IP6T_SO_GET_REVISION_TARGET	69
+#endif
+#include <getopt.h>
+#include "iptables/internal.h"
+#include "xshared.h"
+
+#define NPROTO	255
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+/* we need this for ip6?tables-restore.  ip6?tables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  ip6?tables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
+
+struct xtables_globals *xt_params = NULL;
+
+void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
+{
+	va_list args;
+
+	va_start(args, msg);
+	fprintf(stderr, "%s v%s: ", xt_params->program_name, xt_params->program_version);
+	vfprintf(stderr, msg, args);
+	va_end(args);
+	fprintf(stderr, "\n");
+	exit(status);
+}
+
+void xtables_free_opts(int unused)
+{
+	if (xt_params->opts != xt_params->orig_opts) {
+		free(xt_params->opts);
+		xt_params->opts = NULL;
+	}
+}
+
+struct option *xtables_merge_options(struct option *orig_opts,
+				     struct option *oldopts,
+				     const struct option *newopts,
+				     unsigned int *option_offset)
+{
+	unsigned int num_oold = 0, num_old = 0, num_new = 0, i;
+	struct option *merge, *mp;
+
+	if (newopts == NULL)
+		return oldopts;
+
+	for (num_oold = 0; orig_opts[num_oold].name; num_oold++) ;
+	if (oldopts != NULL)
+		for (num_old = 0; oldopts[num_old].name; num_old++) ;
+	for (num_new = 0; newopts[num_new].name; num_new++) ;
+
+	/*
+	 * Since @oldopts also has @orig_opts already (and does so at the
+	 * start), skip these entries.
+	 */
+	oldopts += num_oold;
+	num_old -= num_oold;
+
+	merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
+	if (merge == NULL)
+		return NULL;
+
+	/* Let the base options -[ADI...] have precedence over everything */
+	memcpy(merge, orig_opts, sizeof(*mp) * num_oold);
+	mp = merge + num_oold;
+
+	/* Second, the new options */
+	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
+	*option_offset = xt_params->option_offset;
+	memcpy(mp, newopts, sizeof(*mp) * num_new);
+
+	for (i = 0; i < num_new; ++i, ++mp)
+		mp->val += *option_offset;
+
+	/* Third, the old options */
+	memcpy(mp, oldopts, sizeof(*mp) * num_old);
+	mp += num_old;
+	xtables_free_opts(0);
+
+	/* Clear trailing entry */
+	memset(mp, 0, sizeof(*mp));
+	return merge;
+}
+
+static const struct xtables_afinfo afinfo_ipv4 = {
+	.kmod          = "ip_tables",
+	.proc_exists   = "/proc/net/ip_tables_names",
+	.libprefix     = "libipt_",
+	.family	       = NFPROTO_IPV4,
+	.ipproto       = IPPROTO_IP,
+	.so_rev_match  = IPT_SO_GET_REVISION_MATCH,
+	.so_rev_target = IPT_SO_GET_REVISION_TARGET,
+};
+
+static const struct xtables_afinfo afinfo_ipv6 = {
+	.kmod          = "ip6_tables",
+	.proc_exists   = "/proc/net/ip6_tables_names",
+	.libprefix     = "libip6t_",
+	.family        = NFPROTO_IPV6,
+	.ipproto       = IPPROTO_IPV6,
+	.so_rev_match  = IP6T_SO_GET_REVISION_MATCH,
+	.so_rev_target = IP6T_SO_GET_REVISION_TARGET,
+};
+
+const struct xtables_afinfo *afinfo;
+
+/* Search path for Xtables .so files */
+static const char *xtables_libdir;
+
+/* the path to command to load kernel module */
+const char *xtables_modprobe_program;
+
+/* Keep track of matches/targets pending full registration: linked lists. */
+struct xtables_match *xtables_pending_matches;
+struct xtables_target *xtables_pending_targets;
+
+/* Keep track of fully registered external matches/targets: linked lists. */
+struct xtables_match *xtables_matches;
+struct xtables_target *xtables_targets;
+
+/* Fully register a match/target which was previously partially registered. */
+static void xtables_fully_register_pending_match(struct xtables_match *me);
+static void xtables_fully_register_pending_target(struct xtables_target *me);
+
+void xtables_init(void)
+{
+	xtables_libdir = getenv("XTABLES_LIBDIR");
+	if (xtables_libdir != NULL)
+		return;
+	xtables_libdir = getenv("IPTABLES_LIB_DIR");
+	if (xtables_libdir != NULL) {
+		fprintf(stderr, "IPTABLES_LIB_DIR is deprecated, "
+		        "use XTABLES_LIBDIR.\n");
+		return;
+	}
+	/*
+	 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
+	 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
+	 * for these env vars are deprecated anyhow, and in light of the
+	 * (shared) libxt_*.so files, makes less sense to have
+	 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
+	 */
+	xtables_libdir = getenv("IP6TABLES_LIB_DIR");
+	if (xtables_libdir != NULL) {
+		fprintf(stderr, "IP6TABLES_LIB_DIR is deprecated, "
+		        "use XTABLES_LIBDIR.\n");
+		return;
+	}
+	xtables_libdir = XTABLES_LIBDIR;
+}
+
+void xtables_set_nfproto(uint8_t nfproto)
+{
+	switch (nfproto) {
+	case NFPROTO_IPV4:
+		afinfo = &afinfo_ipv4;
+		break;
+	case NFPROTO_IPV6:
+		afinfo = &afinfo_ipv6;
+		break;
+	default:
+		fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
+		        __func__);
+	}
+}
+
+/**
+ * xtables_set_params - set the global parameters used by xtables
+ * @xtp:	input xtables_globals structure
+ *
+ * The app is expected to pass a valid xtables_globals data-filled
+ * with proper values
+ * @xtp cannot be NULL
+ *
+ * Returns -1 on failure to set and 0 on success
+ */
+int xtables_set_params(struct xtables_globals *xtp)
+{
+	if (!xtp) {
+		fprintf(stderr, "%s: Illegal global params\n",__func__);
+		return -1;
+	}
+
+	xt_params = xtp;
+
+	if (!xt_params->exit_err)
+		xt_params->exit_err = basic_exit_err;
+
+	return 0;
+}
+
+int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
+{
+	xtables_init();
+	xtables_set_nfproto(nfproto);
+	return xtables_set_params(xtp);
+}
+
+/**
+ * xtables_*alloc - wrappers that exit on failure
+ */
+void *xtables_calloc(size_t count, size_t size)
+{
+	void *p;
+
+	if ((p = calloc(count, size)) == NULL) {
+		perror("ip[6]tables: calloc failed");
+		exit(1);
+	}
+
+	return p;
+}
+
+void *xtables_malloc(size_t size)
+{
+	void *p;
+
+	if ((p = malloc(size)) == NULL) {
+		perror("ip[6]tables: malloc failed");
+		exit(1);
+	}
+
+	return p;
+}
+
+void *xtables_realloc(void *ptr, size_t size)
+{
+	void *p;
+
+	if ((p = realloc(ptr, size)) == NULL) {
+		perror("ip[6]tables: realloc failed");
+		exit(1);
+	}
+
+	return p;
+}
+
+static char *get_modprobe(void)
+{
+	int procfile;
+	char *ret;
+	int count;
+
+	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+	if (procfile < 0)
+		return NULL;
+	if (fcntl(procfile, F_SETFD, FD_CLOEXEC) == -1) {
+		fprintf(stderr, "Could not set close on exec: %s\n",
+			strerror(errno));
+		exit(1);
+	}
+
+	ret = malloc(PATH_MAX);
+	if (ret) {
+		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;
+		}
+	}
+	free(ret);
+	close(procfile);
+	return NULL;
+}
+
+int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
+{
+	char *buf = NULL;
+	char *argv[4];
+	int status;
+
+	/* If they don't explicitly set it, read out of kernel */
+	if (!modprobe) {
+		buf = get_modprobe();
+		if (!buf)
+			return -1;
+		modprobe = buf;
+	}
+
+	/*
+	 * Need to flush the buffer, or the child may output it again
+	 * when switching the program thru execv.
+	 */
+	fflush(stdout);
+
+	switch (vfork()) {
+	case 0:
+		argv[0] = (char *)modprobe;
+		argv[1] = (char *)modname;
+		if (quiet) {
+			argv[2] = "-q";
+			argv[3] = NULL;
+		} else {
+			argv[2] = NULL;
+			argv[3] = NULL;
+		}
+		execv(argv[0], argv);
+
+		/* not usually reached */
+		exit(1);
+	case -1:
+		free(buf);
+		return -1;
+
+	default: /* parent */
+		wait(&status);
+	}
+
+	free(buf);
+	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+		return 0;
+	return -1;
+}
+
+/* return true if a given file exists within procfs */
+static bool proc_file_exists(const char *filename)
+{
+	struct stat s;
+	struct statfs f;
+
+	if (lstat(filename, &s))
+		return false;
+	if (!S_ISREG(s.st_mode))
+		return false;
+	if (statfs(filename, &f))
+		return false;
+	if (f.f_type != PROC_SUPER_MAGIC)
+		return false;
+	return true;
+}
+
+int xtables_load_ko(const char *modprobe, bool quiet)
+{
+	static bool loaded = false;
+	int ret;
+
+	if (loaded)
+		return 0;
+
+	if (proc_file_exists(afinfo->proc_exists)) {
+		loaded = true;
+		return 0;
+	};
+
+	ret = xtables_insmod(afinfo->kmod, modprobe, quiet);
+	if (ret == 0)
+		loaded = true;
+
+	return ret;
+}
+
+/**
+ * xtables_strtou{i,l} - string to number conversion
+ * @s:	input string
+ * @end:	like strtoul's "end" pointer
+ * @value:	pointer for result
+ * @min:	minimum accepted value
+ * @max:	maximum accepted value
+ *
+ * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
+ * "15a" is rejected.
+ * In either case, the value obtained is compared for min-max compliance.
+ * Base is always 0, i.e. autodetect depending on @s.
+ *
+ * Returns true/false whether number was accepted. On failure, *value has
+ * undefined contents.
+ */
+bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
+                     uintmax_t min, uintmax_t max)
+{
+	uintmax_t v;
+	const char *p;
+	char *my_end;
+
+	errno = 0;
+	/* Since strtoul allows leading minus, we have to check for ourself. */
+	for (p = s; isspace(*p); ++p)
+		;
+	if (*p == '-')
+		return false;
+	v = strtoumax(s, &my_end, 0);
+	if (my_end == s)
+		return false;
+	if (end != NULL)
+		*end = my_end;
+
+	if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
+		if (value != NULL)
+			*value = v;
+		if (end == NULL)
+			return *my_end == '\0';
+		return true;
+	}
+
+	return false;
+}
+
+bool xtables_strtoui(const char *s, char **end, unsigned int *value,
+                     unsigned int min, unsigned int max)
+{
+	uintmax_t v;
+	bool ret;
+
+	ret = xtables_strtoul(s, end, &v, min, max);
+	if (value != NULL)
+		*value = v;
+	return ret;
+}
+
+int xtables_service_to_port(const char *name, const char *proto)
+{
+	struct servent *service;
+
+	if ((service = getservbyname(name, proto)) != NULL)
+		return ntohs((unsigned short) service->s_port);
+
+	return -1;
+}
+
+uint16_t xtables_parse_port(const char *port, const char *proto)
+{
+	unsigned int portnum;
+
+	if (xtables_strtoui(port, NULL, &portnum, 0, UINT16_MAX) ||
+	    (portnum = xtables_service_to_port(port, proto)) != (unsigned)-1)
+		return portnum;
+
+	xt_params->exit_err(PARAMETER_PROBLEM,
+		   "invalid port/service `%s' specified", port);
+}
+
+void xtables_parse_interface(const char *arg, char *vianame,
+			     unsigned char *mask)
+{
+	unsigned int vialen = strlen(arg);
+	unsigned int i;
+
+	memset(mask, 0, IFNAMSIZ);
+	memset(vianame, 0, IFNAMSIZ);
+
+	if (vialen + 1 > IFNAMSIZ)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			   "interface name `%s' must be shorter than IFNAMSIZ"
+			   " (%i)", arg, IFNAMSIZ-1);
+
+	strcpy(vianame, arg);
+	if (vialen == 0)
+		return;
+	else if (vianame[vialen - 1] == '+') {
+		memset(mask, 0xFF, vialen - 1);
+		/* Don't remove `+' here! -HW */
+	} else {
+		/* Include nul-terminator in match */
+		memset(mask, 0xFF, vialen + 1);
+		for (i = 0; vianame[i]; i++) {
+			if (vianame[i] == '/' ||
+			    vianame[i] == ' ') {
+				fprintf(stderr,
+					"Warning: weird character in interface"
+					" `%s' ('/' and ' ' are not allowed by the kernel).\n",
+					vianame);
+				break;
+			}
+		}
+	}
+}
+
+#ifndef NO_SHARED_LIBS
+static void *load_extension(const char *search_path, const char *af_prefix,
+    const char *name, bool is_target)
+{
+	const char *all_prefixes[] = {"libxt_", af_prefix, NULL};
+	const char **prefix;
+	const char *dir = search_path, *next;
+	void *ptr = NULL;
+	struct stat sb;
+	char path[256];
+
+	do {
+		next = strchr(dir, ':');
+		if (next == NULL)
+			next = dir + strlen(dir);
+
+		for (prefix = all_prefixes; *prefix != NULL; ++prefix) {
+			snprintf(path, sizeof(path), "%.*s/%s%s.so",
+			         (unsigned int)(next - dir), dir,
+			         *prefix, name);
+
+			if (stat(path, &sb) != 0) {
+				if (errno == ENOENT)
+					continue;
+				fprintf(stderr, "%s: %s\n", path,
+					strerror(errno));
+				return NULL;
+			}
+			if (dlopen(path, RTLD_NOW) == NULL) {
+				fprintf(stderr, "%s: %s\n", path, dlerror());
+				break;
+			}
+
+			if (is_target)
+				ptr = xtables_find_target(name, XTF_DONT_LOAD);
+			else
+				ptr = xtables_find_match(name,
+				      XTF_DONT_LOAD, NULL);
+
+			if (ptr != NULL)
+				return ptr;
+
+			errno = ENOENT;
+			return NULL;
+		}
+		dir = next + 1;
+	} while (*next != '\0');
+
+	return NULL;
+}
+#endif
+
+struct xtables_match *
+xtables_find_match(const char *name, enum xtables_tryload tryload,
+		   struct xtables_rule_match **matches)
+{
+	struct xtables_match **dptr;
+	struct xtables_match *ptr;
+	const char *icmp6 = "icmp6";
+
+	if (strlen(name) >= XT_EXTENSION_MAXNAMELEN)
+		xtables_error(PARAMETER_PROBLEM,
+			   "Invalid match name \"%s\" (%u chars max)",
+			   name, XT_EXTENSION_MAXNAMELEN - 1);
+
+	/* This is ugly as hell. Nonetheless, there is no way of changing
+	 * this without hurting backwards compatibility */
+	if ( (strcmp(name,"icmpv6") == 0) ||
+	     (strcmp(name,"ipv6-icmp") == 0) ||
+	     (strcmp(name,"icmp6") == 0) )
+		name = icmp6;
+
+	/* Trigger delayed initialization */
+	for (dptr = &xtables_pending_matches; *dptr; ) {
+		if (strcmp(name, (*dptr)->name) == 0) {
+			ptr = *dptr;
+			*dptr = (*dptr)->next;
+			ptr->next = NULL;
+			xtables_fully_register_pending_match(ptr);
+		} else {
+			dptr = &((*dptr)->next);
+		}
+	}
+
+	for (ptr = xtables_matches; ptr; ptr = ptr->next) {
+		if (strcmp(name, ptr->name) == 0) {
+			struct xtables_match *clone;
+
+			/* First match of this type: */
+			if (ptr->m == NULL)
+				break;
+
+			/* 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;
+
+			ptr = clone;
+			break;
+		}
+	}
+
+#ifndef NO_SHARED_LIBS
+	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
+		ptr = load_extension(xtables_libdir, afinfo->libprefix,
+		      name, false);
+
+		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				   "Couldn't load match `%s':%s\n",
+				   name, strerror(errno));
+	}
+#else
+	if (ptr && !ptr->loaded) {
+		if (tryload != XTF_DONT_LOAD)
+			ptr->loaded = 1;
+		else
+			ptr = NULL;
+	}
+	if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			   "Couldn't find match `%s'\n", name);
+	}
+#endif
+
+	if (ptr && matches) {
+		struct xtables_rule_match **i;
+		struct xtables_rule_match *newentry;
+
+		newentry = xtables_malloc(sizeof(struct xtables_rule_match));
+
+		for (i = matches; *i; i = &(*i)->next) {
+			if (strcmp(name, (*i)->match->name) == 0)
+				(*i)->completed = true;
+		}
+		newentry->match = ptr;
+		newentry->completed = false;
+		newentry->next = NULL;
+		*i = newentry;
+	}
+
+	return ptr;
+}
+
+struct xtables_target *
+xtables_find_target(const char *name, enum xtables_tryload tryload)
+{
+	struct xtables_target **dptr;
+	struct xtables_target *ptr;
+
+	/* Standard target? */
+	if (strcmp(name, "") == 0
+	    || strcmp(name, XTC_LABEL_ACCEPT) == 0
+	    || strcmp(name, XTC_LABEL_DROP) == 0
+	    || strcmp(name, XTC_LABEL_QUEUE) == 0
+	    || strcmp(name, XTC_LABEL_RETURN) == 0)
+		name = "standard";
+
+	/* Trigger delayed initialization */
+	for (dptr = &xtables_pending_targets; *dptr; ) {
+		if (strcmp(name, (*dptr)->name) == 0) {
+			ptr = *dptr;
+			*dptr = (*dptr)->next;
+			ptr->next = NULL;
+			xtables_fully_register_pending_target(ptr);
+		} else {
+			dptr = &((*dptr)->next);
+		}
+	}
+
+	for (ptr = xtables_targets; ptr; ptr = ptr->next) {
+		if (strcmp(name, ptr->name) == 0)
+			break;
+	}
+
+#ifndef NO_SHARED_LIBS
+	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
+		ptr = load_extension(xtables_libdir, afinfo->libprefix,
+		      name, true);
+
+		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				   "Couldn't load target `%s':%s\n",
+				   name, strerror(errno));
+	}
+#else
+	if (ptr && !ptr->loaded) {
+		if (tryload != XTF_DONT_LOAD)
+			ptr->loaded = 1;
+		else
+			ptr = NULL;
+	}
+	if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED) {
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			   "Couldn't find target `%s'\n", name);
+	}
+#endif
+
+	if (ptr)
+		ptr->used = 1;
+
+	return ptr;
+}
+
+static int compatible_revision(const char *name, uint8_t revision, int opt)
+{
+	struct xt_get_revision rev;
+	socklen_t s = sizeof(rev);
+	int max_rev, sockfd;
+
+	sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
+	if (sockfd < 0) {
+		if (errno == EPERM) {
+			/* revision 0 is always supported. */
+			if (revision != 0)
+				fprintf(stderr, "%s: Could not determine whether "
+						"revision %u is supported, "
+						"assuming it is.\n",
+					name, revision);
+			return 1;
+		}
+		fprintf(stderr, "Could not open socket to kernel: %s\n",
+			strerror(errno));
+		exit(1);
+	}
+
+	if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
+		fprintf(stderr, "Could not set close on exec: %s\n",
+			strerror(errno));
+		exit(1);
+	}
+
+	xtables_load_ko(xtables_modprobe_program, true);
+
+	strcpy(rev.name, name);
+	rev.revision = revision;
+
+	max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
+	if (max_rev < 0) {
+		/* Definitely don't support this? */
+		if (errno == ENOENT || errno == EPROTONOSUPPORT) {
+			close(sockfd);
+			return 0;
+		} else if (errno == ENOPROTOOPT) {
+			close(sockfd);
+			/* Assume only revision 0 support (old kernel) */
+			return (revision == 0);
+		} else {
+			fprintf(stderr, "getsockopt failed strangely: %s\n",
+				strerror(errno));
+			exit(1);
+		}
+	}
+	close(sockfd);
+	return 1;
+}
+
+
+static int compatible_match_revision(const char *name, uint8_t revision)
+{
+	return compatible_revision(name, revision, afinfo->so_rev_match);
+}
+
+static int compatible_target_revision(const char *name, uint8_t revision)
+{
+	return compatible_revision(name, revision, afinfo->so_rev_target);
+}
+
+static void xtables_check_options(const char *name, const struct option *opt)
+{
+	for (; opt->name != NULL; ++opt)
+		if (opt->val < 0 || opt->val >= XT_OPTION_OFFSET_SCALE) {
+			fprintf(stderr, "%s: Extension %s uses invalid "
+			        "option value %d\n",xt_params->program_name,
+			        name, opt->val);
+			exit(1);
+		}
+}
+
+void xtables_register_match(struct xtables_match *me)
+{
+	if (me->version == NULL) {
+		fprintf(stderr, "%s: match %s<%u> is missing a version\n",
+		        xt_params->program_name, me->name, me->revision);
+		exit(1);
+	}
+	if (strcmp(me->version, XTABLES_VERSION) != 0) {
+		fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
+		        "but \"%s\" is required.\n",
+			xt_params->program_name, me->name,
+			me->version, XTABLES_VERSION);
+		exit(1);
+	}
+
+	if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
+		fprintf(stderr, "%s: match `%s' has invalid name\n",
+			xt_params->program_name, me->name);
+		exit(1);
+	}
+
+	if (me->family >= NPROTO) {
+		fprintf(stderr,
+			"%s: BUG: match %s has invalid protocol family\n",
+			xt_params->program_name, me->name);
+		exit(1);
+	}
+
+	if (me->x6_options != NULL)
+		xtables_option_metavalidate(me->name, me->x6_options);
+	if (me->extra_opts != NULL)
+		xtables_check_options(me->name, me->extra_opts);
+
+	/* ignore not interested match */
+	if (me->family != afinfo->family && me->family != AF_UNSPEC)
+		return;
+
+	/* place on linked list of matches pending full registration */
+	me->next = xtables_pending_matches;
+	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) {
+		compare = xtables_match_prefer(old, me);
+		if (compare == 0) {
+			fprintf(stderr,
+				"%s: match `%s' already registered.\n",
+				xt_params->program_name, me->name);
+			exit(1);
+		}
+
+		/* Now we have two (or more) options, check compatibility. */
+		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. */
+		rn = (me->real_name != NULL) ? me->real_name : me->name;
+		if (!compatible_match_revision(rn, me->revision))
+			return;
+
+		/* Delete old one. */
+		for (i = &xtables_matches; *i!=old; i = &(*i)->next);
+		*i = old->next;
+	}
+
+	if (me->size != XT_ALIGN(me->size)) {
+		fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+		        xt_params->program_name, me->name,
+		        (unsigned int)me->size);
+		exit(1);
+	}
+
+	/* Append to list. */
+	for (i = &xtables_matches; *i; i = &(*i)->next);
+	me->next = NULL;
+	*i = me;
+
+	me->m = NULL;
+	me->mflags = 0;
+}
+
+void xtables_register_matches(struct xtables_match *match, unsigned int n)
+{
+	do {
+		xtables_register_match(&match[--n]);
+	} while (n > 0);
+}
+
+void xtables_register_target(struct xtables_target *me)
+{
+	if (me->version == NULL) {
+		fprintf(stderr, "%s: target %s<%u> is missing a version\n",
+		        xt_params->program_name, me->name, me->revision);
+		exit(1);
+	}
+	if (strcmp(me->version, XTABLES_VERSION) != 0) {
+		fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
+		        "but \"%s\" is required.\n",
+			xt_params->program_name, me->name,
+			me->version, XTABLES_VERSION);
+		exit(1);
+	}
+
+	if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
+		fprintf(stderr, "%s: target `%s' has invalid name\n",
+			xt_params->program_name, me->name);
+		exit(1);
+	}
+
+	if (me->family >= NPROTO) {
+		fprintf(stderr,
+			"%s: BUG: target %s has invalid protocol family\n",
+			xt_params->program_name, me->name);
+		exit(1);
+	}
+
+	if (me->x6_options != NULL)
+		xtables_option_metavalidate(me->name, me->x6_options);
+	if (me->extra_opts != NULL)
+		xtables_check_options(me->name, me->extra_opts);
+
+	/* ignore not interested target */
+	if (me->family != afinfo->family && me->family != AF_UNSPEC)
+		return;
+
+	/* place on linked list of targets pending full registration */
+	me->next = xtables_pending_targets;
+	xtables_pending_targets = me;
+}
+
+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;
+
+		compare = xtables_target_prefer(old, me);
+		if (compare == 0) {
+			fprintf(stderr,
+				"%s: target `%s' already registered.\n",
+				xt_params->program_name, me->name);
+			exit(1);
+		}
+
+		/* Now we have two (or more) options, check compatibility. */
+		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. */
+		rn = (me->real_name != NULL) ? me->real_name : me->name;
+		if (!compatible_target_revision(rn, me->revision))
+			return;
+
+		/* Delete old one. */
+		for (i = &xtables_targets; *i!=old; i = &(*i)->next);
+		*i = old->next;
+	}
+
+	if (me->size != XT_ALIGN(me->size)) {
+		fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+		        xt_params->program_name, me->name,
+		        (unsigned int)me->size);
+		exit(1);
+	}
+
+	/* Prepend to list. */
+	me->next = xtables_targets;
+	xtables_targets = me;
+	me->t = NULL;
+	me->tflags = 0;
+}
+
+void xtables_register_targets(struct xtables_target *target, unsigned int n)
+{
+	do {
+		xtables_register_target(&target[--n]);
+	} 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
+ *
+ * %XTF_ONLY_ONCE: print error message that option may only be used once.
+ * @p1:		module name (e.g. "mark")
+ * @p2(...):	option in conflict (e.g. "--mark")
+ * @p3(...):	condition to match on (see extensions/ for examples)
+ *
+ * %XTF_NO_INVERT: option does not support inversion
+ * @p1:		module name
+ * @p2:		option in conflict
+ * @p3:		condition to match on
+ *
+ * %XTF_BAD_VALUE: bad value for option
+ * @p1:		module name
+ * @p2:		option with which the problem occured (e.g. "--mark")
+ * @p3:		string the user passed in (e.g. "99999999999999")
+ *
+ * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
+ * @p1:		module name
+ *
+ * Displays an error message and exits the program.
+ */
+void xtables_param_act(unsigned int status, const char *p1, ...)
+{
+	const char *p2, *p3;
+	va_list args;
+	bool b;
+
+	va_start(args, p1);
+
+	switch (status) {
+	case XTF_ONLY_ONCE:
+		p2 = va_arg(args, const char *);
+		b  = va_arg(args, unsigned int);
+		if (!b) {
+			va_end(args);
+			return;
+		}
+		xt_params->exit_err(PARAMETER_PROBLEM,
+		           "%s: \"%s\" option may only be specified once",
+		           p1, p2);
+		break;
+	case XTF_NO_INVERT:
+		p2 = va_arg(args, const char *);
+		b  = va_arg(args, unsigned int);
+		if (!b) {
+			va_end(args);
+			return;
+		}
+		xt_params->exit_err(PARAMETER_PROBLEM,
+		           "%s: \"%s\" option cannot be inverted", p1, p2);
+		break;
+	case XTF_BAD_VALUE:
+		p2 = va_arg(args, const char *);
+		p3 = va_arg(args, const char *);
+		xt_params->exit_err(PARAMETER_PROBLEM,
+		           "%s: Bad value for \"%s\" option: \"%s\"",
+		           p1, p2, p3);
+		break;
+	case XTF_ONE_ACTION:
+		b = va_arg(args, unsigned int);
+		if (!b) {
+			va_end(args);
+			return;
+		}
+		xt_params->exit_err(PARAMETER_PROBLEM,
+		           "%s: At most one action is possible", p1);
+		break;
+	default:
+		xt_params->exit_err(status, p1, args);
+		break;
+	}
+
+	va_end(args);
+}
+
+const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
+{
+	static char buf[20];
+	const unsigned char *bytep = (const void *)&addrp->s_addr;
+
+	sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
+	return buf;
+}
+
+static const char *ipaddr_to_host(const struct in_addr *addr)
+{
+	struct hostent *host;
+
+	host = gethostbyaddr(addr, sizeof(struct in_addr), AF_INET);
+	if (host == NULL)
+		return NULL;
+
+	return host->h_name;
+}
+
+static const char *ipaddr_to_network(const struct in_addr *addr)
+{
+	struct netent *net;
+
+	if ((net = getnetbyaddr(ntohl(addr->s_addr), AF_INET)) != NULL)
+		return net->n_name;
+
+	return NULL;
+}
+
+const char *xtables_ipaddr_to_anyname(const struct in_addr *addr)
+{
+	const char *name;
+
+	if ((name = ipaddr_to_host(addr)) != NULL ||
+	    (name = ipaddr_to_network(addr)) != NULL)
+		return name;
+
+	return xtables_ipaddr_to_numeric(addr);
+}
+
+int xtables_ipmask_to_cidr(const struct in_addr *mask)
+{
+	uint32_t maskaddr, bits;
+	int i;
+
+	maskaddr = ntohl(mask->s_addr);
+	/* shortcut for /32 networks */
+	if (maskaddr == 0xFFFFFFFFL)
+		return 32;
+
+	i = 32;
+	bits = 0xFFFFFFFEL;
+	while (--i >= 0 && maskaddr != bits)
+		bits <<= 1;
+	if (i >= 0)
+		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 == (unsigned int)-1) {
+		/* 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;
+}
+
+static struct in_addr *__numeric_to_ipaddr(const char *dotted, bool is_mask)
+{
+	static struct in_addr addr;
+	unsigned char *addrp;
+	unsigned int onebyte;
+	char buf[20], *p, *q;
+	int i;
+
+	/* copy dotted string, because we need to modify it */
+	strncpy(buf, dotted, sizeof(buf) - 1);
+	buf[sizeof(buf) - 1] = '\0';
+	addrp = (void *)&addr.s_addr;
+
+	p = buf;
+	for (i = 0; i < 3; ++i) {
+		if ((q = strchr(p, '.')) == NULL) {
+			if (is_mask)
+				return NULL;
+
+			/* autocomplete, this is a network address */
+			if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
+				return NULL;
+
+			addrp[i] = onebyte;
+			while (i < 3)
+				addrp[++i] = 0;
+
+			return &addr;
+		}
+
+		*q = '\0';
+		if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
+			return NULL;
+
+		addrp[i] = onebyte;
+		p = q + 1;
+	}
+
+	/* we have checked 3 bytes, now we check the last one */
+	if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
+		return NULL;
+
+	addrp[3] = onebyte;
+	return &addr;
+}
+
+struct in_addr *xtables_numeric_to_ipaddr(const char *dotted)
+{
+	return __numeric_to_ipaddr(dotted, false);
+}
+
+struct in_addr *xtables_numeric_to_ipmask(const char *dotted)
+{
+	return __numeric_to_ipaddr(dotted, true);
+}
+
+static struct in_addr *network_to_ipaddr(const char *name)
+{
+	static struct in_addr addr;
+	struct netent *net;
+
+	if ((net = getnetbyname(name)) != NULL) {
+		if (net->n_addrtype != AF_INET)
+			return NULL;
+		addr.s_addr = htonl(net->n_net);
+		return &addr;
+	}
+
+	return NULL;
+}
+
+static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
+{
+	struct hostent *host;
+	struct in_addr *addr;
+	unsigned int i;
+
+	*naddr = 0;
+	if ((host = gethostbyname(name)) != NULL) {
+		if (host->h_addrtype != AF_INET ||
+		    host->h_length != sizeof(struct in_addr))
+			return NULL;
+
+		while (host->h_addr_list[*naddr] != NULL)
+			++*naddr;
+		addr = xtables_calloc(*naddr, sizeof(struct in_addr));
+		for (i = 0; i < *naddr; i++)
+			memcpy(&addr[i], host->h_addr_list[i],
+			       sizeof(struct in_addr));
+		return addr;
+	}
+
+	return NULL;
+}
+
+static struct in_addr *
+ipparse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+	struct in_addr *addrptmp, *addrp;
+
+	if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
+	    (addrptmp = network_to_ipaddr(name)) != NULL) {
+		addrp = xtables_malloc(sizeof(struct in_addr));
+		memcpy(addrp, addrptmp, sizeof(*addrp));
+		*naddrs = 1;
+		return addrp;
+	}
+	if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
+		return addrptmp;
+
+	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *parse_ipmask(const char *mask)
+{
+	static struct in_addr maskaddr;
+	struct in_addr *addrp;
+	unsigned int bits;
+
+	if (mask == NULL) {
+		/* no mask at all defaults to 32 bits */
+		maskaddr.s_addr = 0xFFFFFFFF;
+		return &maskaddr;
+	}
+	if ((addrp = xtables_numeric_to_ipmask(mask)) != NULL)
+		/* dotted_to_addr already returns a network byte order addr */
+		return addrp;
+	if (!xtables_strtoui(mask, NULL, &bits, 0, 32))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			   "invalid mask `%s' specified", mask);
+	if (bits != 0) {
+		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+		return &maskaddr;
+	}
+
+	maskaddr.s_addr = 0U;
+	return &maskaddr;
+}
+
+void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp,
+                              struct in_addr **maskpp, unsigned int *naddrs)
+{
+	struct in_addr *addrp;
+	char buf[256], *p, *next;
+	unsigned int len, i, j, n, count = 1;
+	const char *loop = name;
+
+	while ((loop = strchr(loop, ',')) != NULL) {
+		++count;
+		++loop; /* skip ',' */
+	}
+
+	*addrpp = xtables_malloc(sizeof(struct in_addr) * count);
+	*maskpp = xtables_malloc(sizeof(struct in_addr) * count);
+
+	loop = name;
+
+	for (i = 0; i < count; ++i) {
+		while (isspace(*loop))
+			++loop;
+		next = strchr(loop, ',');
+		if (next != NULL)
+			len = next - loop;
+		else
+			len = strlen(loop);
+		if (len > sizeof(buf) - 1)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Hostname too long");
+
+		strncpy(buf, loop, len);
+		buf[len] = '\0';
+		if ((p = strrchr(buf, '/')) != NULL) {
+			*p = '\0';
+			addrp = parse_ipmask(p + 1);
+		} else {
+			addrp = parse_ipmask(NULL);
+		}
+		memcpy(*maskpp + i, addrp, sizeof(*addrp));
+
+		/* if a null mask is given, the name is ignored, like in "any/0" */
+		if ((*maskpp + i)->s_addr == 0)
+			/*
+			 * A bit pointless to process multiple addresses
+			 * in this case...
+			 */
+			strcpy(buf, "0.0.0.0");
+
+		addrp = ipparse_hostnetwork(buf, &n);
+		if (n > 1) {
+			count += n - 1;
+			*addrpp = xtables_realloc(*addrpp,
+			          sizeof(struct in_addr) * count);
+			*maskpp = xtables_realloc(*maskpp,
+			          sizeof(struct in_addr) * count);
+			for (j = 0; j < n; ++j)
+				/* for each new addr */
+				memcpy(*addrpp + i + j, addrp + j,
+				       sizeof(*addrp));
+			for (j = 1; j < n; ++j)
+				/* for each new mask */
+				memcpy(*maskpp + i + j, *maskpp + i,
+				       sizeof(*addrp));
+			i += n - 1;
+		} else {
+			memcpy(*addrpp + i, addrp, sizeof(*addrp));
+		}
+		/* free what ipparse_hostnetwork had allocated: */
+		free(addrp);
+		if (next == NULL)
+			break;
+		loop = next + 1;
+	}
+	*naddrs = count;
+	for (i = 0; i < count; ++i)
+		(*addrpp+i)->s_addr &= (*maskpp+i)->s_addr;
+}
+
+
+/**
+ * xtables_ipparse_any - transform arbitrary name to in_addr
+ *
+ * Possible inputs (pseudo regex):
+ * 	m{^($hostname|$networkname|$ipaddr)(/$mask)?}
+ * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
+ */
+void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
+                         struct in_addr *maskp, unsigned int *naddrs)
+{
+	unsigned int i, j, k, n;
+	struct in_addr *addrp;
+	char buf[256], *p;
+
+	strncpy(buf, name, sizeof(buf) - 1);
+	buf[sizeof(buf) - 1] = '\0';
+	if ((p = strrchr(buf, '/')) != NULL) {
+		*p = '\0';
+		addrp = parse_ipmask(p + 1);
+	} else {
+		addrp = parse_ipmask(NULL);
+	}
+	memcpy(maskp, addrp, sizeof(*maskp));
+
+	/* if a null mask is given, the name is ignored, like in "any/0" */
+	if (maskp->s_addr == 0U)
+		strcpy(buf, "0.0.0.0");
+
+	addrp = *addrpp = ipparse_hostnetwork(buf, naddrs);
+	n = *naddrs;
+	for (i = 0, j = 0; i < n; ++i) {
+		addrp[j++].s_addr &= maskp->s_addr;
+		for (k = 0; k < j - 1; ++k)
+			if (addrp[k].s_addr == addrp[j-1].s_addr) {
+				/*
+				 * Nuke the dup by copying an address from the
+				 * tail here, and check the current position
+				 * again (--j).
+				 */
+				memcpy(&addrp[--j], &addrp[--*naddrs],
+				       sizeof(struct in_addr));
+				break;
+			}
+	}
+}
+
+const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
+{
+	/* 0000:0000:0000:0000:0000:0000:000.000.000.000
+	 * 0000:0000:0000:0000:0000:0000:0000:0000 */
+	static char buf[50+1];
+	return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static const char *ip6addr_to_host(const struct in6_addr *addr)
+{
+	static char hostname[NI_MAXHOST];
+	struct sockaddr_in6 saddr;
+	int err;
+
+	memset(&saddr, 0, sizeof(struct sockaddr_in6));
+	memcpy(&saddr.sin6_addr, addr, sizeof(*addr));
+	saddr.sin6_family = AF_INET6;
+
+	err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
+	      hostname, sizeof(hostname) - 1, NULL, 0, 0);
+	if (err != 0) {
+#ifdef DEBUG
+		fprintf(stderr,"IP2Name: %s\n",gai_strerror(err));
+#endif
+		return NULL;
+	}
+
+#ifdef DEBUG
+	fprintf (stderr, "\naddr2host: %s\n", hostname);
+#endif
+	return hostname;
+}
+
+const char *xtables_ip6addr_to_anyname(const struct in6_addr *addr)
+{
+	const char *name;
+
+	if ((name = ip6addr_to_host(addr)) != NULL)
+		return name;
+
+	return xtables_ip6addr_to_numeric(addr);
+}
+
+int xtables_ip6mask_to_cidr(const struct in6_addr *k)
+{
+	unsigned int bits = 0;
+	uint32_t a, b, c, d;
+
+	a = ntohl(k->s6_addr32[0]);
+	b = ntohl(k->s6_addr32[1]);
+	c = ntohl(k->s6_addr32[2]);
+	d = ntohl(k->s6_addr32[3]);
+	while (a & 0x80000000U) {
+		++bits;
+		a <<= 1;
+		a  |= (b >> 31) & 1;
+		b <<= 1;
+		b  |= (c >> 31) & 1;
+		c <<= 1;
+		c  |= (d >> 31) & 1;
+		d <<= 1;
+	}
+	if (a != 0 || b != 0 || c != 0 || d != 0)
+		return -1;
+	return bits;
+}
+
+const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
+{
+	static char buf[50+2];
+	int l = xtables_ip6mask_to_cidr(addrp);
+
+	if (l == -1) {
+		strcpy(buf, "/");
+		strcat(buf, xtables_ip6addr_to_numeric(addrp));
+		return buf;
+	}
+	/* we don't want to see "/128" */
+	if (l == 128)
+		return "";
+	else
+		sprintf(buf, "/%d", l);
+	return buf;
+}
+
+struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
+{
+	static struct in6_addr ap;
+	int err;
+
+	if ((err = inet_pton(AF_INET6, num, &ap)) == 1)
+		return &ap;
+#ifdef DEBUG
+	fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+	return NULL;
+}
+
+static struct in6_addr *
+host_to_ip6addr(const char *name, unsigned int *naddr)
+{
+	struct in6_addr *addr;
+	struct addrinfo hints;
+	struct addrinfo *res, *p;
+	int err;
+	unsigned int i;
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_flags    = AI_CANONNAME;
+	hints.ai_family   = AF_INET6;
+	hints.ai_socktype = SOCK_RAW;
+
+	*naddr = 0;
+	if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
+#ifdef DEBUG
+		fprintf(stderr,"Name2IP: %s\n",gai_strerror(err));
+#endif
+		return NULL;
+	} else {
+		/* Find length of address chain */
+		for (p = res; p != NULL; p = p->ai_next)
+			++*naddr;
+#ifdef DEBUG
+		fprintf(stderr, "resolved: len=%d  %s ", res->ai_addrlen,
+		        xtables_ip6addr_to_numeric(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr));
+#endif
+		/* Copy each element of the address chain */
+		addr = xtables_calloc(*naddr, sizeof(struct in6_addr));
+		for (i = 0, p = res; p != NULL; p = p->ai_next)
+			memcpy(&addr[i++],
+			       &((const struct sockaddr_in6 *)p->ai_addr)->sin6_addr,
+			       sizeof(struct in6_addr));
+		freeaddrinfo(res);
+		return addr;
+	}
+
+	return NULL;
+}
+
+static struct in6_addr *network_to_ip6addr(const char *name)
+{
+	/*	abort();*/
+	/* TODO: not implemented yet, but the exception breaks the
+	 *       name resolvation */
+	return NULL;
+}
+
+static struct in6_addr *
+ip6parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+	struct in6_addr *addrp, *addrptmp;
+
+	if ((addrptmp = xtables_numeric_to_ip6addr(name)) != NULL ||
+	    (addrptmp = network_to_ip6addr(name)) != NULL) {
+		addrp = xtables_malloc(sizeof(struct in6_addr));
+		memcpy(addrp, addrptmp, sizeof(*addrp));
+		*naddrs = 1;
+		return addrp;
+	}
+	if ((addrp = host_to_ip6addr(name, naddrs)) != NULL)
+		return addrp;
+
+	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in6_addr *parse_ip6mask(char *mask)
+{
+	static struct in6_addr maskaddr;
+	struct in6_addr *addrp;
+	unsigned int bits;
+
+	if (mask == NULL) {
+		/* no mask at all defaults to 128 bits */
+		memset(&maskaddr, 0xff, sizeof maskaddr);
+		return &maskaddr;
+	}
+	if ((addrp = xtables_numeric_to_ip6addr(mask)) != NULL)
+		return addrp;
+	if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			   "invalid mask `%s' specified", mask);
+	if (bits != 0) {
+		char *p = (void *)&maskaddr;
+		memset(p, 0xff, bits / 8);
+		memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+		p[bits/8] = 0xff << (8 - (bits & 7));
+		return &maskaddr;
+	}
+
+	memset(&maskaddr, 0, sizeof(maskaddr));
+	return &maskaddr;
+}
+
+void
+xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp,
+		      struct in6_addr **maskpp, unsigned int *naddrs)
+{
+	static const struct in6_addr zero_addr;
+	struct in6_addr *addrp;
+	char buf[256], *p, *next;
+	unsigned int len, i, j, n, count = 1;
+	const char *loop = name;
+
+	while ((loop = strchr(loop, ',')) != NULL) {
+		++count;
+		++loop; /* skip ',' */
+	}
+
+	*addrpp = xtables_malloc(sizeof(struct in6_addr) * count);
+	*maskpp = xtables_malloc(sizeof(struct in6_addr) * count);
+
+	loop = name;
+
+	for (i = 0; i < count /*NB: count can grow*/; ++i) {
+		while (isspace(*loop))
+			++loop;
+		next = strchr(loop, ',');
+		if (next != NULL)
+			len = next - loop;
+		else
+			len = strlen(loop);
+		if (len > sizeof(buf) - 1)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Hostname too long");
+
+		strncpy(buf, loop, len);
+		buf[len] = '\0';
+		if ((p = strrchr(buf, '/')) != NULL) {
+			*p = '\0';
+			addrp = parse_ip6mask(p + 1);
+		} else {
+			addrp = parse_ip6mask(NULL);
+		}
+		memcpy(*maskpp + i, addrp, sizeof(*addrp));
+
+		/* if a null mask is given, the name is ignored, like in "any/0" */
+		if (memcmp(*maskpp + i, &zero_addr, sizeof(zero_addr)) == 0)
+			strcpy(buf, "::");
+
+		addrp = ip6parse_hostnetwork(buf, &n);
+		if (n > 1) {
+			count += n - 1;
+			*addrpp = xtables_realloc(*addrpp,
+			          sizeof(struct in6_addr) * count);
+			*maskpp = xtables_realloc(*maskpp,
+			          sizeof(struct in6_addr) * count);
+			for (j = 0; j < n; ++j)
+				/* for each new addr */
+				memcpy(*addrpp + i + j, addrp + j,
+				       sizeof(*addrp));
+			for (j = 1; j < n; ++j)
+				/* for each new mask */
+				memcpy(*maskpp + i + j, *maskpp + i,
+				       sizeof(*addrp));
+			i += n - 1;
+		} else {
+			memcpy(*addrpp + i, addrp, sizeof(*addrp));
+		}
+		/* free what ip6parse_hostnetwork had allocated: */
+		free(addrp);
+		if (next == NULL)
+			break;
+		loop = next + 1;
+	}
+	*naddrs = count;
+	for (i = 0; i < count; ++i)
+		for (j = 0; j < 4; ++j)
+			(*addrpp+i)->s6_addr32[j] &= (*maskpp+i)->s6_addr32[j];
+}
+
+void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp,
+                          struct in6_addr *maskp, unsigned int *naddrs)
+{
+	static const struct in6_addr zero_addr;
+	struct in6_addr *addrp;
+	unsigned int i, j, k, n;
+	char buf[256], *p;
+
+	strncpy(buf, name, sizeof(buf) - 1);
+	buf[sizeof(buf)-1] = '\0';
+	if ((p = strrchr(buf, '/')) != NULL) {
+		*p = '\0';
+		addrp = parse_ip6mask(p + 1);
+	} else {
+		addrp = parse_ip6mask(NULL);
+	}
+	memcpy(maskp, addrp, sizeof(*maskp));
+
+	/* if a null mask is given, the name is ignored, like in "any/0" */
+	if (memcmp(maskp, &zero_addr, sizeof(zero_addr)) == 0)
+		strcpy(buf, "::");
+
+	addrp = *addrpp = ip6parse_hostnetwork(buf, naddrs);
+	n = *naddrs;
+	for (i = 0, j = 0; i < n; ++i) {
+		for (k = 0; k < 4; ++k)
+			addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
+		++j;
+		for (k = 0; k < j - 1; ++k)
+			if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
+				/*
+				 * Nuke the dup by copying an address from the
+				 * tail here, and check the current position
+				 * again (--j).
+				 */
+				memcpy(&addrp[--j], &addrp[--*naddrs],
+				       sizeof(struct in_addr));
+				break;
+			}
+	}
+}
+
+void xtables_save_string(const char *value)
+{
+	static const char no_quote_chars[] = "_-0123456789"
+		"abcdefghijklmnopqrstuvwxyz"
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	static const char escape_chars[] = "\"\\'";
+	size_t length;
+	const char *p;
+
+	length = strspn(value, no_quote_chars);
+	if (length > 0 && value[length] == 0) {
+		/* no quoting required */
+		putchar(' ');
+		fputs(value, stdout);
+	} else {
+		/* there is at least one dangerous character in the
+		   value, which we have to quote.  Write double quotes
+		   around the value and escape special characters with
+		   a backslash */
+		printf(" \"");
+
+		for (p = strpbrk(value, escape_chars); p != NULL;
+		     p = strpbrk(value, escape_chars)) {
+			if (p > value)
+				fwrite(value, 1, p - value, stdout);
+			putchar('\\');
+			putchar(*p);
+			value = p + 1;
+		}
+
+		/* print the rest and finish the double quoted
+		   string */
+		fputs(value, stdout);
+		putchar('\"');
+	}
+}
+
+const struct xtables_pprot xtables_chain_protos[] = {
+	{"tcp",       IPPROTO_TCP},
+	{"sctp",      IPPROTO_SCTP},
+	{"udp",       IPPROTO_UDP},
+	{"udplite",   IPPROTO_UDPLITE},
+	{"icmp",      IPPROTO_ICMP},
+	{"icmpv6",    IPPROTO_ICMPV6},
+	{"ipv6-icmp", IPPROTO_ICMPV6},
+	{"esp",       IPPROTO_ESP},
+	{"ah",        IPPROTO_AH},
+	{"ipv6-mh",   IPPROTO_MH},
+	{"mh",        IPPROTO_MH},
+	{"all",       0},
+	{NULL},
+};
+
+uint16_t
+xtables_parse_protocol(const char *s)
+{
+	const struct protoent *pent;
+	unsigned int proto, i;
+
+	if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX))
+		return proto;
+
+	/* first deal with the special case of 'all' to prevent
+	 * people from being able to redefine 'all' in nsswitch
+	 * and/or provoke expensive [not working] ldap/nis/...
+	 * lookups */
+	if (strcmp(s, "all") == 0)
+		return 0;
+
+	pent = getprotobyname(s);
+	if (pent != NULL)
+		return pent->p_proto;
+
+	for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) {
+		if (xtables_chain_protos[i].name == NULL)
+			continue;
+		if (strcmp(s, xtables_chain_protos[i].name) == 0)
+			return xtables_chain_protos[i].num;
+	}
+	xt_params->exit_err(PARAMETER_PROBLEM,
+		"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/libxtables/xtoptions.c b/libxtables/xtoptions.c
new file mode 100644
index 0000000..78e9abd
--- /dev/null
+++ b/libxtables/xtoptions.c
@@ -0,0 +1,1204 @@
+/*
+ *	Argument parser
+ *	Copyright © Jan Engelhardt, 2011
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation; either version 2 of
+ *	the License, or (at your option) any later version.
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include "xtables.h"
+#include "xshared.h"
+#ifndef IPTOS_NORMALSVC
+#	define IPTOS_NORMALSVC 0
+#endif
+
+#define XTOPT_MKPTR(cb) \
+	((void *)((char *)(cb)->data + (cb)->entry->ptroff))
+
+/**
+ * Simple key-value pairs for syslog levels
+ */
+struct syslog_level {
+	char name[8];
+	uint8_t level;
+};
+
+struct tos_value_mask {
+	uint8_t value, mask;
+};
+
+static const size_t xtopt_psize[] = {
+	/*
+	 * All types not listed here, and thus essentially being initialized to
+	 * zero have zero on purpose.
+	 */
+	[XTTYPE_UINT8]       = sizeof(uint8_t),
+	[XTTYPE_UINT16]      = sizeof(uint16_t),
+	[XTTYPE_UINT32]      = sizeof(uint32_t),
+	[XTTYPE_UINT64]      = sizeof(uint64_t),
+	[XTTYPE_UINT8RC]     = sizeof(uint8_t[2]),
+	[XTTYPE_UINT16RC]    = sizeof(uint16_t[2]),
+	[XTTYPE_UINT32RC]    = sizeof(uint32_t[2]),
+	[XTTYPE_UINT64RC]    = sizeof(uint64_t[2]),
+	[XTTYPE_DOUBLE]      = sizeof(double),
+	[XTTYPE_STRING]      = -1,
+	[XTTYPE_SYSLOGLEVEL] = sizeof(uint8_t),
+	[XTTYPE_HOST]        = sizeof(union nf_inet_addr),
+	[XTTYPE_HOSTMASK]    = sizeof(union nf_inet_addr),
+	[XTTYPE_PROTOCOL]    = sizeof(uint8_t),
+	[XTTYPE_PORT]        = sizeof(uint16_t),
+	[XTTYPE_PORTRC]      = sizeof(uint16_t[2]),
+	[XTTYPE_PLENMASK]    = sizeof(union nf_inet_addr),
+	[XTTYPE_ETHERMAC]    = sizeof(uint8_t[6]),
+};
+
+/**
+ * Creates getopt options from the x6-style option map, and assigns each a
+ * getopt id.
+ */
+struct option *
+xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
+		     const struct xt_option_entry *entry, unsigned int *offset)
+{
+	unsigned int num_orig, num_old = 0, num_new, i;
+	struct option *merge, *mp;
+
+	if (entry == NULL)
+		return oldopts;
+	for (num_orig = 0; orig_opts[num_orig].name != NULL; ++num_orig)
+		;
+	if (oldopts != NULL)
+		for (num_old = 0; oldopts[num_old].name != NULL; ++num_old)
+			;
+	for (num_new = 0; entry[num_new].name != NULL; ++num_new)
+		;
+
+	/*
+	 * Since @oldopts also has @orig_opts already (and does so at the
+	 * start), skip these entries.
+	 */
+	oldopts += num_orig;
+	num_old -= num_orig;
+
+	merge = malloc(sizeof(*mp) * (num_orig + num_old + num_new + 1));
+	if (merge == NULL)
+		return NULL;
+
+	/* Let the base options -[ADI...] have precedence over everything */
+	memcpy(merge, orig_opts, sizeof(*mp) * num_orig);
+	mp = merge + num_orig;
+
+	/* Second, the new options */
+	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
+	*offset = xt_params->option_offset;
+
+	for (i = 0; i < num_new; ++i, ++mp, ++entry) {
+		mp->name         = entry->name;
+		mp->has_arg      = entry->type != XTTYPE_NONE;
+		mp->flag         = NULL;
+		mp->val          = entry->id + *offset;
+	}
+
+	/* Third, the old options */
+	memcpy(mp, oldopts, sizeof(*mp) * num_old);
+	mp += num_old;
+	xtables_free_opts(0);
+
+	/* Clear trailing entry */
+	memset(mp, 0, sizeof(*mp));
+	return merge;
+}
+
+/**
+ * Give the upper limit for a certain type.
+ */
+static uintmax_t xtopt_max_by_type(enum xt_option_type type)
+{
+	switch (type) {
+	case XTTYPE_UINT8:
+	case XTTYPE_UINT8RC:
+		return UINT8_MAX;
+	case XTTYPE_UINT16:
+	case XTTYPE_UINT16RC:
+		return UINT16_MAX;
+	case XTTYPE_UINT32:
+	case XTTYPE_UINT32RC:
+		return UINT32_MAX;
+	case XTTYPE_UINT64:
+	case XTTYPE_UINT64RC:
+		return UINT64_MAX;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * Return the size of a single entity based upon a type - predominantly an
+ * XTTYPE_UINT*RC type.
+ */
+static size_t xtopt_esize_by_type(enum xt_option_type type)
+{
+	switch (type) {
+	case XTTYPE_UINT8RC:
+		return xtopt_psize[XTTYPE_UINT8];
+	case XTTYPE_UINT16RC:
+		return xtopt_psize[XTTYPE_UINT16];
+	case XTTYPE_UINT32RC:
+		return xtopt_psize[XTTYPE_UINT32];
+	case XTTYPE_UINT64RC:
+		return xtopt_psize[XTTYPE_UINT64];
+	default:
+		return xtopt_psize[type];
+	}
+}
+
+/**
+ * Require a simple integer.
+ */
+static void xtopt_parse_int(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	uintmax_t lmin = 0, lmax = xtopt_max_by_type(entry->type);
+	uintmax_t value;
+
+	if (cb->entry->min != 0)
+		lmin = cb->entry->min;
+	if (cb->entry->max != 0)
+		lmax = cb->entry->max;
+
+	if (!xtables_strtoul(cb->arg, NULL, &value, lmin, lmax))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: bad value for option \"--%s\", "
+			"or out of range (%ju-%ju).\n",
+			cb->ext_name, entry->name, lmin, lmax);
+
+	if (entry->type == XTTYPE_UINT8) {
+		cb->val.u8 = value;
+		if (entry->flags & XTOPT_PUT)
+			*(uint8_t *)XTOPT_MKPTR(cb) = cb->val.u8;
+	} else if (entry->type == XTTYPE_UINT16) {
+		cb->val.u16 = value;
+		if (entry->flags & XTOPT_PUT)
+			*(uint16_t *)XTOPT_MKPTR(cb) = cb->val.u16;
+	} else if (entry->type == XTTYPE_UINT32) {
+		cb->val.u32 = value;
+		if (entry->flags & XTOPT_PUT)
+			*(uint32_t *)XTOPT_MKPTR(cb) = cb->val.u32;
+	} else if (entry->type == XTTYPE_UINT64) {
+		cb->val.u64 = value;
+		if (entry->flags & XTOPT_PUT)
+			*(uint64_t *)XTOPT_MKPTR(cb) = cb->val.u64;
+	}
+}
+
+/**
+ * Require a simple floating point number.
+ */
+static void xtopt_parse_float(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	double value;
+	char *end;
+
+	value = strtod(cb->arg, &end);
+	if (end == cb->arg || *end != '\0' ||
+	    (entry->min != entry->max &&
+	    (value < entry->min || value > entry->max)))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: bad value for option \"--%s\", "
+			"or out of range (%u-%u).\n",
+			cb->ext_name, entry->name, entry->min, entry->max);
+
+	cb->val.dbl = value;
+	if (entry->flags & XTOPT_PUT)
+		*(double *)XTOPT_MKPTR(cb) = cb->val.dbl;
+}
+
+/**
+ * Copy the parsed value to the appropriate entry in cb->val.
+ */
+static void xtopt_mint_value_to_cb(struct xt_option_call *cb, uintmax_t value)
+{
+	const struct xt_option_entry *entry = cb->entry;
+
+	if (cb->nvals >= ARRAY_SIZE(cb->val.u32_range))
+		return;
+	if (entry->type == XTTYPE_UINT8RC)
+		cb->val.u8_range[cb->nvals] = value;
+	else if (entry->type == XTTYPE_UINT16RC)
+		cb->val.u16_range[cb->nvals] = value;
+	else if (entry->type == XTTYPE_UINT32RC)
+		cb->val.u32_range[cb->nvals] = value;
+	else if (entry->type == XTTYPE_UINT64RC)
+		cb->val.u64_range[cb->nvals] = value;
+}
+
+/**
+ * Copy the parsed value to the data area, using appropriate type access.
+ */
+static void xtopt_mint_value_to_ptr(struct xt_option_call *cb, void **datap,
+				    uintmax_t value)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	void *data = *datap;
+
+	if (!(entry->flags & XTOPT_PUT))
+		return;
+	if (entry->type == XTTYPE_UINT8RC)
+		*(uint8_t *)data = value;
+	else if (entry->type == XTTYPE_UINT16RC)
+		*(uint16_t *)data = value;
+	else if (entry->type == XTTYPE_UINT32RC)
+		*(uint32_t *)data = value;
+	else if (entry->type == XTTYPE_UINT64RC)
+		*(uint64_t *)data = value;
+	data += xtopt_esize_by_type(entry->type);
+	*datap = data;
+}
+
+/**
+ * Multiple integer parse routine.
+ *
+ * This function is capable of parsing any number of fields. Only the first
+ * two values from the string will be put into @cb however (and as such,
+ * @cb->val.uXX_range is just that large) to cater for the few extensions that
+ * do not have a range[2] field, but {min, max}, and which cannot use
+ * XTOPT_POINTER.
+ */
+static void xtopt_parse_mint(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	const char *arg = cb->arg;
+	size_t esize = xtopt_esize_by_type(entry->type);
+	const uintmax_t lmax = xtopt_max_by_type(entry->type);
+	void *put = XTOPT_MKPTR(cb);
+	unsigned int maxiter;
+	uintmax_t value;
+	char *end = "";
+	char sep = ':';
+
+	maxiter = entry->size / esize;
+	if (maxiter == 0)
+		maxiter = ARRAY_SIZE(cb->val.u32_range);
+	if (entry->size % esize != 0)
+		xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
+			"not have proper size\n", __func__);
+
+	cb->nvals = 0;
+	for (arg = cb->arg, end = (char *)arg; ; arg = end + 1) {
+		if (cb->nvals == maxiter)
+			xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
+				"components for option \"--%s\" (max: %u)\n",
+				cb->ext_name, entry->name, maxiter);
+		if (*arg == '\0' || *arg == sep) {
+			/* Default range components when field not spec'd. */
+			end = (char *)arg;
+			value = (cb->nvals == 1) ? lmax : 0;
+		} else {
+			if (!xtables_strtoul(arg, &end, &value, 0, lmax))
+				xt_params->exit_err(PARAMETER_PROBLEM,
+					"%s: bad value for option \"--%s\" near "
+					"\"%s\", or out of range (0-%ju).\n",
+					cb->ext_name, entry->name, arg, lmax);
+			if (*end != '\0' && *end != sep)
+				xt_params->exit_err(PARAMETER_PROBLEM,
+					"%s: Argument to \"--%s\" has "
+					"unexpected characters near \"%s\".\n",
+					cb->ext_name, entry->name, end);
+		}
+		xtopt_mint_value_to_cb(cb, value);
+		++cb->nvals;
+		xtopt_mint_value_to_ptr(cb, &put, value);
+		if (*end == '\0')
+			break;
+	}
+}
+
+static void xtopt_parse_string(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	size_t z = strlen(cb->arg);
+	char *p;
+
+	if (entry->min != 0 && z < entry->min)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"Argument must have a minimum length of "
+			"%u characters\n", entry->min);
+	if (entry->max != 0 && z > entry->max)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"Argument must have a maximum length of "
+			"%u characters\n", entry->max);
+	if (!(entry->flags & XTOPT_PUT))
+		return;
+	if (z >= entry->size)
+		z = entry->size - 1;
+	p = XTOPT_MKPTR(cb);
+	strncpy(p, cb->arg, z);
+	p[z] = '\0';
+}
+
+static const struct tos_symbol_info {
+	unsigned char value;
+	const char *name;
+} tos_symbol_names[] = {
+	{IPTOS_LOWDELAY,    "Minimize-Delay"},
+	{IPTOS_THROUGHPUT,  "Maximize-Throughput"},
+	{IPTOS_RELIABILITY, "Maximize-Reliability"},
+	{IPTOS_MINCOST,     "Minimize-Cost"},
+	{IPTOS_NORMALSVC,   "Normal-Service"},
+	{},
+};
+
+/*
+ * tos_parse_numeric - parse a string like "15/255"
+ *
+ * @str:	input string
+ * @tvm:	(value/mask) tuple
+ * @max:	maximum allowed value (must be pow(2,some_int)-1)
+ */
+static bool tos_parse_numeric(const char *str, struct xt_option_call *cb,
+                              unsigned int max)
+{
+	unsigned int value;
+	char *end;
+
+	xtables_strtoui(str, &end, &value, 0, max);
+	cb->val.tos_value = value;
+	cb->val.tos_mask  = max;
+
+	if (*end == '/') {
+		const char *p = end + 1;
+
+		if (!xtables_strtoui(p, &end, &value, 0, max))
+			xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"",
+			           str);
+		cb->val.tos_mask = value;
+	}
+
+	if (*end != '\0')
+		xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"", str);
+	return true;
+}
+
+/**
+ * @str:	input string
+ * @tvm:	(value/mask) tuple
+ * @def_mask:	mask to force when a symbolic name is used
+ */
+static void xtopt_parse_tosmask(struct xt_option_call *cb)
+{
+	const struct tos_symbol_info *symbol;
+	char *tmp;
+
+	if (xtables_strtoui(cb->arg, &tmp, NULL, 0, UINT8_MAX)) {
+		tos_parse_numeric(cb->arg, cb, UINT8_MAX);
+		return;
+	}
+	/*
+	 * This is our way we deal with different defaults
+	 * for different revisions.
+	 */
+	cb->val.tos_mask = cb->entry->max;
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		if (strcasecmp(cb->arg, symbol->name) == 0) {
+			cb->val.tos_value = symbol->value;
+			return;
+		}
+
+	xtables_error(PARAMETER_PROBLEM, "Symbolic name \"%s\" is unknown",
+		      cb->arg);
+}
+
+/**
+ * Validate the input for being conformant to "mark[/mask]".
+ */
+static void xtopt_parse_markmask(struct xt_option_call *cb)
+{
+	unsigned int mark = 0, mask = ~0U;
+	char *end;
+
+	if (!xtables_strtoui(cb->arg, &end, &mark, 0, UINT32_MAX))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: bad mark value for option \"--%s\", "
+			"or out of range.\n",
+			cb->ext_name, cb->entry->name);
+	if (*end == '/' &&
+	    !xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: bad mask value for option \"--%s\", "
+			"or out of range.\n",
+			cb->ext_name, cb->entry->name);
+	if (*end != '\0')
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: trailing garbage after value "
+			"for option \"--%s\".\n",
+			cb->ext_name, cb->entry->name);
+	cb->val.mark = mark;
+	cb->val.mask = mask;
+}
+
+static int xtopt_sysloglvl_compare(const void *a, const void *b)
+{
+	const char *name = a;
+	const struct syslog_level *entry = b;
+
+	return strcmp(name, entry->name);
+}
+
+static void xtopt_parse_sysloglevel(struct xt_option_call *cb)
+{
+	static const struct syslog_level log_names[] = { /* must be sorted */
+		{"alert",   LOG_ALERT},
+		{"crit",    LOG_CRIT},
+		{"debug",   LOG_DEBUG},
+		{"emerg",   LOG_EMERG},
+		{"error",   LOG_ERR}, /* deprecated */
+		{"info",    LOG_INFO},
+		{"notice",  LOG_NOTICE},
+		{"panic",   LOG_EMERG}, /* deprecated */
+		{"warning", LOG_WARNING},
+	};
+	const struct syslog_level *e;
+	unsigned int num = 0;
+
+	if (!xtables_strtoui(cb->arg, NULL, &num, 0, 7)) {
+		e = bsearch(cb->arg, log_names, ARRAY_SIZE(log_names),
+			    sizeof(*log_names), xtopt_sysloglvl_compare);
+		if (e == NULL)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"log level \"%s\" unknown\n", cb->arg);
+		num = e->level;
+	}
+	cb->val.syslog_level = num;
+	if (cb->entry->flags & XTOPT_PUT)
+		*(uint8_t *)XTOPT_MKPTR(cb) = num;
+}
+
+static void *xtables_sa_host(const void *sa, unsigned int afproto)
+{
+	if (afproto == AF_INET6)
+		return &((struct sockaddr_in6 *)sa)->sin6_addr;
+	else if (afproto == AF_INET)
+		return &((struct sockaddr_in *)sa)->sin_addr;
+	return (void *)sa;
+}
+
+static socklen_t xtables_sa_hostlen(unsigned int afproto)
+{
+	if (afproto == AF_INET6)
+		return sizeof(struct in6_addr);
+	else if (afproto == AF_INET)
+		return sizeof(struct in_addr);
+	return 0;
+}
+
+/**
+ * Accepts: a hostname (DNS), or a single inetaddr - without any mask. The
+ * result is stored in @cb->val.haddr. Additionally, @cb->val.hmask and
+ * @cb->val.hlen are set for completeness to the appropriate values.
+ */
+static void xtopt_parse_host(struct xt_option_call *cb)
+{
+	struct addrinfo hints = {.ai_family = afinfo->family};
+	unsigned int adcount = 0;
+	struct addrinfo *res, *p;
+	int ret;
+
+	ret = getaddrinfo(cb->arg, NULL, &hints, &res);
+	if (ret < 0)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"getaddrinfo: %s\n", gai_strerror(ret));
+
+	memset(&cb->val.hmask, 0xFF, sizeof(cb->val.hmask));
+	cb->val.hlen = (afinfo->family == NFPROTO_IPV4) ? 32 : 128;
+
+	for (p = res; p != NULL; p = p->ai_next) {
+		if (adcount == 0) {
+			memset(&cb->val.haddr, 0, sizeof(cb->val.haddr));
+			memcpy(&cb->val.haddr,
+			       xtables_sa_host(p->ai_addr, p->ai_family),
+			       xtables_sa_hostlen(p->ai_family));
+			++adcount;
+			continue;
+		}
+		if (memcmp(&cb->val.haddr,
+		    xtables_sa_host(p->ai_addr, p->ai_family),
+		    xtables_sa_hostlen(p->ai_family)) != 0)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"%s resolves to more than one address\n",
+				cb->arg);
+	}
+
+	freeaddrinfo(res);
+	if (cb->entry->flags & XTOPT_PUT)
+		/* Validation in xtables_option_metavalidate */
+		memcpy(XTOPT_MKPTR(cb), &cb->val.haddr,
+		       sizeof(cb->val.haddr));
+}
+
+/**
+ * @name:	port name, or number as a string (e.g. "http" or "80")
+ *
+ * Resolve a port name to a number. Returns the port number in integral
+ * form on success, or <0 on error. (errno will not be set.)
+ */
+static int xtables_getportbyname(const char *name)
+{
+	struct addrinfo *res = NULL, *p;
+	int ret;
+
+	ret = getaddrinfo(NULL, name, NULL, &res);
+	if (ret < 0)
+		return -1;
+	ret = -1;
+	for (p = res; p != NULL; p = p->ai_next) {
+		if (p->ai_family == AF_INET6) {
+			ret = ((struct sockaddr_in6 *)p->ai_addr)->sin6_port;
+			break;
+		} else if (p->ai_family == AF_INET) {
+			ret = ((struct sockaddr_in *)p->ai_addr)->sin_port;
+			break;
+		}
+	}
+	freeaddrinfo(res);
+	if (ret < 0)
+		return ret;
+	return ntohs(ret);
+}
+
+/**
+ * Validate and parse a protocol specification (number or name) by use of
+ * /etc/protocols and put the result into @cb->val.protocol.
+ */
+static void xtopt_parse_protocol(struct xt_option_call *cb)
+{
+	cb->val.protocol = xtables_parse_protocol(cb->arg);
+	if (cb->entry->flags & XTOPT_PUT)
+		*(uint8_t *)XTOPT_MKPTR(cb) = cb->val.protocol;
+}
+
+/**
+ * Validate and parse a port specification and put the result into
+ * @cb->val.port.
+ */
+static void xtopt_parse_port(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	int ret;
+
+	ret = xtables_getportbyname(cb->arg);
+	if (ret < 0)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"Port \"%s\" does not resolve to anything.\n",
+			cb->arg);
+	if (entry->flags & XTOPT_NBO)
+		ret = htons(ret);
+	cb->val.port = ret;
+	if (entry->flags & XTOPT_PUT)
+		*(uint16_t *)XTOPT_MKPTR(cb) = cb->val.port;
+}
+
+static void xtopt_parse_mport(struct xt_option_call *cb)
+{
+	static const size_t esize = sizeof(uint16_t);
+	const struct xt_option_entry *entry = cb->entry;
+	char *lo_arg, *wp_arg, *arg;
+	unsigned int maxiter;
+	int value;
+
+	wp_arg = lo_arg = strdup(cb->arg);
+	if (lo_arg == NULL)
+		xt_params->exit_err(RESOURCE_PROBLEM, "strdup");
+
+	maxiter = entry->size / esize;
+	if (maxiter == 0)
+		maxiter = 2; /* ARRAY_SIZE(cb->val.port_range) */
+	if (entry->size % esize != 0)
+		xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
+			"not have proper size\n", __func__);
+
+	cb->val.port_range[0] = 0;
+	cb->val.port_range[1] = UINT16_MAX;
+	cb->nvals = 0;
+
+	while ((arg = strsep(&wp_arg, ":")) != NULL) {
+		if (cb->nvals == maxiter)
+			xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
+				"components for option \"--%s\" (max: %u)\n",
+				cb->ext_name, entry->name, maxiter);
+		if (*arg == '\0') {
+			++cb->nvals;
+			continue;
+		}
+
+		value = xtables_getportbyname(arg);
+		if (value < 0)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Port \"%s\" does not resolve to "
+				"anything.\n", arg);
+		if (entry->flags & XTOPT_NBO)
+			value = htons(value);
+		if (cb->nvals < ARRAY_SIZE(cb->val.port_range))
+			cb->val.port_range[cb->nvals] = value;
+		++cb->nvals;
+	}
+
+	if (cb->nvals == 1) {
+		cb->val.port_range[1] = cb->val.port_range[0];
+		++cb->nvals;
+	}
+	if (entry->flags & XTOPT_PUT)
+		memcpy(XTOPT_MKPTR(cb), cb->val.port_range, sizeof(uint16_t) *
+		       (cb->nvals <= maxiter ? cb->nvals : maxiter));
+	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.
+ */
+static void xtopt_parse_plen(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	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)) {
+		/* 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\", "
+			"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;
+}
+
+/**
+ * Reuse xtopt_parse_plen for testing the integer. Afterwards convert this to
+ * a bitmask, and make it available through @cb->val.hmask (hlen remains
+ * valid). If %XTOPT_PUT is used, hmask will be copied to the target area.
+ */
+static void xtopt_parse_plenmask(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	uint32_t *mask = cb->val.hmask.all;
+
+	xtopt_parse_plen(cb);
+
+	memset(mask, 0xFF, sizeof(union nf_inet_addr));
+	/* This shifting is AF-independent. */
+	if (cb->val.hlen == 0) {
+		mask[0] = mask[1] = mask[2] = mask[3] = 0;
+	} else if (cb->val.hlen <= 32) {
+		mask[0] <<= 32 - cb->val.hlen;
+		mask[1] = mask[2] = mask[3] = 0;
+	} else if (cb->val.hlen <= 64) {
+		mask[1] <<= 32 - (cb->val.hlen - 32);
+		mask[2] = mask[3] = 0;
+	} else if (cb->val.hlen <= 96) {
+		mask[2] <<= 32 - (cb->val.hlen - 64);
+		mask[3] = 0;
+	} else if (cb->val.hlen <= 128) {
+		mask[3] <<= 32 - (cb->val.hlen - 96);
+	}
+	mask[0] = htonl(mask[0]);
+	mask[1] = htonl(mask[1]);
+	mask[2] = htonl(mask[2]);
+	mask[3] = htonl(mask[3]);
+	if (entry->flags & XTOPT_PUT)
+		memcpy(XTOPT_MKPTR(cb), mask, sizeof(union nf_inet_addr));
+}
+
+static void xtopt_parse_hostmask(struct xt_option_call *cb)
+{
+	const char *orig_arg = cb->arg;
+	char *work, *p;
+
+	if (strchr(cb->arg, '/') == NULL) {
+		xtopt_parse_host(cb);
+		return;
+	}
+	work = strdup(orig_arg);
+	if (work == NULL)
+		xt_params->exit_err(PARAMETER_PROBLEM, "strdup");
+	p = strchr(work, '/'); /* by def this can't be NULL now */
+	*p++ = '\0';
+	/*
+	 * Because xtopt_parse_host and xtopt_parse_plenmask would store
+	 * different things in the same target area, XTTYPE_HOSTMASK must
+	 * disallow XTOPT_PUT, which it does by forcing its absence,
+	 * cf. not being listed in xtopt_psize.
+	 */
+	cb->arg = work;
+	xtopt_parse_host(cb);
+	cb->arg = p;
+	xtopt_parse_plenmask(cb);
+	cb->arg = orig_arg;
+}
+
+static void xtopt_parse_ethermac(struct xt_option_call *cb)
+{
+	const char *arg = cb->arg;
+	unsigned int i;
+	char *end;
+
+	for (i = 0; i < ARRAY_SIZE(cb->val.ethermac) - 1; ++i) {
+		cb->val.ethermac[i] = strtoul(arg, &end, 16);
+		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 (*end != '\0' || end - arg > 2)
+		goto out;
+	if (cb->entry->flags & XTOPT_PUT)
+		memcpy(XTOPT_MKPTR(cb), cb->val.ethermac,
+		       sizeof(cb->val.ethermac));
+	return;
+ out:
+	xt_params->exit_err(PARAMETER_PROBLEM, "ether");
+}
+
+static void (*const xtopt_subparse[])(struct xt_option_call *) = {
+	[XTTYPE_UINT8]       = xtopt_parse_int,
+	[XTTYPE_UINT16]      = xtopt_parse_int,
+	[XTTYPE_UINT32]      = xtopt_parse_int,
+	[XTTYPE_UINT64]      = xtopt_parse_int,
+	[XTTYPE_UINT8RC]     = xtopt_parse_mint,
+	[XTTYPE_UINT16RC]    = xtopt_parse_mint,
+	[XTTYPE_UINT32RC]    = xtopt_parse_mint,
+	[XTTYPE_UINT64RC]    = xtopt_parse_mint,
+	[XTTYPE_DOUBLE]      = xtopt_parse_float,
+	[XTTYPE_STRING]      = xtopt_parse_string,
+	[XTTYPE_TOSMASK]     = xtopt_parse_tosmask,
+	[XTTYPE_MARKMASK32]  = xtopt_parse_markmask,
+	[XTTYPE_SYSLOGLEVEL] = xtopt_parse_sysloglevel,
+	[XTTYPE_HOST]        = xtopt_parse_host,
+	[XTTYPE_HOSTMASK]    = xtopt_parse_hostmask,
+	[XTTYPE_PROTOCOL]    = xtopt_parse_protocol,
+	[XTTYPE_PORT]        = xtopt_parse_port,
+	[XTTYPE_PORTRC]      = xtopt_parse_mport,
+	[XTTYPE_PLEN]        = xtopt_parse_plen,
+	[XTTYPE_PLENMASK]    = xtopt_parse_plenmask,
+	[XTTYPE_ETHERMAC]    = xtopt_parse_ethermac,
+};
+
+/**
+ * The master option parsing routine. May be used for the ".x6_parse"
+ * function pointer in extensions if fully automatic parsing is desired.
+ * It may be also called manually from a custom x6_parse function.
+ */
+void xtables_option_parse(struct xt_option_call *cb)
+{
+	const struct xt_option_entry *entry = cb->entry;
+	unsigned int eflag = 1 << cb->entry->id;
+
+	/*
+	 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
+	 * prevention. Though it turned out that this is too much typing (most
+	 * of the options are one-time use only), so now we also have
+	 * %XTOPT_MULTI.
+	 */
+	if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
+	    cb->xflags & eflag)
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: option \"--%s\" can only be used once.\n",
+			cb->ext_name, cb->entry->name);
+	if (cb->invert && !(entry->flags & XTOPT_INVERT))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: option \"--%s\" cannot be inverted.\n",
+			cb->ext_name, entry->name);
+	if (entry->type != XTTYPE_NONE && optarg == NULL)
+		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);
+	/* Exclusion with other flags tested later in finalize. */
+	cb->xflags |= 1 << entry->id;
+}
+
+/**
+ * Verifies that an extension's option map descriptor is valid, and ought to
+ * be called right after the extension has been loaded, and before option
+ * merging/xfrm.
+ */
+void xtables_option_metavalidate(const char *name,
+				 const struct xt_option_entry *entry)
+{
+	for (; entry->name != NULL; ++entry) {
+		if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
+		    entry->id >= XT_OPTION_OFFSET_SCALE)
+			xt_params->exit_err(OTHER_PROBLEM,
+				"Extension %s uses invalid ID %u\n",
+				name, entry->id);
+		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,
+				"%s: entry type of option \"--%s\" cannot be "
+				"combined with XTOPT_PUT\n",
+				name, entry->name);
+		if (xtopt_psize[entry->type] != -1 &&
+		    xtopt_psize[entry->type] != entry->size)
+			xt_params->exit_err(OTHER_PROBLEM,
+				"%s: option \"--%s\" points to a memory block "
+				"of wrong size (expected %zu, got %zu)\n",
+				name, entry->name,
+				xtopt_psize[entry->type], entry->size);
+	}
+}
+
+/**
+ * Find an option entry by its id.
+ */
+static const struct xt_option_entry *
+xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
+{
+	for (; entry->name != NULL; ++entry)
+		if (entry->id == id)
+			return entry;
+	return NULL;
+}
+
+/**
+ * @c:		getopt id (i.e. with offset)
+ * @fw:		struct ipt_entry or ip6t_entry
+ *
+ * Dispatch arguments to the appropriate parse function, based upon the
+ * extension's choice of API.
+ */
+void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
+			   struct xtables_target *t, void *fw)
+{
+	struct xt_option_call cb;
+
+	if (t->x6_parse == NULL) {
+		if (t->parse != NULL)
+			t->parse(c - t->option_offset, argv, invert,
+				 &t->tflags, fw, &t->t);
+		return;
+	}
+
+	c -= t->option_offset;
+	cb.entry = xtables_option_lookup(t->x6_options, c);
+	if (cb.entry == NULL)
+		xtables_error(OTHER_PROBLEM,
+			"Extension does not know id %u\n", c);
+	cb.arg      = optarg;
+	cb.invert   = invert;
+	cb.ext_name = t->name;
+	cb.data     = t->t->data;
+	cb.xflags   = t->tflags;
+	cb.target   = &t->t;
+	cb.xt_entry = fw;
+	cb.udata    = t->udata;
+	t->x6_parse(&cb);
+	t->tflags = cb.xflags;
+}
+
+/**
+ * @c:		getopt id (i.e. with offset)
+ * @fw:		struct ipt_entry or ip6t_entry
+ *
+ * Dispatch arguments to the appropriate parse function, based upon the
+ * extension's choice of API.
+ */
+void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
+			   struct xtables_match *m, void *fw)
+{
+	struct xt_option_call cb;
+
+	if (m->x6_parse == NULL) {
+		if (m->parse != NULL)
+			m->parse(c - m->option_offset, argv, invert,
+				 &m->mflags, fw, &m->m);
+		return;
+	}
+
+	c -= m->option_offset;
+	cb.entry = xtables_option_lookup(m->x6_options, c);
+	if (cb.entry == NULL)
+		xtables_error(OTHER_PROBLEM,
+			"Extension does not know id %u\n", c);
+	cb.arg      = optarg;
+	cb.invert   = invert;
+	cb.ext_name = m->name;
+	cb.data     = m->m->data;
+	cb.xflags   = m->mflags;
+	cb.match    = &m->m;
+	cb.xt_entry = fw;
+	cb.udata    = m->udata;
+	m->x6_parse(&cb);
+	m->mflags = cb.xflags;
+}
+
+/**
+ * @name:	name of extension
+ * @entry:	current option (from all ext's entries) being validated
+ * @xflags:	flags the extension has collected
+ * @i:		conflicting option (id) to test for
+ */
+static void
+xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
+		       const struct xt_option_entry *other,
+		       unsigned int xflags)
+{
+	unsigned int ef = 1 << entry->id, of = 1 << other->id;
+
+	if (entry->also & of && !(xflags & of))
+		xt_params->exit_err(PARAMETER_PROBLEM,
+			"%s: option \"--%s\" also requires \"--%s\".\n",
+			name, entry->name, other->name);
+
+	if (!(entry->excl & of))
+		/* Use of entry does not collide with other option, good. */
+		return;
+	if ((xflags & (ef | of)) != (ef | of))
+		/* Conflicting options were not used. */
+		return;
+
+	xt_params->exit_err(PARAMETER_PROBLEM,
+		"%s: option \"--%s\" cannot be used together with \"--%s\".\n",
+		name, entry->name, other->name);
+}
+
+/**
+ * @name:	name of extension
+ * @xflags:	accumulated flags
+ * @entry:	extension's option table
+ *
+ * Check that all option constraints have been met. This effectively replaces
+ * ->final_check of the older API.
+ */
+void xtables_options_fcheck(const char *name, unsigned int xflags,
+			    const struct xt_option_entry *table)
+{
+	const struct xt_option_entry *entry, *other;
+	unsigned int i;
+
+	for (entry = table; entry->name != NULL; ++entry) {
+		if (entry->flags & XTOPT_MAND &&
+		    !(xflags & (1 << entry->id)))
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"%s: option \"--%s\" must be specified\n",
+				name, entry->name);
+		if (!(xflags & (1 << entry->id)))
+			/* Not required, not specified, thus skip. */
+			continue;
+
+		for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
+			if (entry->id == i)
+				/*
+				 * Avoid conflict with self. Multi-use check
+				 * was done earlier in xtables_option_parse.
+				 */
+				continue;
+			other = xtables_option_lookup(table, i);
+			if (other == NULL)
+				continue;
+			xtables_option_fcheck2(name, entry, other, xflags);
+		}
+	}
+}
+
+/**
+ * Dispatch arguments to the appropriate final_check function, based upon the
+ * extension's choice of API.
+ */
+void xtables_option_tfcall(struct xtables_target *t)
+{
+	if (t->x6_fcheck != NULL) {
+		struct xt_fcheck_call cb;
+
+		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);
+	}
+	if (t->x6_options != NULL)
+		xtables_options_fcheck(t->name, t->tflags, t->x6_options);
+}
+
+/**
+ * Dispatch arguments to the appropriate final_check function, based upon the
+ * extension's choice of API.
+ */
+void xtables_option_mfcall(struct xtables_match *m)
+{
+	if (m->x6_fcheck != NULL) {
+		struct xt_fcheck_call cb;
+
+		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);
+	}
+	if (m->x6_options != NULL)
+		xtables_options_fcheck(m->name, m->mflags, m->x6_options);
+}
+
+struct xtables_lmap *xtables_lmap_init(const char *file)
+{
+	struct xtables_lmap *lmap_head = NULL, *lmap_prev = NULL, *lmap_this;
+	char buf[512];
+	FILE *fp;
+	char *cur, *nxt;
+	int id;
+
+	fp = fopen(file, "re");
+	if (fp == NULL)
+		return NULL;
+
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		cur = buf;
+		while (isspace(*cur))
+			++cur;
+		if (*cur == '#' || *cur == '\n' || *cur == '\0')
+			continue;
+
+		/* iproute2 allows hex and dec format */
+		errno = 0;
+		id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) == 0 ? 16 : 10);
+		if (nxt == cur || errno != 0)
+			continue;
+
+		/* same boundaries as in iproute2 */
+		if (id < 0 || id > 255)
+			continue;
+		cur = nxt;
+
+		if (!isspace(*cur))
+			continue;
+		while (isspace(*cur))
+			++cur;
+		if (*cur == '#' || *cur == '\n' || *cur == '\0')
+			continue;
+		nxt = cur;
+		while (*nxt != '\0' && !isspace(*nxt))
+			++nxt;
+		if (nxt == cur)
+			continue;
+		*nxt = '\0';
+
+		/* found valid data */
+		lmap_this = malloc(sizeof(*lmap_this));
+		if (lmap_this == NULL) {
+			perror("malloc");
+			goto out;
+		}
+		lmap_this->id   = id;
+		lmap_this->name = strdup(cur);
+		if (lmap_this->name == NULL) {
+			free(lmap_this);
+			goto out;
+		}
+		lmap_this->next = NULL;
+
+		if (lmap_prev != NULL)
+			lmap_prev->next = lmap_this;
+		else
+			lmap_head = lmap_this;
+		lmap_prev = lmap_this;
+	}
+
+	fclose(fp);
+	return lmap_head;
+ out:
+	fclose(fp);
+	xtables_lmap_free(lmap_head);
+	return NULL;
+}
+
+void xtables_lmap_free(struct xtables_lmap *head)
+{
+	struct xtables_lmap *next;
+
+	for (; head != NULL; head = next) {
+		next = head->next;
+		free(head->name);
+		free(head);
+	}
+}
+
+int xtables_lmap_name2id(const struct xtables_lmap *head, const char *name)
+{
+	for (; head != NULL; head = head->next)
+		if (strcmp(head->name, name) == 0)
+			return head->id;
+	return -1;
+}
+
+const char *xtables_lmap_id2name(const struct xtables_lmap *head, int id)
+{
+	for (; head != NULL; head = head->next)
+		if (head->id == id)
+			return head->name;
+	return NULL;
+}
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..64d9bbc
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,2 @@
+/libtool.m4
+/lt*.m4
diff --git a/m4/ax_check_linker_flags.m4 b/m4/ax_check_linker_flags.m4
new file mode 100644
index 0000000..ba7bf3c
--- /dev/null
+++ b/m4/ax_check_linker_flags.m4
@@ -0,0 +1,78 @@
+#http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_check_linker_flags.m4
+# ===========================================================================
+#   http://www.gnu.org/software/autoconf-archive/ax_check_linker_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_CHECK_LINKER_FLAGS(FLAGS, [ACTION-SUCCESS], [ACTION-FAILURE])
+#
+# DESCRIPTION
+#
+#   Check whether the given linker FLAGS work with the current language's
+#   linker, or whether they give an error.
+#
+#   ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+#   success/failure.
+#
+#   NOTE: Based on AX_CHECK_COMPILER_FLAGS.
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Mike Frysinger <vapier@gentoo.org>
+#   Copyright (c) 2009 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2009 Matteo Frigo
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_LINKER_FLAGS],
+[AC_MSG_CHECKING([whether the linker accepts $1])
+dnl Some hackery here since AC_CACHE_VAL can't handle a non-literal varname:
+AS_LITERAL_IF([$1],
+  [AC_CACHE_VAL(AS_TR_SH(ax_cv_linker_flags_[$1]), [
+      ax_save_FLAGS=$LDFLAGS
+      LDFLAGS="$1"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+        AS_TR_SH(ax_cv_linker_flags_[$1])=yes,
+        AS_TR_SH(ax_cv_linker_flags_[$1])=no)
+      LDFLAGS=$ax_save_FLAGS])],
+  [ax_save_FLAGS=$LDFLAGS
+   LDFLAGS="$1"
+   AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+     eval AS_TR_SH(ax_cv_linker_flags_[$1])=yes,
+     eval AS_TR_SH(ax_cv_linker_flags_[$1])=no)
+   LDFLAGS=$ax_save_FLAGS])
+eval ax_check_linker_flags=$AS_TR_SH(ax_cv_linker_flags_[$1])
+AC_MSG_RESULT($ax_check_linker_flags)
+if test "x$ax_check_linker_flags" = xyes; then
+	m4_default([$2], :)
+else
+	m4_default([$3], :)
+fi
+])dnl AX_CHECK_LINKER_FLAGS
diff --git a/release.sh b/release.sh
new file mode 100644
index 0000000..7c76423
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,31 @@
+#! /bin/sh
+#
+set -e
+
+VERSION=1.4.7
+PREV_VERSION=1.4.6
+TMPDIR=/tmp/ipt-release
+IPTDIR="$TMPDIR/iptables-$VERSION"
+
+PATCH="patch-iptables-$PREV_VERSION-$VERSION.bz2";
+TARBALL="iptables-$VERSION.tar.bz2";
+CHANGELOG="changes-iptables-$PREV_VERSION-$VERSION.txt";
+
+mkdir -p "$TMPDIR"
+git shortlog "v$PREV_VERSION..v$VERSION" > "$TMPDIR/$CHANGELOG"
+git diff "v$PREV_VERSION..v$VERSION" | bzip2 > "$TMPDIR/$PATCH"
+git archive --prefix="iptables-$VERSION/" "v$VERSION" | tar -xC "$TMPDIR/"
+
+cd "$IPTDIR" && {
+	sh autogen.sh
+	cd ..
+}
+
+tar -cjf "$TARBALL" "iptables-$VERSION";
+gpg -u "Netfilter Core Team" -sb "$TARBALL";
+md5sum "$TARBALL" >"$TARBALL.md5sum";
+sha1sum "$TARBALL" >"$TARBALL.sha1sum";
+
+gpg -u "Netfilter Core Team" -sb "$PATCH";
+md5sum "$PATCH" >"$PATCH.md5sum";
+sha1sum "$PATCH" >"$PATCH.sha1sum";
diff --git a/tests/options-ipv4.rules b/tests/options-ipv4.rules
new file mode 100644
index 0000000..b4adc92
--- /dev/null
+++ b/tests/options-ipv4.rules
@@ -0,0 +1,52 @@
+# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
+*mangle
+:PREROUTING ACCEPT [2461:977932]
+:INPUT ACCEPT [2461:977932]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [1740:367048]
+:POSTROUTING ACCEPT [1740:367048]
+
+# libipt_
+-A INPUT -p ah -m ah --ahspi 1
+-A INPUT -p ah -m ah --ahspi :2
+-A INPUT -p ah -m ah --ahspi 0:3
+-A INPUT -p ah -m ah --ahspi 4:
+-A INPUT -p ah -m ah --ahspi 5:4294967295
+
+-A FORWARD -p tcp -j ECN --ecn-tcp-remove
+-A FORWARD -j LOG --log-prefix "hi" --log-tcp-sequence --log-tcp-options --log-ip-options --log-uid --log-macdecode
+-A FORWARD -j TTL --ttl-inc 1
+-A FORWARD -j TTL --ttl-dec 1
+-A FORWARD -j TTL --ttl-set 1
+-A FORWARD -j ULOG --ulog-prefix "abc" --ulog-cprange 2 --ulog-qthreshold 2
+COMMIT
+# Completed on Mon Jan 31 03:03:38 2011
+# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
+*nat
+:PREROUTING ACCEPT [0:0]
+:INPUT ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+:POSTROUTING ACCEPT [0:0]
+-A PREROUTING -d 1.2.3.4/32 -i lo -j CLUSTERIP --new --hashmode sourceip --clustermac 01:02:03:04:05:06 --total-nodes 9 --local-node 2 --hash-init 123456789
+-A PREROUTING -i dummy0 -j DNAT --to-destination 1.2.3.4 --random --persistent
+-A PREROUTING -i dummy0 -p tcp -j REDIRECT --to-ports 1-2 --random
+-A POSTROUTING -o dummy0 -p tcp -j MASQUERADE --to-ports 1-2 --random
+-A POSTROUTING -o dummy0 -p tcp -j NETMAP --to 1.0.0.0/8
+-A POSTROUTING -o dummy0 -p tcp -j SNAT --to-source 1.2.3.4-1.2.3.5 --random --persistent
+COMMIT
+# Completed on Mon Jan 31 03:03:38 2011
+# Generated by iptables-save v1.4.10 on Mon Jan 31 03:03:38 2011
+*filter
+:INPUT ACCEPT [76:13548]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [59:11240]
+#-A INPUT -m addrtype --src-type UNICAST --dst-type UNICAST --limit-iface-in
+-A INPUT -p tcp -m ecn --ecn-tcp-ece --ecn-tcp-cwr --ecn-ip-ect 0
+-A INPUT -p tcp -m ecn --ecn-tcp-ece --ecn-tcp-cwr --ecn-ip-ect 1
+-A INPUT -p icmp -m icmp --icmp-type 5/0
+-A INPUT -p icmp -m icmp --icmp-type 5/1
+-A INPUT -p icmp -m icmp --icmp-type 5
+-A INPUT -m realm --realm 0x1 -m ttl --ttl-eq  64 -m ttl --ttl-lt  64 -m ttl --ttl-gt  64
+-A FORWARD -p tcp -j REJECT --reject-with tcp-reset
+COMMIT
+# Completed on Mon Jan 31 03:03:39 2011
diff --git a/tests/options-most.rules b/tests/options-most.rules
new file mode 100644
index 0000000..ef4e7f1
--- /dev/null
+++ b/tests/options-most.rules
@@ -0,0 +1,214 @@
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+:matches - -
+:ntarg - -
+:zmatches - -
+-A INPUT -j matches
+-A INPUT -m u32 --u32 "0x0=0x0&&0x0=0x1" -j ntarg
+-A INPUT -j zmatches
+-A INPUT -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 cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001 -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001 -m comment --comment foo -m connbytes --connbytes 1:2 --connbytes-mode packets --connbytes-dir both -m connlimit --connlimit-upto 1 --connlimit-mask 8 --connlimit-saddr -m connlimit --connlimit-above 1 --connlimit-mask 9 --connlimit-daddr -m connmark --mark 0x99 -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 -m cpu --cpu 2 -m dscp --dscp 0x04 -m dscp --dscp 0x00 -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 -m hashlimit --hashlimit-above 5/sec --hashlimit-burst 5 --hashlimit-name f1 -m helper --helper ftp -m iprange --src-range ::1-::2 --dst-range ::1-::2 -m ipvs --vaddr fe80::/64 --vport 1 --vdir REPLY --vmethod GATE --vportctl 21 -m length --length 1:2 -m limit --limit 1/sec -m mac --mac-source 01:02:03:04:05:06 -m mark --mark 0x1 -m physdev --physdev-in eth0 -m pkttype --pkt-type unicast -m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --mode tunnel --tunnel-dst fe80::/64 --tunnel-src fe80::/64 --next --reqid 2 -m quota --quota 0 -m recent --rcheck --name DEFAULT --rsource -m socket --transparent -m string --string "foobar" --algo kmp --from 1 --to 2 --icase -m time --timestart 01:02:03 --timestop 03:04:05 --monthdays 1,2,3,4,5 --weekdays Mon,Fri,Sun --datestart 2001-02-03T04:05:06 --datestop 2012-09-08T09:06:05 --utc -m tos --tos 0xff/0x01 -m u32 --u32 "0x0=0x0" -m u32 --u32 "0x0=0x0" -m hbh -m hbh -m hl --hl-eq 1
+-A INPUT -m ipv6header --header hop-by-hop --soft
+-A INPUT -p tcp -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001
+-A INPUT -p tcp -m cluster --cluster-local-nodemask 0x00000001 --cluster-total-nodes 2 --cluster-hash-seed 0x00000001
+-A INPUT -p tcp -m comment --comment foo
+-A INPUT -p tcp -m connbytes --connbytes 1:2 --connbytes-mode packets --connbytes-dir both
+-A INPUT -p tcp -m connlimit --connlimit-upto 1 --connlimit-mask 8 --connlimit-saddr
+-A INPUT -p tcp -m connlimit --connlimit-above 1 --connlimit-mask 9 --connlimit-daddr
+-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 -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
+-A INPUT -p tcp -m iprange --src-range ::1-::2 --dst-range ::1-::2
+-A INPUT -p tcp -m length --length 1:2
+-A INPUT -p tcp -m limit --limit 1/sec
+-A INPUT -p tcp -m mac --mac-source 01:02:03:04:05:06
+-A INPUT -p tcp -m mark --mark 0x1
+-A INPUT -p tcp -m physdev --physdev-in eth0
+-A INPUT -p tcp -m pkttype --pkt-type unicast
+-A INPUT -p tcp -m policy --dir in --pol ipsec --strict --reqid 1 --spi 0x1 --proto esp --mode tunnel --tunnel-dst fe80::/64 --tunnel-src fe80::/64 --next --reqid 2
+-A INPUT -p tcp -m quota --quota 0
+-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 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
+-A INPUT -p tcp -m multiport --dports 1,2 -m multiport --dports 1,2
+-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 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
+-A matches
+-A matches -m connbytes --connbytes 0:3 --connbytes-mode bytes --connbytes-dir both
+-A matches
+-A matches -m connbytes --connbytes 4: --connbytes-mode bytes --connbytes-dir both
+-A matches
+-A matches -m connbytes --connbytes 5:18446744073709551615 --connbytes-mode bytes --connbytes-dir both
+-A matches
+-A matches -m conntrack --ctexpire 1
+-A matches
+-A matches -m conntrack --ctexpire :2
+-A matches
+-A matches -m conntrack --ctexpire 0:3
+-A matches
+-A matches -m conntrack --ctexpire 4:
+-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
+-A matches
+-A matches -p esp -m esp --espspi 0:3
+-A matches
+-A matches -p esp -m esp --espspi 4:
+-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
+-A matches
+-A matches -m length --length :2
+-A matches
+-A matches -m length --length 0:3
+-A matches
+-A matches -m length --length 4:
+-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
+-A matches
+-A matches -p tcp -m tcpmss --mss 0:3
+-A matches
+-A matches -p tcp -m tcpmss --mss 4:
+-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
+-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
+-A matches
+-A matches -m time --timestart 02:00:00 --timestop 03:00:00 --datestart 1970-01-01T02:00:00 --datestop 1970-01-01T03:00:00
+-A matches
+-A matches -m ah --ahspi 1
+-A matches
+-A matches -m ah --ahspi :2
+-A matches
+-A matches -m ah --ahspi 0:3
+-A matches
+-A matches -m ah --ahspi 4:
+-A matches
+-A matches -m ah --ahspi 5:4294967295
+-A matches
+-A matches -m frag --fragid 1
+-A matches
+-A matches -m frag --fragid :2
+-A matches
+-A matches -m frag --fragid 0:3
+-A matches
+-A matches -m frag --fragid 4:
+-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
+-A matches
+-A matches -m rt --rt-segsleft 0:3
+-A matches
+-A matches -m rt --rt-segsleft 4:
+-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-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
+*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
new file mode 100644
index 0000000..216d1e4
--- /dev/null
+++ b/utils/.gitignore
@@ -0,0 +1,2 @@
+/nfnl_osf
+/nfbpf_compile
diff --git a/utils/Makefile.am b/utils/Makefile.am
new file mode 100644
index 0000000..c4192a9
--- /dev/null
+++ b/utils/Makefile.am
@@ -0,0 +1,25 @@
+# -*- Makefile -*-
+
+AM_CFLAGS = ${regular_CFLAGS}
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include \
+              -I${top_srcdir}/include ${libnfnetlink_CFLAGS}
+
+sbin_PROGRAMS =
+pkgdata_DATA =
+
+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
+
+if ENABLE_SYNCONF
+sbin_PROGRAMS += nfsynproxy
+nfsynproxy_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;
+}
+
diff --git a/utils/nfnl_osf.c b/utils/nfnl_osf.c
new file mode 100644
index 0000000..bb5f92d
--- /dev/null
+++ b/utils/nfnl_osf.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru>
+ * 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <linux/connector.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/unistd.h>
+
+#include <libnfnetlink/libnfnetlink.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/xt_osf.h>
+
+#define OPTDEL			','
+#define OSFPDEL 		':'
+#define MAXOPTSTRLEN		128
+
+#ifndef NIPQUAD
+#define NIPQUAD(addr) \
+	((unsigned char *)&addr)[0], \
+	((unsigned char *)&addr)[1], \
+	((unsigned char *)&addr)[2], \
+	((unsigned char *)&addr)[3]
+#endif
+
+static struct nfnl_handle *nfnlh;
+static struct nfnl_subsys_handle *nfnlssh;
+
+static struct xt_osf_opt IANA_opts[] = {
+	{ .kind = 0, .length = 1,},
+	{ .kind=1, .length=1,},
+	{ .kind=2, .length=4,},
+	{ .kind=3, .length=3,},
+	{ .kind=4, .length=2,},
+	{ .kind=5, .length=1,},		/* SACK length is not defined */
+	{ .kind=6, .length=6,},
+	{ .kind=7, .length=6,},
+	{ .kind=8, .length=10,},
+	{ .kind=9, .length=2,},
+	{ .kind=10, .length=3,},
+	{ .kind=11, .length=1,},		/* CC: Suppose 1 */
+	{ .kind=12, .length=1,},		/* the same */
+	{ .kind=13, .length=1,},		/* and here too */
+	{ .kind=14, .length=3,},
+	{ .kind=15, .length=1,},		/* TCP Alternate Checksum Data. Length is not defined */
+	{ .kind=16, .length=1,},
+	{ .kind=17, .length=1,},
+	{ .kind=18, .length=3,},
+	{ .kind=19, .length=18,},
+	{ .kind=20, .length=1,},
+	{ .kind=21, .length=1,},
+	{ .kind=22, .length=1,},
+	{ .kind=23, .length=1,},
+	{ .kind=24, .length=1,},
+	{ .kind=25, .length=1,},
+	{ .kind=26, .length=1,},
+};
+
+static FILE *osf_log_stream;
+
+static void uloga(const char *f, ...)
+{
+	va_list ap;
+
+	if (!osf_log_stream)
+		osf_log_stream = stdout;
+
+	va_start(ap, f);
+	vfprintf(osf_log_stream, f, ap);
+	va_end(ap);
+
+	fflush(osf_log_stream);
+}
+
+static void ulog(const char *f, ...)
+{
+	char str[64];
+	struct tm tm;
+	struct timeval tv;
+	va_list ap;
+
+	if (!osf_log_stream)
+		osf_log_stream = stdout;
+
+	gettimeofday(&tv, NULL);
+	localtime_r((time_t *)&tv.tv_sec, &tm);
+	strftime(str, sizeof(str), "%F %R:%S", &tm);
+
+	fprintf(osf_log_stream, "%s.%lu %ld ", str, tv.tv_usec, syscall(__NR_gettid));
+
+	va_start(ap, f);
+	vfprintf(osf_log_stream, f, ap);
+	va_end(ap);
+
+	fflush(osf_log_stream);
+}
+
+#define ulog_err(f, a...) uloga(f ": %s [%d].\n", ##a, strerror(errno), errno)
+
+static char *xt_osf_strchr(char *ptr, char c)
+{
+	char *tmp;
+
+	tmp = strchr(ptr, c);
+	if (tmp)
+		*tmp = '\0';
+
+	while (tmp && tmp + 1 && isspace(*(tmp + 1)))
+		tmp++;
+
+	return tmp;
+}
+
+static void xt_osf_parse_opt(struct xt_osf_opt *opt, __u16 *optnum, char *obuf, int olen)
+{
+	int i, op;
+	char *ptr, wc;
+	unsigned long val;
+
+	ptr = &obuf[0];
+	i = 0;
+	while (ptr != NULL && i < olen && *ptr != 0) {
+		val = 0;
+		op = 0;
+		wc = OSF_WSS_PLAIN;
+		switch (obuf[i]) {
+		case 'N':
+			op = OSFOPT_NOP;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				*ptr = '\0';
+				ptr++;
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		case 'S':
+			op = OSFOPT_SACKP;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				*ptr = '\0';
+				ptr++;
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		case 'T':
+			op = OSFOPT_TS;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				*ptr = '\0';
+				ptr++;
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		case 'W':
+			op = OSFOPT_WSO;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				switch (obuf[i + 1]) {
+				case '%':
+					wc = OSF_WSS_MODULO;
+					break;
+				case 'S':
+					wc = OSF_WSS_MSS;
+					break;
+				case 'T':
+					wc = OSF_WSS_MTU;
+					break;
+				default:
+					wc = OSF_WSS_PLAIN;
+					break;
+				}
+
+				*ptr = '\0';
+				ptr++;
+				if (wc)
+					val = strtoul(&obuf[i + 2], NULL, 10);
+				else
+					val = strtoul(&obuf[i + 1], NULL, 10);
+				i += (int)(ptr - &obuf[i]);
+
+			} else
+				i++;
+			break;
+		case 'M':
+			op = OSFOPT_MSS;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				if (obuf[i + 1] == '%')
+					wc = OSF_WSS_MODULO;
+				*ptr = '\0';
+				ptr++;
+				if (wc)
+					val = strtoul(&obuf[i + 2], NULL, 10);
+				else
+					val = strtoul(&obuf[i + 1], NULL, 10);
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		case 'E':
+			op = OSFOPT_EOL;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				*ptr = '\0';
+				ptr++;
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		default:
+			op = OSFOPT_EMPTY;
+			ptr = xt_osf_strchr(&obuf[i], OPTDEL);
+			if (ptr) {
+				ptr++;
+				i += (int)(ptr - &obuf[i]);
+			} else
+				i++;
+			break;
+		}
+
+		if (op != OSFOPT_EMPTY) {
+			opt[*optnum].kind = IANA_opts[op].kind;
+			opt[*optnum].length = IANA_opts[op].length;
+			opt[*optnum].wc.wc = wc;
+			opt[*optnum].wc.val = val;
+			(*optnum)++;
+		}
+	}
+}
+
+static int osf_load_line(char *buffer, int len, int del)
+{
+	int i, cnt = 0;
+	char obuf[MAXOPTSTRLEN];
+	struct xt_osf_user_finger f;
+	char *pbeg, *pend;
+	char buf[NFNL_HEADER_LEN + NFA_LENGTH(sizeof(struct xt_osf_user_finger))];
+	struct nlmsghdr *nmh = (struct nlmsghdr *) buf;
+
+	memset(&f, 0, sizeof(struct xt_osf_user_finger));
+
+	ulog("Loading '%s'.\n", buffer);
+
+	for (i = 0; i < len && buffer[i] != '\0'; ++i) {
+		if (buffer[i] == ':')
+			cnt++;
+	}
+
+	if (cnt != 8) {
+		ulog("Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len);
+		return -EINVAL;
+	}
+
+	memset(obuf, 0, sizeof(obuf));
+
+	pbeg = buffer;
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		if (pbeg[0] == 'S') {
+			f.wss.wc = OSF_WSS_MSS;
+			if (pbeg[1] == '%')
+				f.wss.val = strtoul(&pbeg[2], NULL, 10);
+			else if (pbeg[1] == '*')
+				f.wss.val = 0;
+			else
+				f.wss.val = strtoul(&pbeg[1], NULL, 10);
+		} else if (pbeg[0] == 'T') {
+			f.wss.wc = OSF_WSS_MTU;
+			if (pbeg[1] == '%')
+				f.wss.val = strtoul(&pbeg[2], NULL, 10);
+			else if (pbeg[1] == '*')
+				f.wss.val = 0;
+			else
+				f.wss.val = strtoul(&pbeg[1], NULL, 10);
+		} else if (pbeg[0] == '%') {
+			f.wss.wc = OSF_WSS_MODULO;
+			f.wss.val = strtoul(&pbeg[1], NULL, 10);
+		} else if (isdigit(pbeg[0])) {
+			f.wss.wc = OSF_WSS_PLAIN;
+			f.wss.val = strtoul(&pbeg[0], NULL, 10);
+		}
+
+		pbeg = pend + 1;
+	}
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		f.ttl = strtoul(pbeg, NULL, 10);
+		pbeg = pend + 1;
+	}
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		f.df = strtoul(pbeg, NULL, 10);
+		pbeg = pend + 1;
+	}
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		f.ss = strtoul(pbeg, NULL, 10);
+		pbeg = pend + 1;
+	}
+
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		cnt = snprintf(obuf, sizeof(obuf), "%s,", pbeg);
+		pbeg = pend + 1;
+	}
+
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		if (pbeg[0] == '@' || pbeg[0] == '*')
+			cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
+		else
+			cnt = snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
+		pbeg = pend + 1;
+	}
+
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		cnt = snprintf(f.version, sizeof(f.version), "%s", pbeg);
+		pbeg = pend + 1;
+	}
+
+	pend = xt_osf_strchr(pbeg, OSFPDEL);
+	if (pend) {
+		*pend = '\0';
+		cnt =
+		    snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
+		pbeg = pend + 1;
+	}
+
+	xt_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf));
+
+	memset(buf, 0, sizeof(buf));
+
+	if (del)
+		nfnl_fill_hdr(nfnlssh, nmh, 0, AF_UNSPEC, 0, OSF_MSG_REMOVE, NLM_F_REQUEST);
+	else
+		nfnl_fill_hdr(nfnlssh, nmh, 0, AF_UNSPEC, 0, OSF_MSG_ADD, NLM_F_REQUEST | NLM_F_CREATE);
+
+	nfnl_addattr_l(nmh, sizeof(buf), OSF_ATTR_FINGER, &f, sizeof(struct xt_osf_user_finger));
+
+	return nfnl_talk(nfnlh, nmh, 0, 0, NULL, NULL, NULL);
+}
+
+static int osf_load_entries(char *path, int del)
+{
+	FILE *inf;
+	int err = 0;
+	char buf[1024];
+
+	inf = fopen(path, "r");
+	if (!inf) {
+		ulog_err("Failed to open file '%s'", path);
+		return -1;
+	}
+
+	while(fgets(buf, sizeof(buf), inf)) {
+		int len;
+
+		if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r')
+			continue;
+
+		len = strlen(buf) - 1;
+
+		if (len <= 0)
+			continue;
+
+		buf[len] = '\0';
+
+		err = osf_load_line(buf, len, del);
+		if (err)
+			break;
+
+		memset(buf, 0, sizeof(buf));
+	}
+
+	fclose(inf);
+	return err;
+}
+
+int main(int argc, char *argv[])
+{
+	int ch, del = 0, err;
+	char *fingerprints = NULL;
+
+	while ((ch = getopt(argc, argv, "f:dh")) != -1) {
+		switch (ch) {
+			case 'f':
+				fingerprints = optarg;
+				break;
+			case 'd':
+				del = 1;
+				break;
+			default:
+				fprintf(stderr,
+					"Usage: %s -f fingerprints -d <del rules> -h\n",
+					argv[0]);
+				return -1;
+		}
+	}
+
+	if (!fingerprints) {
+		err = -ENOENT;
+		goto err_out_exit;
+	}
+
+	nfnlh = nfnl_open();
+	if (!nfnlh) {
+		err = -EINVAL;
+		ulog_err("Failed to create nfnl handler");
+		goto err_out_exit;
+	}
+
+#ifndef NFNL_SUBSYS_OSF
+#define NFNL_SUBSYS_OSF	5
+#endif
+
+	nfnlssh = nfnl_subsys_open(nfnlh, NFNL_SUBSYS_OSF, OSF_MSG_MAX, 0);
+	if (!nfnlssh) {
+		err = -EINVAL;
+		ulog_err("Faied to create nfnl subsystem");
+		goto err_out_close;
+	}
+
+	err = osf_load_entries(fingerprints, del);
+	if (err)
+		goto err_out_close_subsys;
+
+	nfnl_subsys_close(nfnlssh);
+	nfnl_close(nfnlh);
+
+	return 0;
+
+err_out_close_subsys:
+	nfnl_subsys_close(nfnlssh);
+err_out_close:
+	nfnl_close(nfnlh);
+err_out_exit:
+	return err;
+}
diff --git a/utils/nfsynproxy.c b/utils/nfsynproxy.c
new file mode 100644
index 0000000..baedc92
--- /dev/null
+++ b/utils/nfsynproxy.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2013 Patrick McHardy <kaber@trash.net>
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pcap/pcap.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+static const char *iface = "lo";
+static uint16_t port;
+static const char *chain = "SYNPROXY";
+
+static int parse_packet(const char *host, const uint8_t *data)
+{
+	const struct iphdr *iph = (void *)data + 14;
+	const struct tcphdr *th = (void *)iph + iph->ihl * 4;
+	int length;
+	uint8_t *ptr;
+
+	if (!th->syn || !th->ack)
+		return 0;
+
+	printf("-A %s -d %s -p tcp --dport %u "
+	       "-m state --state UNTRACKED,INVALID "
+	       "-j SYNPROXY ", chain, host, port);
+
+	/* ECE && !CWR */
+	if (th->res2 == 0x1)
+		printf("--ecn ");
+
+	length = th->doff * 4 - sizeof(*th);
+	ptr = (uint8_t *)(th + 1);
+	while (length > 0) {
+		int opcode = *ptr++;
+		int opsize;
+
+		switch (opcode) {
+		case TCPOPT_EOL:
+			return 1;
+		case TCPOPT_NOP:
+			length--;
+			continue;
+		default:
+			opsize = *ptr++;
+			if (opsize < 2)
+				return 1;
+			if (opsize > length)
+				return 1;
+
+			switch (opcode) {
+			case TCPOPT_MAXSEG:
+				if (opsize == TCPOLEN_MAXSEG)
+					printf("--mss %u ", ntohs(*(uint16_t *)ptr));
+				break;
+			case TCPOPT_WINDOW:
+				if (opsize == TCPOLEN_WINDOW)
+					printf("--wscale %u ", *ptr);
+				break;
+			case TCPOPT_TIMESTAMP:
+				if (opsize == TCPOLEN_TIMESTAMP)
+					printf("--timestamp ");
+				break;
+			case TCPOPT_SACK_PERMITTED:
+				if (opsize == TCPOLEN_SACK_PERMITTED)
+					printf("--sack-perm ");
+				break;
+			}
+
+			ptr += opsize - 2;
+			length -= opsize;
+		}
+	}
+	printf("\n");
+	return 1;
+}
+
+static void probe_host(const char *host)
+{
+	struct sockaddr_in sin;
+	char pcap_errbuf[PCAP_ERRBUF_SIZE];
+	struct pcap_pkthdr pkthdr;
+	const uint8_t *data;
+	struct bpf_program fp;
+	pcap_t *ph;
+	int fd;
+
+	ph = pcap_create(iface, pcap_errbuf);
+	if (ph == NULL) {
+		perror("pcap_create");
+		goto err1;
+	}
+
+	if (pcap_setnonblock(ph, 1, pcap_errbuf) == -1) {
+		perror("pcap_setnonblock");
+		goto err2;
+	}
+
+	if (pcap_setfilter(ph, &fp) == -1) {
+		pcap_perror(ph, "pcap_setfilter");
+		goto err2;
+	}
+
+	if (pcap_activate(ph) != 0) {
+		pcap_perror(ph, "pcap_activate");
+		goto err2;
+	}
+
+	if (pcap_compile(ph, &fp, "src host 127.0.0.1 and tcp and src port 80",
+			 1, PCAP_NETMASK_UNKNOWN) == -1) {
+		pcap_perror(ph, "pcap_compile");
+		goto err2;
+	}
+
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	if (fd < 0) {
+		perror("socket");
+		goto err3;
+	}
+
+	memset(&sin, 0, sizeof(sin));
+	sin.sin_family		= AF_INET;
+	sin.sin_port		= htons(port);
+	sin.sin_addr.s_addr	= inet_addr(host);
+
+	if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+		perror("connect");
+		goto err4;
+	}
+
+	for (;;) {
+		data = pcap_next(ph, &pkthdr);
+		if (data == NULL)
+			break;
+		if (parse_packet(host, data))
+			break;
+	}
+
+	close(fd);
+
+err4:
+	close(fd);
+err3:
+	pcap_freecode(&fp);
+err2:
+	pcap_close(ph);
+err1:
+	return;
+}
+
+enum {
+	OPT_HELP	= 'h',
+	OPT_IFACE	= 'i',
+	OPT_PORT	= 'p',
+	OPT_CHAIN	= 'c',
+};
+
+static const struct option options[] = {
+	{ .name = "help",  .has_arg = false, .val = OPT_HELP },
+	{ .name = "iface", .has_arg = true,  .val = OPT_IFACE },
+	{ .name = "port" , .has_arg = true,  .val = OPT_PORT },
+	{ .name = "chain", .has_arg = true,  .val = OPT_CHAIN },
+	{ }
+};
+
+static void print_help(const char *name)
+{
+	printf("%s [ options ] address...\n"
+	       "\n"
+	       "Options:\n"
+	       " -i/--iface        Outbound interface\n"
+	       " -p/--port         Port number to probe\n"
+	       " -c/--chain        Chain name to use for rules\n"
+	       " -h/--help         Show this help\n",
+	       name);
+}
+
+int main(int argc, char **argv)
+{
+	int optidx = 0, c;
+
+	for (;;) {
+		c = getopt_long(argc, argv, "hi:p:c:", options, &optidx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case OPT_IFACE:
+			iface = optarg;
+			break;
+		case OPT_PORT:
+			port = atoi(optarg);
+			break;
+		case OPT_CHAIN:
+			chain = optarg;
+			break;
+		case OPT_HELP:
+			print_help(argv[0]);
+			exit(0);
+		case '?':
+			print_help(argv[0]);
+			exit(1);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	while (argc > 0) {
+		probe_host(*argv);
+		argc--;
+		argv++;
+	}
+	return 0;
+}
diff --git a/utils/pf.os b/utils/pf.os
new file mode 100644
index 0000000..44e0014
--- /dev/null
+++ b/utils/pf.os
@@ -0,0 +1,687 @@
+# $OpenBSD: pf.os,v 1.20 2006/06/02 16:54:34 david Exp $
+# passive OS fingerprinting
+# -------------------------
+#
+# SYN signatures. Those signatures work for SYN packets only (duh!).
+#
+# (C) Copyright 2000-2003 by Michal Zalewski <lcamtuf@coredump.cx>
+# (C) Copyright 2003 by Mike Frantzen <frantzen@w4g.org>
+#
+#  Permission to use, copy, modify, and distribute this software for any
+#  purpose with or without fee is hereby granted, provided that the above
+#  copyright notice and this permission notice appear in all copies.
+#
+#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# This fingerprint database is adapted from Michal Zalewski's p0f passive
+# operating system package.  The last database sync was from a Nov 3 2003
+# p0f.fp.
+#
+#
+# Each line in this file specifies a single fingerprint. Please read the
+# information below carefully before attempting to append any signatures
+# reported as UNKNOWN to this file to avoid mistakes.
+#
+# We use the following set metrics for fingerprinting:
+#
+# - Window size (WSS) - a highly OS dependent setting used for TCP/IP
+#   performance control (max. amount of data to be sent without ACK).
+#   Some systems use a fixed value for initial packets. On other
+#   systems, it is a multiple of MSS or MTU (MSS+40). In some rare
+#   cases, the value is just arbitrary.
+#
+#   NEW SIGNATURE: if p0f reported a special value of 'Snn', the number
+#   appears to be a multiple of MSS (MSS*nn); a special value of 'Tnn'
+#   means it is a multiple of MTU ((MSS+40)*nn). Unless you notice the
+#   value of nn is not fixed (unlikely), just copy the Snn or Tnn token
+#   literally. If you know this device has a simple stack and a fixed
+#   MTU, you can however multiply S value by MSS, or T value by MSS+40,
+#   and put it instead of Snn or Tnn.
+#
+#   If WSS otherwise looks like a fixed value (for example a multiple
+#   of two), or if you can confirm the value is fixed, please quote
+#   it literally. If there's no apparent pattern in WSS chosen, you
+#   should consider wildcarding this value.
+#
+# - Overall packet size - a function of all IP and TCP options and bugs.
+#
+#   NEW SIGNATURE: Copy this value literally.
+#
+# - Initial TTL - We check the actual TTL of a received packet. It can't
+#   be higher than the initial TTL, and also shouldn't be dramatically
+#   lower (maximum distance is defined as 40 hops).
+#
+#   NEW SIGNATURE: *Never* copy TTL from a p0f-reported signature literally.
+#   You need to determine the initial TTL. The best way to do it is to
+#   check the documentation for a remote system, or check its settings.
+#   A fairly good method is to simply round the observed TTL up to
+#   32, 64, 128, or 255, but it should be noted that some obscure devices
+#   might not use round TTLs (in particular, some shoddy appliances use
+#   "original" initial TTL settings). If not sure, you can see how many
+#   hops you're away from the remote party with traceroute or mtr.
+#
+# - Don't fragment flag (DF) - some modern OSes set this to implement PMTU
+#   discovery. Others do not bother.
+#
+#   NEW SIGNATURE: Copy this value literally.
+#
+# - Maximum segment size (MSS) - this setting is usually link-dependent. P0f
+#   uses it to determine link type of the remote host.
+#
+#   NEW SIGNATURE: Always wildcard this value, except for rare cases when
+#   you have an appliance with a fixed value, know the system supports only
+#   a very limited number of network interface types, or know the system
+#   is using a value it pulled out of nowhere.  Specific unique MSS
+#   can be used to tell Google crawlbots from the rest of the population.
+#
+# - Window scaling (WSCALE) - this feature is used to scale WSS.
+#   It extends the size of a TCP/IP window to 32 bits. Some modern
+#   systems implement this feature.
+#
+#   NEW SIGNATURE: Observe several signatures. Initial WSCALE is often set
+#   to zero or other low value. There's usually no need to wildcard this
+#   parameter.
+#
+# - Timestamp - some systems that implement timestamps set them to
+#   zero in the initial SYN. This case is detected and handled appropriately.
+#
+# - Selective ACK permitted - a flag set by systems that implement
+#   selective ACK functionality.
+#
+# - The sequence of TCP all options (MSS, window scaling, selective ACK
+#   permitted, timestamp, NOP). Other than the options previously
+#   discussed, p0f also checks for timestamp option (a silly
+#   extension to broadcast your uptime ;-), NOP options (used for
+#   header padding) and sackOK option (selective ACK feature).
+#
+#   NEW SIGNATURE: Copy the sequence literally.
+#
+# To wildcard any value (except for initial TTL or TCP options), replace
+# it with '*'. You can also use a modulo operator to match any values
+# that divide by nnn - '%nnn'.
+#
+# Fingerprint entry format:
+#
+# wwww:ttt:D:ss:OOO...:OS:Version:Subtype:Details
+#
+# wwww     - window size (can be *, %nnn, Snn or Tnn).  The special values
+#            "S" and "T" which are a multiple of MSS or a multiple of MTU
+#            respectively.
+# ttt      - initial TTL
+# D        - don't fragment bit (0 - not set, 1 - set)
+# ss       - overall SYN packet size
+# OOO      - option value and order specification (see below)
+# OS       - OS genre (Linux, Solaris, Windows)
+# Version  - OS Version (2.0.27 on x86, etc)
+# Subtype  - OS subtype or patchlevel (SP3, lo0)
+# details  - Generic OS details
+#
+# If OS genre starts with '*', p0f will not show distance, link type
+# and timestamp data. It is useful for userland TCP/IP stacks of
+# network scanners and so on, where many settings are randomized or
+# bogus.
+#
+# If OS genre starts with @, it denotes an approximate hit for a group
+# of operating systems (signature reporting still enabled in this case).
+# Use this feature at the end of this file to catch cases for which
+# you don't have a precise match, but can tell it's Windows or FreeBSD
+# or whatnot by looking at, say, flag layout alone.
+#
+# Option block description is a list of comma or space separated
+# options in the order they appear in the packet:
+#
+# N	   - NOP option
+# Wnnn	   - window scaling option, value nnn (or * or %nnn)
+# Mnnn	   - maximum segment size option, value nnn (or * or %nnn)
+# S	   - selective ACK OK
+# T	   - timestamp
+# T0	   - timestamp with a zero value
+#
+# To denote no TCP options, use a single '.'.
+#
+# Please report any additions to this file, or any inaccuracies or
+# problems spotted, to the maintainers: lcamtuf@coredump.cx,
+# frantzen@openbsd.org and bugs@openbsd.org with a tcpdump packet
+# capture of the relevant SYN packet(s)
+#
+# A test and submission page is available at
+# http://lcamtuf.coredump.cx/p0f-help/
+#
+#
+# WARNING WARNING WARNING
+# -----------------------
+#
+# Do not add a system X as OS Y just because NMAP says so. It is often
+# the case that X is a NAT firewall. While nmap is talking to the
+# device itself, p0f is fingerprinting the guy behind the firewall
+# instead.
+#
+# When in doubt, use common sense, don't add something that looks like
+# a completely different system as Linux or FreeBSD or LinkSys router.
+# Check DNS name, establish a connection to the remote host and look
+# at SYN+ACK - does it look similar?
+#
+# Some users tweak their TCP/IP settings - enable or disable RFC1323
+# functionality, enable or disable timestamps or selective ACK,
+# disable PMTU discovery, change MTU and so on. Always compare a new rule
+# to other fingerprints for this system, and verify the system isn't
+# "customized" before adding it. It is OK to add signature variants
+# caused by a commonly used software (personal firewalls, security
+# packages, etc), but it makes no sense to try to add every single
+# possible /proc/sys/net/ipv4 tweak on Linux or so.
+#
+# KEEP IN MIND: Some packet firewalls configured to normalize outgoing
+# traffic (OpenBSD pf with "scrub" enabled, for example) will, well,
+# normalize packets. Signatures will not correspond to the originating
+# system (and probably not quite to the firewall either).
+#
+# NOTE: Try to keep this file in some reasonable order, from most to
+# least likely systems. This will speed up operation. Also keep most
+# generic and broad rules near the end.
+#
+
+##########################
+# Standard OS signatures #
+##########################
+
+# ----------------- AIX ---------------------
+
+# AIX is first because its signatures are close to NetBSD, MacOS X and
+# Linux 2.0, but it uses a fairly rare MSSes, at least sometimes...
+# This is a shoddy hack, though.
+
+45046:64:0:44:M*:		AIX:4.3::AIX 4.3
+16384:64:0:44:M512:		AIX:4.3:2-3:AIX 4.3.2 and earlier
+
+16384:64:0:60:M512,N,W%2,N,N,T:		AIX:4.3:3:AIX 4.3.3-5.2
+16384:64:0:60:M512,N,W%2,N,N,T:		AIX:5.1-5.2::AIX 4.3.3-5.2
+32768:64:0:60:M512,N,W%2,N,N,T:		AIX:4.3:3:AIX 4.3.3-5.2
+32768:64:0:60:M512,N,W%2,N,N,T:		AIX:5.1-5.2::AIX 4.3.3-5.2
+65535:64:0:60:M512,N,W%2,N,N,T:		AIX:4.3:3:AIX 4.3.3-5.2
+65535:64:0:60:M512,N,W%2,N,N,T:		AIX:5.1-5.2::AIX 4.3.3-5.2
+65535:64:0:64:M*,N,W1,N,N,T,N,N,S:	AIX:5.3:ML1:AIX 5.3 ML1
+
+# ----------------- Linux -------------------
+
+# S1:64:0:44:M*:A:		Linux:1.2::Linux 1.2.x (XXX quirks support)
+512:64:0:44:M*:			Linux:2.0:3x:Linux 2.0.3x
+16384:64:0:44:M*:		Linux:2.0:3x:Linux 2.0.3x
+
+# Endian snafu! Nelson says "ha-ha":
+2:64:0:44:M*:			Linux:2.0:3x:Linux 2.0.3x (MkLinux) on Mac
+64:64:0:44:M*:			Linux:2.0:3x:Linux 2.0.3x (MkLinux) on Mac
+
+
+S4:64:1:60:M1360,S,T,N,W0:	Linux:google::Linux (Google crawlbot)
+
+S2:64:1:60:M*,S,T,N,W0:		Linux:2.4::Linux 2.4 (big boy)
+S3:64:1:60:M*,S,T,N,W0:		Linux:2.4:.18-21:Linux 2.4.18 and newer
+S4:64:1:60:M*,S,T,N,W0:		Linux:2.4::Linux 2.4/2.6 <= 2.6.7
+S4:64:1:60:M*,S,T,N,W0:		Linux:2.6:.1-7:Linux 2.4/2.6 <= 2.6.7
+S4:64:1:60:M*,S,T,N,W7:		Linux:2.6:8:Linux 2.6.8 and newer (?)
+
+S3:64:1:60:M*,S,T,N,W1:		Linux:2.5::Linux 2.5 (sometimes 2.4)
+S4:64:1:60:M*,S,T,N,W1:		Linux:2.5-2.6::Linux 2.5/2.6
+S3:64:1:60:M*,S,T,N,W2:		Linux:2.5::Linux 2.5 (sometimes 2.4)
+S4:64:1:60:M*,S,T,N,W2:		Linux:2.5::Linux 2.5 (sometimes 2.4)
+
+S20:64:1:60:M*,S,T,N,W0:	Linux:2.2:20-25:Linux 2.2.20 and newer
+S22:64:1:60:M*,S,T,N,W0:	Linux:2.2::Linux 2.2
+S11:64:1:60:M*,S,T,N,W0:	Linux:2.2::Linux 2.2
+
+# Popular cluster config scripts disable timestamps and
+# selective ACK:
+S4:64:1:48:M1460,N,W0:		Linux:2.4:cluster:Linux 2.4 in cluster
+
+# This needs to be investigated. On some systems, WSS
+# is selected as a multiple of MTU instead of MSS. I got
+# many submissions for this for many late versions of 2.4:
+T4:64:1:60:M1412,S,T,N,W0:	Linux:2.4::Linux 2.4 (late, uncommon)
+
+# This happens only over loopback, but let's make folks happy:
+32767:64:1:60:M16396,S,T,N,W0:	Linux:2.4:lo0:Linux 2.4 (local)
+S8:64:1:60:M3884,S,T,N,W0:	Linux:2.2:lo0:Linux 2.2 (local)
+
+# Opera visitors:
+16384:64:1:60:M*,S,T,N,W0:	Linux:2.2:Opera:Linux 2.2 (Opera?)
+32767:64:1:60:M*,S,T,N,W0:	Linux:2.4:Opera:Linux 2.4 (Opera?)
+
+# Some fairly common mods:
+S4:64:1:52:M*,N,N,S,N,W0:	Linux:2.4:ts:Linux 2.4 w/o timestamps
+S22:64:1:52:M*,N,N,S,N,W0:	Linux:2.2:ts:Linux 2.2 w/o timestamps
+
+
+# ----------------- FreeBSD -----------------
+
+16384:64:1:44:M*:		FreeBSD:2.0-2.2::FreeBSD 2.0-4.2
+16384:64:1:44:M*:		FreeBSD:3.0-3.5::FreeBSD 2.0-4.2
+16384:64:1:44:M*:		FreeBSD:4.0-4.2::FreeBSD 2.0-4.2
+16384:64:1:60:M*,N,W0,N,N,T:	FreeBSD:4.4::FreeBSD 4.4
+
+1024:64:1:60:M*,N,W0,N,N,T:	FreeBSD:4.4::FreeBSD 4.4
+
+57344:64:1:44:M*:		FreeBSD:4.6-4.8:noRFC1323:FreeBSD 4.6-4.8 (no RFC1323)
+57344:64:1:60:M*,N,W0,N,N,T:	FreeBSD:4.6-4.9::FreeBSD 4.6-4.9
+
+32768:64:1:60:M*,N,W0,N,N,T:	FreeBSD:4.8-4.11::FreeBSD 4.8-5.1 (or MacOS X)
+32768:64:1:60:M*,N,W0,N,N,T:	FreeBSD:5.0-5.1::FreeBSD 4.8-5.1 (or MacOS X)
+65535:64:1:60:M*,N,W0,N,N,T:	FreeBSD:4.8-4.11::FreeBSD 4.8-5.2 (or MacOS X)
+65535:64:1:60:M*,N,W0,N,N,T:	FreeBSD:5.0-5.2::FreeBSD 4.8-5.2 (or MacOS X)
+65535:64:1:60:M*,N,W1,N,N,T:	FreeBSD:4.7-4.11::FreeBSD 4.7-5.2
+65535:64:1:60:M*,N,W1,N,N,T:	FreeBSD:5.0-5.2::FreeBSD 4.7-5.2
+
+# XXX need quirks support
+# 65535:64:1:60:M*,N,W0,N,N,T:Z:FreeBSD:5.1-5.4::5.1-current (1)
+# 65535:64:1:60:M*,N,W1,N,N,T:Z:FreeBSD:5.1-5.4::5.1-current (2)
+# 65535:64:1:60:M*,N,W2,N,N,T:Z:FreeBSD:5.1-5.4::5.1-current (3)
+# 65535:64:1:44:M*:Z:FreeBSD:5.2::FreeBSD 5.2 (no RFC1323)
+
+# 16384:64:1:60:M*,N,N,N,N,N,N,T:FreeBSD:4.4:noTS:FreeBSD 4.4 (w/o timestamps)
+
+# ----------------- NetBSD ------------------
+
+16384:64:0:60:M*,N,W0,N,N,T:	NetBSD:1.3::NetBSD 1.3
+65535:64:0:60:M*,N,W0,N,N,T0:	NetBSD:1.6:opera:NetBSD 1.6 (Opera)
+16384:64:0:60:M*,N,W0,N,N,T0:	NetBSD:1.6::NetBSD 1.6
+16384:64:1:60:M*,N,W0,N,N,T0:	NetBSD:1.6:df:NetBSD 1.6 (DF)
+65535:64:1:60:M*,N,W1,N,N,T0:	NetBSD:1.6::NetBSD 1.6W-current (DF)
+65535:64:1:60:M*,N,W0,N,N,T0:	NetBSD:1.6::NetBSD 1.6X (DF)
+32768:64:1:60:M*,N,W0,N,N,T0:	NetBSD:1.6:randomization:NetBSD 1.6ZH-current (w/ ip_id randomization)
+
+# ----------------- OpenBSD -----------------
+
+16384:64:0:60:M*,N,W0,N,N,T:		OpenBSD:2.6::NetBSD 1.3 (or OpenBSD 2.6)
+16384:64:1:64:M*,N,N,S,N,W0,N,N,T:	OpenBSD:3.0-3.9::OpenBSD 3.0-3.9
+16384:64:0:64:M*,N,N,S,N,W0,N,N,T:	OpenBSD:3.0-3.9:no-df:OpenBSD 3.0-3.9 (scrub no-df)
+57344:64:1:64:M*,N,N,S,N,W0,N,N,T:	OpenBSD:3.3-3.9::OpenBSD 3.3-3.9
+57344:64:0:64:M*,N,N,S,N,W0,N,N,T:	OpenBSD:3.3-3.9:no-df:OpenBSD 3.3-3.9 (scrub no-df)
+
+65535:64:1:64:M*,N,N,S,N,W0,N,N,T:	OpenBSD:3.0-3.9:opera:OpenBSD 3.0-3.9 (Opera)
+
+# ----------------- Solaris -----------------
+
+S17:64:1:64:N,W3,N,N,T0,N,N,S,M*:	Solaris:8:RFC1323:Solaris 8 RFC1323
+S17:64:1:48:N,N,S,M*:			Solaris:8::Solaris 8
+S17:255:1:44:M*:			Solaris:2.5-2.7::Solaris 2.5 to 7
+
+S6:255:1:44:M*:				Solaris:2.6-2.7::Solaris 2.6 to 7
+S23:255:1:44:M*:			Solaris:2.5:1:Solaris 2.5.1
+S34:64:1:48:M*,N,N,S:			Solaris:2.9::Solaris 9
+S44:255:1:44:M*:			Solaris:2.7::Solaris 7
+
+4096:64:0:44:M1460:			SunOS:4.1::SunOS 4.1.x
+
+S34:64:1:52:M*,N,W0,N,N,S:		Solaris:10:beta:Solaris 10 (beta)
+32850:64:1:64:M*,N,N,T,N,W1,N,N,S:	Solaris:10::Solaris 10 1203
+
+# ----------------- IRIX --------------------
+
+49152:64:0:44:M*:			IRIX:6.4::IRIX 6.4
+61440:64:0:44:M*:			IRIX:6.2-6.5::IRIX 6.2-6.5
+49152:64:0:52:M*,N,W2,N,N,S:		IRIX:6.5:RFC1323:IRIX 6.5 (RFC1323)
+49152:64:0:52:M*,N,W3,N,N,S:		IRIX:6.5:RFC1323:IRIX 6.5 (RFC1323)
+
+61440:64:0:48:M*,N,N,S:			IRIX:6.5:12-21:IRIX 6.5.12 - 6.5.21
+49152:64:0:48:M*,N,N,S:			IRIX:6.5:15-21:IRIX 6.5.15 - 6.5.21
+
+49152:60:0:64:M*,N,W2,N,N,T,N,N,S:	IRIX:6.5:IP27:IRIX 6.5 IP27
+
+
+# ----------------- Tru64 -------------------
+
+32768:64:1:48:M*,N,W0:			Tru64:4.0::Tru64 4.0 (or OS/2 Warp 4)
+32768:64:0:48:M*,N,W0:			Tru64:5.0::Tru64 5.0
+8192:64:0:44:M1460:			Tru64:5.1:noRFC1323:Tru64 6.1 (no RFC1323) (or QNX 6)
+61440:64:0:48:M*,N,W0:			Tru64:5.1a:JP4:Tru64 v5.1a JP4 (or OpenVMS 7.x on Compaq 5.x stack)
+
+# ----------------- OpenVMS -----------------
+
+6144:64:1:60:M*,N,W0,N,N,T:		OpenVMS:7.2::OpenVMS 7.2 (Multinet 4.4 stack)
+
+# ----------------- MacOS -------------------
+
+# XXX Need EOL tcp opt support
+# S2:255:1:48:M*,W0,E:.:MacOS:8.6 classic
+
+# XXX some of these use EOL too
+16616:255:1:48:M*,W0:			MacOS:7.3-7.6:OTTCP:MacOS 7.3-8.6 (OTTCP)
+16616:255:1:48:M*,W0:			MacOS:8.0-8.6:OTTCP:MacOS 7.3-8.6 (OTTCP)
+16616:255:1:48:M*,N,N,N:		MacOS:8.1-8.6:OTTCP:MacOS 8.1-8.6 (OTTCP)
+32768:255:1:48:M*,W0,N:			MacOS:9.0-9.2::MacOS 9.0-9.2
+65535:255:1:48:M*,N,N,N,N:		MacOS:9.1::MacOS 9.1 (OT 2.7.4)
+65535:64:1:64:M*,N,W0,N,N,T,S,E,E:	MacOS:10::MacOS X
+
+
+# ----------------- Windows -----------------
+
+# Windows TCP/IP stack is a mess. For most recent XP, 2000 and
+# even 98, the pathlevel, not the actual OS version, is more
+# relevant to the signature. They share the same code, so it would
+# seem. Luckily for us, almost all Windows 9x boxes have an
+# awkward MSS of 536, which I use to tell one from another
+# in most difficult cases.
+
+8192:32:1:44:M*:			Windows:3.11::Windows 3.11 (Tucows)
+S44:64:1:64:M*,N,W0,N,N,T0,N,N,S:	Windows:95::Windows 95
+8192:128:1:64:M*,N,W0,N,N,T0,N,N,S:	Windows:95:b:Windows 95b
+
+# There were so many tweaking tools and so many stack versions for
+# Windows 98 it is no longer possible to tell them from each other
+# without some very serious research. Until then, there's an insane
+# number of signatures, for your amusement:
+
+S44:32:1:48:M*,N,N,S:			Windows:98:lowTTL:Windows 98 (low TTL)
+8192:32:1:48:M*,N,N,S:			Windows:98:lowTTL:Windows 98 (low TTL)
+%8192:64:1:48:M536,N,N,S:		Windows:98::Windows 98
+%8192:128:1:48:M536,N,N,S:		Windows:98::Windows 98
+S4:64:1:48:M*,N,N,S:			Windows:98::Windows 98
+S6:64:1:48:M*,N,N,S:			Windows:98::Windows 98
+S12:64:1:48:M*,N,N,S:			Windows:98::Windows 98
+T30:64:1:64:M1460,N,W0,N,N,T0,N,N,S:	Windows:98::Windows 98
+32767:64:1:48:M*,N,N,S:			Windows:98::Windows 98
+37300:64:1:48:M*,N,N,S:			Windows:98::Windows 98
+46080:64:1:52:M*,N,W3,N,N,S:		Windows:98:RFC1323:Windows 98 (RFC1323)
+65535:64:1:44:M*:			Windows:98:noSack:Windows 98 (no sack)
+S16:128:1:48:M*,N,N,S:			Windows:98::Windows 98
+S16:128:1:64:M*,N,W0,N,N,T0,N,N,S:	Windows:98::Windows 98
+S26:128:1:48:M*,N,N,S:			Windows:98::Windows 98
+T30:128:1:48:M*,N,N,S:			Windows:98::Windows 98
+32767:128:1:52:M*,N,W0,N,N,S:		Windows:98::Windows 98
+60352:128:1:48:M*,N,N,S:		Windows:98::Windows 98
+60352:128:1:64:M*,N,W2,N,N,T0,N,N,S:	Windows:98::Windows 98
+
+# What's with 1414 on NT?
+T31:128:1:44:M1414:			Windows:NT:4.0:Windows NT 4.0 SP6a
+64512:128:1:44:M1414:			Windows:NT:4.0:Windows NT 4.0 SP6a
+8192:128:1:44:M*:			Windows:NT:4.0:Windows NT 4.0 (older)
+
+# Windows XP and 2000. Most of the signatures that were
+# either dubious or non-specific (no service pack data)
+# were deleted and replaced with generics at the end.
+
+65535:128:1:48:M*,N,N,S:		Windows:2000:SP4:Windows 2000 SP4, XP SP1
+65535:128:1:48:M*,N,N,S:		Windows:XP:SP1:Windows 2000 SP4, XP SP1
+%8192:128:1:48:M*,N,N,S:		Windows:2000:SP2+:Windows 2000 SP2, XP SP1 (seldom 98 4.10.2222)
+%8192:128:1:48:M*,N,N,S:		Windows:XP:SP1:Windows 2000 SP2, XP SP1 (seldom 98 4.10.2222)
+S20:128:1:48:M*,N,N,S:			Windows:2000::Windows 2000/XP SP3
+S20:128:1:48:M*,N,N,S:			Windows:XP:SP3:Windows 2000/XP SP3
+S45:128:1:48:M*,N,N,S:			Windows:2000:SP4:Windows 2000 SP4, XP SP 1
+S45:128:1:48:M*,N,N,S:			Windows:XP:SP1:Windows 2000 SP4, XP SP 1
+40320:128:1:48:M*,N,N,S:		Windows:2000:SP4:Windows 2000 SP4
+
+S6:128:1:48:M*,N,N,S:			Windows:2000:SP2:Windows XP, 2000 SP2+
+S6:128:1:48:M*,N,N,S:			Windows:XP::Windows XP, 2000 SP2+
+S12:128:1:48:M*,N,N,S:			Windows:XP:SP1:Windows XP SP1
+S44:128:1:48:M*,N,N,S:			Windows:2000:SP3:Windows Pro SP1, 2000 SP3
+S44:128:1:48:M*,N,N,S:			Windows:XP:SP1:Windows Pro SP1, 2000 SP3
+64512:128:1:48:M*,N,N,S:		Windows:2000:SP3:Windows SP1, 2000 SP3
+64512:128:1:48:M*,N,N,S:		Windows:XP:SP1:Windows SP1, 2000 SP3
+32767:128:1:48:M*,N,N,S:		Windows:2000:SP4:Windows SP1, 2000 SP4
+32767:128:1:48:M*,N,N,S:		Windows:XP:SP1:Windows SP1, 2000 SP4
+
+# Odds, ends, mods:
+
+S52:128:1:48:M1260,N,N,S:		Windows:2000:cisco:Windows XP/2000 via Cisco
+S52:128:1:48:M1260,N,N,S:		Windows:XP:cisco:Windows XP/2000 via Cisco
+65520:128:1:48:M*,N,N,S:		Windows:XP::Windows XP bare-bone
+16384:128:1:52:M536,N,W0,N,N,S:		Windows:2000:ZoneAlarm:Windows 2000 w/ZoneAlarm?
+2048:255:0:40:.:			Windows:.NET::Windows .NET Enterprise Server
+
+44620:64:0:48:M*,N,N,S:			Windows:ME::Windows ME no SP (?)
+S6:255:1:48:M536,N,N,S:			Windows:95:winsock2:Windows 95 winsock 2
+32768:32:1:52:M1460,N,W0,N,N,S:		Windows:2003:AS:Windows 2003 AS
+
+
+# No need to be more specific, it passes:
+# *:128:1:48:M*,N,N,S:U:-Windows:XP/2000 while downloading (leak!) XXX quirk
+# there is an equiv similar generic sig w/o the quirk
+
+# ----------------- HP/UX -------------------
+
+32768:64:1:44:M*:			HP-UX:B.10.20::HP-UX B.10.20
+32768:64:0:48:M*,W0,N:			HP-UX:11.0::HP-UX 11.0
+32768:64:1:48:M*,W0,N:			HP-UX:11.10::HP-UX 11.0 or 11.11
+32768:64:1:48:M*,W0,N:			HP-UX:11.11::HP-UX 11.0 or 11.11
+
+# Whoa. Hardcore WSS.
+0:64:0:48:M*,W0,N:			HP-UX:B.11.00:A:HP-UX B.11.00 A (RFC1323)
+
+# ----------------- RiscOS ------------------
+
+# We don't yet support the ?12 TCP option
+#16384:64:1:68:M1460,N,W0,N,N,T,N,N,?12:	RISCOS:3.70-4.36::RISC OS 3.70-4.36
+12288:32:0:44:M536:				RISC OS:3.70:4.10:RISC OS 3.70 inet 4.10
+
+# XXX quirk
+# 4096:64:1:56:M1460,N,N,T:T:			RISC OS:3.70:freenet:RISC OS 3.70 freenet 2.00
+
+
+
+# ----------------- BSD/OS ------------------
+
+# Once again, power of two WSS is also shared by MacOS X with DF set
+8192:64:1:60:M1460,N,W0,N,N,T:		BSD/OS:3.1::BSD/OS 3.1-4.3 (or MacOS X 10.2 w/DF)
+8192:64:1:60:M1460,N,W0,N,N,T:		BSD/OS:4.0-4.3::BSD/OS 3.1-4.3 (or MacOS X 10.2)
+
+
+# ---------------- NewtonOS -----------------
+
+4096:64:0:44:M1420:		NewtonOS:2.1::NewtonOS 2.1
+
+# ---------------- NeXTSTEP -----------------
+
+S4:64:0:44:M1024:		NeXTSTEP:3.3::NeXTSTEP 3.3
+S8:64:0:44:M512:		NeXTSTEP:3.3::NeXTSTEP 3.3
+
+# ------------------ BeOS -------------------
+
+1024:255:0:48:M*,N,W0:		BeOS:5.0-5.1::BeOS 5.0-5.1
+12288:255:0:44:M1402:		BeOS:5.0::BeOS 5.0.x
+
+# ------------------ OS/400 -----------------
+
+8192:64:1:60:M1440,N,W0,N,N,T:	OS/400:VR4::OS/400 VR4/R5
+8192:64:1:60:M1440,N,W0,N,N,T:	OS/400:VR5::OS/400 VR4/R5
+4096:64:1:60:M1440,N,W0,N,N,T:	OS/400:V4R5:CF67032:OS/400 V4R5 + CF67032
+
+# XXX quirk
+# 28672:64:0:44:M1460:A:OS/390:?
+
+# ------------------ ULTRIX -----------------
+
+16384:64:0:40:.:		ULTRIX:4.5::ULTRIX 4.5
+
+# ------------------- QNX -------------------
+
+S16:64:0:44:M512:		QNX:::QNX demodisk
+
+# ------------------ Novell -----------------
+
+16384:128:1:44:M1460:		Novell:NetWare:5.0:Novel Netware 5.0
+6144:128:1:44:M1460:		Novell:IntranetWare:4.11:Novell IntranetWare 4.11
+6144:128:1:44:M1368:		Novell:BorderManager::Novell BorderManager ?
+
+6144:128:1:52:M*,W0,N,S,N,N:	Novell:Netware:6:Novell Netware 6 SP3
+
+
+# ----------------- SCO ------------------
+S3:64:1:60:M1460,N,W0,N,N,T:	SCO:UnixWare:7.1:SCO UnixWare 7.1
+S17:64:1:60:M1380,N,W0,N,N,T:	SCO:UnixWare:7.1:SCO UnixWare 7.1.3 MP3
+S23:64:1:44:M1380:		SCO:OpenServer:5.0:SCO OpenServer 5.0
+
+# ------------------- DOS -------------------
+
+2048:255:0:44:M536:		DOS:WATTCP:1.05:DOS Arachne via WATTCP/1.05
+T2:255:0:44:M984:		DOS:WATTCP:1.05Arachne:Arachne via WATTCP/1.05 (eepro)
+
+# ------------------ OS/2 -------------------
+
+S56:64:0:44:M512:		OS/2:4::OS/2 4
+28672:64:0:44:M1460:		OS/2:4::OS/2 Warp 4.0
+
+# ----------------- TOPS-20 -----------------
+
+# Another hardcore MSS, one of the ACK leakers hunted down.
+# XXX QUIRK 0:64:0:44:M1460:A:TOPS-20:version 7
+0:64:0:44:M1460:		TOPS-20:7::TOPS-20 version 7
+
+# ----------------- FreeMiNT ----------------
+
+S44:255:0:44:M536:		FreeMiNT:1:16A:FreeMiNT 1 patch 16A (Atari)
+
+# ------------------ AMIGA ------------------
+
+# XXX TCP option 12
+# S32:64:1:56:M*,N,N,S,N,N,?12:.:AMIGA:3.9 BB2 with Miami stack
+
+# ------------------ Plan9 ------------------
+
+65535:255:0:48:M1460,W0,N:	Plan9:4::Plan9 edition 4
+
+# ----------------- AMIGAOS -----------------
+
+16384:64:1:48:M1560,N,N,S:	AMIGAOS:3.9::AMIGAOS 3.9 BB2 MiamiDX
+
+###########################################
+# Appliance / embedded / other signatures #
+###########################################
+
+# ---------- Firewalls / routers ------------
+
+S12:64:1:44:M1460:			@Checkpoint:::Checkpoint (unknown 1)
+S12:64:1:48:N,N,S,M1460:		@Checkpoint:::Checkpoint (unknown 2)
+4096:32:0:44:M1460:			ExtremeWare:4.x::ExtremeWare 4.x
+
+# XXX TCP option 12
+# S32:64:0:68:M512,N,W0,N,N,T,N,N,?12:.:Nokia:IPSO w/Checkpoint NG FP3
+# S16:64:0:68:M1024,N,W0,N,N,T,N,N,?12:.:Nokia:IPSO 3.7 build 026
+
+S4:64:1:60:W0,N,S,T,M1460:		FortiNet:FortiGate:50:FortiNet FortiGate 50
+
+8192:64:1:44:M1460:			Eagle:::Eagle Secure Gateway
+
+S52:128:1:48:M1260,N,N,N,N:		LinkSys:WRV54G::LinkSys WRV54G VPN router
+
+
+
+# ------- Switches and other stuff ----------
+
+4128:255:0:44:M*:			Cisco:::Cisco Catalyst 3500, 7500 etc
+S8:255:0:44:M*:				Cisco:12008::Cisco 12008
+60352:128:1:64:M1460,N,W2,N,N,T,N,N,S:	Alteon:ACEswitch::Alteon ACEswitch
+64512:128:1:44:M1370:			Nortel:Contivity Client::Nortel Conectivity Client
+
+
+# ---------- Caches and whatnots ------------
+
+S4:64:1:52:M1460,N,N,S,N,W0:		AOL:web cache::AOL web cache
+
+32850:64:1:64:N,W1,N,N,T,N,N,S,M*:	NetApp:5.x::NetApp Data OnTap 5.x
+16384:64:1:64:M1460,N,N,S,N,W0,N:	NetApp:5.3:1:NetApp 5.3.1
+65535:64:0:64:M1460,N,N,S,N,W*,N,N,T:	NetApp:5.3-5.5::NetApp 5.3-5.5
+65535:64:0:60:M1460,N,W0,N,N,T:		NetApp:CacheFlow::NetApp CacheFlow
+8192:64:1:64:M1460,N,N,S,N,W0,N,N,T:	NetApp:5.2:1:NetApp NetCache 5.2.1
+20480:64:1:64:M1460,N,N,S,N,W0,N,N,T:	NetApp:4.1::NetApp NetCache4.1
+
+65535:64:0:60:M1460,N,W0,N,N,T:		CacheFlow:4.1::CacheFlow CacheOS 4.1
+8192:64:0:60:M1380,N,N,N,N,N,N,T:	CacheFlow:1.1::CacheFlow CacheOS 1.1
+
+S4:64:0:48:M1460,N,N,S:			Cisco:Content Engine::Cisco Content Engine
+
+27085:128:0:40:.:			Dell:PowerApp cache::Dell PowerApp (Linux-based)
+
+65535:255:1:48:N,W1,M1460:		Inktomi:crawler::Inktomi crawler
+S1:255:1:60:M1460,S,T,N,W0:		LookSmart:ZyBorg::LookSmart ZyBorg
+
+16384:255:0:40:.:			Proxyblocker:::Proxyblocker (what's this?)
+
+65535:255:0:48:M*,N,N,S:		Redline:::Redline T|X 2200
+
+32696:128:0:40:M1460:			Spirent:Avalanche::Spirent Web Avalanche HTTP benchmarking engine
+
+# ----------- Embedded systems --------------
+
+S9:255:0:44:M536:			PalmOS:Tungsten:C:PalmOS Tungsten C
+S5:255:0:44:M536:			PalmOS:3::PalmOS 3/4
+S5:255:0:44:M536:			PalmOS:4::PalmOS 3/4
+S4:255:0:44:M536:			PalmOS:3:5:PalmOS 3.5
+2948:255:0:44:M536:			PalmOS:3:5:PalmOS 3.5.3 (Handera)
+S29:255:0:44:M536:			PalmOS:5::PalmOS 5.0
+16384:255:0:44:M1398:			PalmOS:5.2:Clie:PalmOS 5.2 (Clie)
+S14:255:0:44:M1350:			PalmOS:5.2:Treo:PalmOS 5.2.1 (Treo)
+
+S23:64:1:64:N,W1,N,N,T,N,N,S,M1460:	SymbianOS:7::SymbianOS 7
+
+8192:255:0:44:M1460:			SymbianOS:6048::Symbian OS 6048 (Nokia 7650?)
+8192:255:0:44:M536:			SymbianOS:9210::Symbian OS (Nokia 9210?)
+S22:64:1:56:M1460,T,S:			SymbianOS:P800::Symbian OS ? (SE P800?)
+S36:64:1:56:M1360,T,S:			SymbianOS:6600::Symbian OS 60xx (Nokia 6600?)
+
+
+# Perhaps S4?
+5840:64:1:60:M1452,S,T,N,W1:		Zaurus:3.10::Zaurus 3.10
+
+32768:128:1:64:M1460,N,W0,N,N,T0,N,N,S:	PocketPC:2002::PocketPC 2002
+
+S1:255:0:44:M346:			Contiki:1.1:rc0:Contiki 1.1-rc0
+
+4096:128:0:44:M1460:			Sega:Dreamcast:3.0:Sega Dreamcast Dreamkey 3.0
+T5:64:0:44:M536:			Sega:Dreamcast:HKT-3020:Sega Dreamcast HKT-3020 (browser disc 51027)
+S22:64:1:44:M1460:			Sony:PS2::Sony Playstation 2 (SOCOM?)
+
+S12:64:0:44:M1452:			AXIS:5600:v5.64:AXIS Printer Server 5600 v5.64
+
+3100:32:1:44:M1460:			Windows:CE:2.0:Windows CE 2.0
+
+####################
+# Fancy signatures #
+####################
+
+1024:64:0:40:.:				*NMAP:syn scan:1:NMAP syn scan (1)
+2048:64:0:40:.:				*NMAP:syn scan:2:NMAP syn scan (2)
+3072:64:0:40:.:				*NMAP:syn scan:3:NMAP syn scan (3)
+4096:64:0:40:.:				*NMAP:syn scan:4:NMAP syn scan (4)
+
+# Requires quirks support
+# 1024:64:0:40:.:A:*NMAP:TCP sweep probe (1)
+# 2048:64:0:40:.:A:*NMAP:TCP sweep probe (2)
+# 3072:64:0:40:.:A:*NMAP:TCP sweep probe (3)
+# 4096:64:0:40:.:A:*NMAP:TCP sweep probe (4)
+
+1024:64:0:60:W10,N,M265,T:		*NMAP:OS:1:NMAP OS detection probe (1)
+2048:64:0:60:W10,N,M265,T:		*NMAP:OS:2:NMAP OS detection probe (2)
+3072:64:0:60:W10,N,M265,T:		*NMAP:OS:3:NMAP OS detection probe (3)
+4096:64:0:60:W10,N,M265,T:		*NMAP:OS:4:NMAP OS detection probe (4)
+
+32767:64:0:40:.:			*NAST:::NASTsyn scan
+
+# Requires quirks support
+# 12345:255:0:40:.:A:-p0f:sendsyn utility
+
+
+#####################################
+# Generic signatures - just in case #
+#####################################
+
+#*:64:1:60:M*,N,W*,N,N,T:		@FreeBSD:4.0-4.9::FreeBSD 4.x/5.x
+#*:64:1:60:M*,N,W*,N,N,T:		@FreeBSD:5.0-5.1::FreeBSD 4.x/5.x
+
+*:128:1:52:M*,N,W0,N,N,S:		@Windows:XP:RFC1323:Windows XP/2000 (RFC1323 no tstamp)
+*:128:1:52:M*,N,W0,N,N,S:		@Windows:2000:RFC1323:Windows XP/2000 (RFC1323 no tstamp)
+*:128:1:52:M*,N,W*,N,N,S:		@Windows:XP:RFC1323:Windows XP/2000 (RFC1323 no tstamp)
+*:128:1:52:M*,N,W*,N,N,S:		@Windows:2000:RFC1323:Windows XP/2000 (RFC1323 no tstamp)
+*:128:1:64:M*,N,W0,N,N,T0,N,N,S:	@Windows:XP:RFC1323:Windows XP/2000 (RFC1323)
+*:128:1:64:M*,N,W0,N,N,T0,N,N,S:	@Windows:2000:RFC1323:Windows XP/2000 (RFC1323)
+*:128:1:64:M*,N,W*,N,N,T0,N,N,S:	@Windows:XP:RFC1323:Windows XP (RFC1323, w+)
+*:128:1:48:M536,N,N,S:			@Windows:98::Windows 98
+*:128:1:48:M*,N,N,S:			@Windows:XP::Windows XP/2000
+*:128:1:48:M*,N,N,S:			@Windows:2000::Windows XP/2000
+
+