diff --git a/.gitignore b/.gitignore
index b4fe46c0f3a15ebbd0c4ed48e5b562f9c928552d..92eb178a83bdb688b38d22fd5769303bddaf7c52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,6 @@ Makefile
 Makefile.in
 
 /include/xtables-version.h
-/include/iptables/internal.h
 
 /aclocal.m4
 /autom4te.cache/
diff --git a/Makefile.am b/Makefile.am
index b1ba015ffe8e626143bc6d05fff70384494596c5..799bf8b81c74ad6d3df64199551367e560d853e2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,4 +30,4 @@ tarball:
 	rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
 
 config.status: extensions/GNUmakefile.in \
-	include/xtables-version.h.in include/iptables/internal.h.in
+	include/xtables-version.h.in
diff --git a/Makefile.in b/Makefile.in
index 1d8a0e1ec85d85e03acda2c22204fb24aea01dba..bdbd7c9e574c25863d13595211534fa552c52403 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -93,10 +93,10 @@ host_triplet = @host@
 @ENABLE_LIBIPQ_TRUE@am__append_2 = libipq
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
@@ -106,8 +106,7 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
  configure.lineno config.status.lineno
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES = extensions/GNUmakefile \
-	include/iptables/internal.h
+CONFIG_CLEAN_FILES = extensions/GNUmakefile
 CONFIG_CLEAN_VPATH_FILES =
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -204,8 +203,7 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
 	$(top_srcdir)/build-aux/install-sh \
 	$(top_srcdir)/build-aux/ltmain.sh \
 	$(top_srcdir)/build-aux/missing \
-	$(top_srcdir)/extensions/GNUmakefile.in \
-	$(top_srcdir)/include/iptables/internal.h.in COPYING INSTALL \
+	$(top_srcdir)/extensions/GNUmakefile.in COPYING INSTALL \
 	build-aux/ar-lib build-aux/compile build-aux/config.guess \
 	build-aux/config.sub build-aux/install-sh build-aux/ltmain.sh \
 	build-aux/missing
@@ -285,9 +283,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -321,8 +316,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -367,7 +360,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
@@ -464,8 +456,6 @@ distclean-hdr:
 	-rm -f config.h stamp-h1
 extensions/GNUmakefile: $(top_builddir)/config.status $(top_srcdir)/extensions/GNUmakefile.in
 	cd $(top_builddir) && $(SHELL) ./config.status $@
-include/iptables/internal.h: $(top_builddir)/config.status $(top_srcdir)/include/iptables/internal.h.in
-	cd $(top_builddir) && $(SHELL) ./config.status $@
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -930,7 +920,7 @@ tarball:
 	rm -Rf /tmp/${PACKAGE_TARNAME}-${PACKAGE_VERSION};
 
 config.status: extensions/GNUmakefile.in \
-	include/xtables-version.h.in include/iptables/internal.h.in
+	include/xtables-version.h.in
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/aclocal.m4 b/aclocal.m4
index 2e5fe91ece49d2dcfdfa191bb8f1f52d92f336fc..4982a0fc3bf2fdc2aaf715a079e5b88eaf8da2b6 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -981,24 +981,6 @@ fi
 rmdir .tst 2>/dev/null
 AC_SUBST([am__leading_dot])])
 
-# Copyright (C) 1998-2014 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# AM_PROG_LEX
-# -----------
-# Autoconf leaves LEX=: if lex or flex can't be found.  Change that to a
-# "missing" invocation, for better error output.
-AC_DEFUN([AM_PROG_LEX],
-[AC_PREREQ([2.50])dnl
-AC_REQUIRE([AM_MISSING_HAS_RUN])dnl
-AC_REQUIRE([AC_PROG_LEX])dnl
-if test "$LEX" = :; then
-  LEX=${am_missing_run}flex
-fi])
-
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
 # Copyright (C) 2001-2014 Free Software Foundation, Inc.
@@ -1504,7 +1486,6 @@ AC_SUBST([am__tar])
 AC_SUBST([am__untar])
 ]) # _AM_PROG_TAR
 
-m4_include([m4/ax_check_linker_flags.m4])
 m4_include([m4/libtool.m4])
 m4_include([m4/ltoptions.m4])
 m4_include([m4/ltsugar.m4])
diff --git a/build-aux/ylwrap b/build-aux/ylwrap
deleted file mode 100755
index 7c2d927f7f67970737afa04378850c0c9859688f..0000000000000000000000000000000000000000
--- a/build-aux/ylwrap
+++ /dev/null
@@ -1,247 +0,0 @@
-#! /bin/sh
-# ylwrap - wrapper for lex/yacc invocations.
-
-scriptversion=2013-01-12.17; # UTC
-
-# Copyright (C) 1996-2014 Free Software Foundation, Inc.
-#
-# Written by Tom Tromey <tromey@cygnus.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, 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 to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# This file is maintained in Automake, please report
-# bugs to <bug-automake@gnu.org> or send patches to
-# <automake-patches@gnu.org>.
-
-get_dirname ()
-{
-  case $1 in
-    */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';;
-    # Otherwise,  we want the empty string (not ".").
-  esac
-}
-
-# guard FILE
-# ----------
-# The CPP macro used to guard inclusion of FILE.
-guard ()
-{
-  printf '%s\n' "$1"                                                    \
-    | sed                                                               \
-        -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'   \
-        -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'                        \
-        -e 's/__*/_/g'
-}
-
-# quote_for_sed [STRING]
-# ----------------------
-# Return STRING (or stdin) quoted to be used as a sed pattern.
-quote_for_sed ()
-{
-  case $# in
-    0) cat;;
-    1) printf '%s\n' "$1";;
-  esac \
-    | sed -e 's|[][\\.*]|\\&|g'
-}
-
-case "$1" in
-  '')
-    echo "$0: No files given.  Try '$0 --help' for more information." 1>&2
-    exit 1
-    ;;
-  --basedir)
-    basedir=$2
-    shift 2
-    ;;
-  -h|--h*)
-    cat <<\EOF
-Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
-
-Wrapper for lex/yacc invocations, renaming files as desired.
-
-  INPUT is the input file
-  OUTPUT is one file PROG generates
-  DESIRED is the file we actually want instead of OUTPUT
-  PROGRAM is program to run
-  ARGS are passed to PROG
-
-Any number of OUTPUT,DESIRED pairs may be used.
-
-Report bugs to <bug-automake@gnu.org>.
-EOF
-    exit $?
-    ;;
-  -v|--v*)
-    echo "ylwrap $scriptversion"
-    exit $?
-    ;;
-esac
-
-
-# The input.
-input=$1
-shift
-# We'll later need for a correct munging of "#line" directives.
-input_sub_rx=`get_dirname "$input" | quote_for_sed`
-case $input in
-  [\\/]* | ?:[\\/]*)
-    # Absolute path; do nothing.
-    ;;
-  *)
-    # Relative path.  Make it absolute.
-    input=`pwd`/$input
-    ;;
-esac
-input_rx=`get_dirname "$input" | quote_for_sed`
-
-# Since DOS filename conventions don't allow two dots,
-# the DOS version of Bison writes out y_tab.c instead of y.tab.c
-# and y_tab.h instead of y.tab.h. Test to see if this is the case.
-y_tab_nodot=false
-if test -f y_tab.c || test -f y_tab.h; then
-  y_tab_nodot=true
-fi
-
-# The parser itself, the first file, is the destination of the .y.c
-# rule in the Makefile.
-parser=$1
-
-# A sed program to s/FROM/TO/g for all the FROM/TO so that, for
-# instance, we rename #include "y.tab.h" into #include "parse.h"
-# during the conversion from y.tab.c to parse.c.
-sed_fix_filenames=
-
-# Also rename header guards, as Bison 2.7 for instance uses its header
-# guard in its implementation file.
-sed_fix_header_guards=
-
-while test $# -ne 0; do
-  if test x"$1" = x"--"; then
-    shift
-    break
-  fi
-  from=$1
-  # Handle y_tab.c and y_tab.h output by DOS
-  if $y_tab_nodot; then
-    case $from in
-      "y.tab.c") from=y_tab.c;;
-      "y.tab.h") from=y_tab.h;;
-    esac
-  fi
-  shift
-  to=$1
-  shift
-  sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;"
-  sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;"
-done
-
-# The program to run.
-prog=$1
-shift
-# Make any relative path in $prog absolute.
-case $prog in
-  [\\/]* | ?:[\\/]*) ;;
-  *[\\/]*) prog=`pwd`/$prog ;;
-esac
-
-dirname=ylwrap$$
-do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret'
-trap "ret=129; $do_exit" 1
-trap "ret=130; $do_exit" 2
-trap "ret=141; $do_exit" 13
-trap "ret=143; $do_exit" 15
-mkdir $dirname || exit 1
-
-cd $dirname
-
-case $# in
-  0) "$prog" "$input" ;;
-  *) "$prog" "$@" "$input" ;;
-esac
-ret=$?
-
-if test $ret -eq 0; then
-  for from in *
-  do
-    to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"`
-    if test -f "$from"; then
-      # If $2 is an absolute path name, then just use that,
-      # otherwise prepend '../'.
-      case $to in
-        [\\/]* | ?:[\\/]*) target=$to;;
-        *) target=../$to;;
-      esac
-
-      # Do not overwrite unchanged header files to avoid useless
-      # recompilations.  Always update the parser itself: it is the
-      # destination of the .y.c rule in the Makefile.  Divert the
-      # output of all other files to a temporary file so we can
-      # compare them to existing versions.
-      if test $from != $parser; then
-        realtarget=$target
-        target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'`
-      fi
-
-      # Munge "#line" or "#" directives.  Don't let the resulting
-      # debug information point at an absolute srcdir.  Use the real
-      # output file name, not yy.lex.c for instance.  Adjust the
-      # include guards too.
-      sed -e "/^#/!b"                           \
-          -e "s|$input_rx|$input_sub_rx|"       \
-          -e "$sed_fix_filenames"               \
-          -e "$sed_fix_header_guards"           \
-        "$from" >"$target" || ret=$?
-
-      # Check whether files must be updated.
-      if test "$from" != "$parser"; then
-        if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
-          echo "$to is unchanged"
-          rm -f "$target"
-        else
-          echo "updating $to"
-          mv -f "$target" "$realtarget"
-        fi
-      fi
-    else
-      # A missing file is only an error for the parser.  This is a
-      # blatant hack to let us support using "yacc -d".  If -d is not
-      # specified, don't fail when the header file is "missing".
-      if test "$from" = "$parser"; then
-        ret=1
-      fi
-    fi
-  done
-fi
-
-# Remove the directory.
-cd ..
-rm -rf $dirname
-
-exit $ret
-
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/config.h.in b/config.h.in
index 9ffb1de12d690dceb6965ff0fbf9a6b438ca738f..1ca8929500355260e87c87c3b36a290afda834d5 100644
--- a/config.h.in
+++ b/config.h.in
@@ -83,7 +83,3 @@
 
 /* Location of the iptables lock file */
 #undef XT_LOCK_NAME
-
-/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
-   `char[]'. */
-#undef YYTEXT_POINTER
diff --git a/configure b/configure
index da51d51a357c03366051212867cab3f80fb5763d..158679b5aaa2179a7215052de517fb2d69b87668 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for iptables 1.8.3.
+# Generated by GNU Autoconf 2.69 for iptables 1.8.4.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='iptables'
 PACKAGE_TARNAME='iptables'
-PACKAGE_VERSION='1.8.3'
-PACKAGE_STRING='iptables 1.8.3'
+PACKAGE_VERSION='1.8.4'
+PACKAGE_STRING='iptables 1.8.4'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -658,11 +658,6 @@ HAVE_LIBNFTNL_FALSE
 HAVE_LIBNFTNL_TRUE
 HAVE_LIBMNL_FALSE
 HAVE_LIBMNL_TRUE
-YFLAGS
-YACC
-LEXLIB
-LEX_OUTPUT_ROOT
-LEX
 libnftnl_LIBS
 libnftnl_CFLAGS
 libmnl_LIBS
@@ -696,7 +691,6 @@ ENABLE_SHARED_FALSE
 ENABLE_SHARED_TRUE
 ENABLE_STATIC_FALSE
 ENABLE_STATIC_TRUE
-libiptc_LDFLAGS2
 CPP
 LT_SYS_LIBRARY_PATH
 OTOOL64
@@ -859,8 +853,6 @@ libmnl_CFLAGS
 libmnl_LIBS
 libnftnl_CFLAGS
 libnftnl_LIBS
-YACC
-YFLAGS
 libnetfilter_conntrack_CFLAGS
 libnetfilter_conntrack_LIBS'
 
@@ -1413,7 +1405,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures iptables 1.8.3 to adapt to many kinds of systems.
+\`configure' configures iptables 1.8.4 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1484,7 +1476,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of iptables 1.8.3:";;
+     short | recursive ) echo "Configuration of iptables 1.8.4:";;
    esac
   cat <<\_ACEOF
 
@@ -1563,12 +1555,6 @@ Some influential environment variables:
               C compiler flags for libnftnl, overriding pkg-config
   libnftnl_LIBS
               linker flags for libnftnl, overriding pkg-config
-  YACC        The `Yet Another Compiler Compiler' implementation to use.
-              Defaults to the first program found out of: `bison -y', `byacc',
-              `yacc'.
-  YFLAGS      The list of arguments that will be passed by default to $YACC.
-              This script will default YFLAGS to the empty string to avoid a
-              default value of `-d' given by some make applications.
   libnetfilter_conntrack_CFLAGS
               C compiler flags for libnetfilter_conntrack, overriding
               pkg-config
@@ -1641,7 +1627,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-iptables configure 1.8.3
+iptables configure 1.8.4
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2189,7 +2175,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by iptables $as_me 1.8.3, which was
+It was created by iptables $as_me 1.8.4, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3060,7 +3046,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='iptables'
- VERSION='1.8.3'
+ VERSION='1.8.4'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -12561,47 +12547,6 @@ else
 fi
 
 
-libiptc_LDFLAGS2="";
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts -Wl,--no-as-needed" >&5
-$as_echo_n "checking whether the linker accepts -Wl,--no-as-needed... " >&6; }
-if ${ax_cv_linker_flags__Wl___no_as_needed+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-
-      ax_save_FLAGS=$LDFLAGS
-      LDFLAGS="-Wl,--no-as-needed"
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ax_cv_linker_flags__Wl___no_as_needed=yes
-else
-  ax_cv_linker_flags__Wl___no_as_needed=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-      LDFLAGS=$ax_save_FLAGS
-fi
-
-eval ax_check_linker_flags=$ax_cv_linker_flags__Wl___no_as_needed
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_check_linker_flags" >&5
-$as_echo "$ax_check_linker_flags" >&6; }
-if test "x$ax_check_linker_flags" = xyes; then
-	libiptc_LDFLAGS2="-Wl,--no-as-needed"
-else
-	:
-fi
-
-
-
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $LD knows -Wl,--no-undefined" >&5
 $as_echo_n "checking whether $LD knows -Wl,--no-undefined... " >&6; }
 saved_LDFLAGS="$LDFLAGS";
@@ -13185,225 +13130,6 @@ fi
 		echo "    iptables-compat over nftables support."
 		exit 1
 	fi
-
-	for ac_prog in flex lex
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_LEX+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$LEX"; then
-  ac_cv_prog_LEX="$LEX" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_LEX="$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-LEX=$ac_cv_prog_LEX
-if test -n "$LEX"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LEX" >&5
-$as_echo "$LEX" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-  test -n "$LEX" && break
-done
-test -n "$LEX" || LEX=":"
-
-if test "x$LEX" != "x:"; then
-  cat >conftest.l <<_ACEOF
-%%
-a { ECHO; }
-b { REJECT; }
-c { yymore (); }
-d { yyless (1); }
-e { /* IRIX 6.5 flex 2.5.4 underquotes its yyless argument.  */
-    yyless ((input () != 0)); }
-f { unput (yytext[0]); }
-. { BEGIN INITIAL; }
-%%
-#ifdef YYTEXT_POINTER
-extern char *yytext;
-#endif
-int
-main (void)
-{
-  return ! yylex () + ! yywrap ();
-}
-_ACEOF
-{ { ac_try="$LEX conftest.l"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
-  (eval "$LEX conftest.l") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking lex output file root" >&5
-$as_echo_n "checking lex output file root... " >&6; }
-if ${ac_cv_prog_lex_root+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-
-if test -f lex.yy.c; then
-  ac_cv_prog_lex_root=lex.yy
-elif test -f lexyy.c; then
-  ac_cv_prog_lex_root=lexyy
-else
-  as_fn_error $? "cannot find output from $LEX; giving up" "$LINENO" 5
-fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_root" >&5
-$as_echo "$ac_cv_prog_lex_root" >&6; }
-LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
-
-if test -z "${LEXLIB+set}"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking lex library" >&5
-$as_echo_n "checking lex library... " >&6; }
-if ${ac_cv_lib_lex+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-
-    ac_save_LIBS=$LIBS
-    ac_cv_lib_lex='none needed'
-    for ac_lib in '' -lfl -ll; do
-      LIBS="$ac_lib $ac_save_LIBS"
-      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-`cat $LEX_OUTPUT_ROOT.c`
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_lex=$ac_lib
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-      test "$ac_cv_lib_lex" != 'none needed' && break
-    done
-    LIBS=$ac_save_LIBS
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lex" >&5
-$as_echo "$ac_cv_lib_lex" >&6; }
-  test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether yytext is a pointer" >&5
-$as_echo_n "checking whether yytext is a pointer... " >&6; }
-if ${ac_cv_prog_lex_yytext_pointer+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  # POSIX says lex can declare yytext either as a pointer or an array; the
-# default is implementation-dependent.  Figure out which it is, since
-# not all implementations provide the %pointer and %array declarations.
-ac_cv_prog_lex_yytext_pointer=no
-ac_save_LIBS=$LIBS
-LIBS="$LEXLIB $ac_save_LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-  #define YYTEXT_POINTER 1
-`cat $LEX_OUTPUT_ROOT.c`
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_prog_lex_yytext_pointer=yes
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_save_LIBS
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_yytext_pointer" >&5
-$as_echo "$ac_cv_prog_lex_yytext_pointer" >&6; }
-if test $ac_cv_prog_lex_yytext_pointer = yes; then
-
-$as_echo "#define YYTEXT_POINTER 1" >>confdefs.h
-
-fi
-rm -f conftest.l $LEX_OUTPUT_ROOT.c
-
-fi
-if test "$LEX" = :; then
-  LEX=${am_missing_run}flex
-fi
-	for ac_prog in 'bison -y' byacc
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_YACC+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$YACC"; then
-  ac_cv_prog_YACC="$YACC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_YACC="$ac_prog"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-fi
-fi
-YACC=$ac_cv_prog_YACC
-if test -n "$YACC"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5
-$as_echo "$YACC" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-  test -n "$YACC" && break
-done
-test -n "$YACC" || YACC="yacc"
-
-
-	if test -z "$ac_cv_prog_YACC"
-	then
-		echo "*** Error: No suitable bison/yacc found. ***"
-		echo "    Please install the 'bison' package."
-		exit 1
-	fi
-	if test -z "$ac_cv_prog_LEX"
-	then
-	        echo "*** Error: No suitable flex/lex found. ***"
-	        echo "    Please install the 'flex' package."
-	        exit 1
-	fi
 fi
 
  if test "$mnl" = 1; then
@@ -13562,7 +13288,7 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
-ac_config_files="$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 iptables/xtables-monitor.8 utils/nfnl_osf.8 utils/nfbpf_compile.8"
+ac_config_files="$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 iptables/xtables-monitor.8 utils/nfnl_osf.8 utils/nfbpf_compile.8"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -14154,7 +13880,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by iptables $as_me 1.8.3, which was
+This file was extended by iptables $as_me 1.8.4, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14220,7 +13946,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-iptables config.status 1.8.3
+iptables config.status 1.8.4
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -14655,7 +14381,6 @@ do
     "libxtables/Makefile") CONFIG_FILES="$CONFIG_FILES libxtables/Makefile" ;;
     "utils/Makefile") CONFIG_FILES="$CONFIG_FILES utils/Makefile" ;;
     "include/xtables-version.h") CONFIG_FILES="$CONFIG_FILES include/xtables-version.h" ;;
-    "include/iptables/internal.h") CONFIG_FILES="$CONFIG_FILES include/iptables/internal.h" ;;
     "iptables/xtables-monitor.8") CONFIG_FILES="$CONFIG_FILES iptables/xtables-monitor.8" ;;
     "utils/nfnl_osf.8") CONFIG_FILES="$CONFIG_FILES utils/nfnl_osf.8" ;;
     "utils/nfbpf_compile.8") CONFIG_FILES="$CONFIG_FILES utils/nfbpf_compile.8" ;;
diff --git a/configure.ac b/configure.ac
index b94512d79442de4c9ccd2ffbea24014884a69c1c..cab77a489dfd855868ce863bd3e4b081e0c29cc5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 
-AC_INIT([iptables], [1.8.3])
+AC_INIT([iptables], [1.8.4])
 
 # See libtool.info "Libtool's versioning system"
 libxtables_vcurrent=14
@@ -73,11 +73,6 @@ AC_ARG_WITH([xt-lock-name], AS_HELP_STRING([--with-xt-lock-name=PATH],
 	[xt_lock_name="$withval"],
 	[xt_lock_name="/run/xtables.lock"])
 
-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";
@@ -146,22 +141,6 @@ if test "x$enable_nftables" = "xyes"; then
 		echo "    iptables-compat over nftables support."
 		exit 1
 	fi
-
-	AM_PROG_LEX
-	AC_PROG_YACC
-
-	if test -z "$ac_cv_prog_YACC"
-	then
-		echo "*** Error: No suitable bison/yacc found. ***"
-		echo "    Please install the 'bison' package."
-		exit 1
-	fi
-	if test -z "$ac_cv_prog_LEX"
-	then
-	        echo "*** Error: No suitable flex/lex found. ***"
-	        echo "    Please install the 'flex' package."
-	        exit 1
-	fi
 fi
 
 AM_CONDITIONAL([HAVE_LIBMNL], [test "$mnl" = 1])
@@ -250,7 +229,7 @@ AC_CONFIG_FILES([Makefile extensions/GNUmakefile include/Makefile
 	libiptc/Makefile libiptc/libiptc.pc
 	libiptc/libip4tc.pc libiptc/libip6tc.pc
 	libxtables/Makefile utils/Makefile
-	include/xtables-version.h include/iptables/internal.h
+	include/xtables-version.h
 	iptables/xtables-monitor.8
 	utils/nfnl_osf.8
 	utils/nfbpf_compile.8])
diff --git a/extensions/libebt_among.c b/extensions/libebt_among.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e87db3bc06faa8fae4e3dcd454dd23af3afc015
--- /dev/null
+++ b/extensions/libebt_among.c
@@ -0,0 +1,243 @@
+/* ebt_among
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ */
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <xtables.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include "iptables/nft.h"
+#include "iptables/nft-bridge.h"
+
+#define AMONG_DST '1'
+#define AMONG_SRC '2'
+#define AMONG_DST_F '3'
+#define AMONG_SRC_F '4'
+
+static const struct option bramong_opts[] = {
+	{"among-dst", required_argument, 0, AMONG_DST},
+	{"among-src", required_argument, 0, AMONG_SRC},
+	{"among-dst-file", required_argument, 0, AMONG_DST_F},
+	{"among-src-file", required_argument, 0, AMONG_SRC_F},
+	{0}
+};
+
+static void bramong_print_help(void)
+{
+	printf(
+"`among' options:\n"
+"--among-dst      [!] list      : matches if ether dst is in list\n"
+"--among-src      [!] list      : matches if ether src is in list\n"
+"--among-dst-file [!] file      : obtain dst list from file\n"
+"--among-src-file [!] file      : obtain src list from file\n"
+"list has form:\n"
+" xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]"
+",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n"
+"Things in brackets are optional.\n"
+"If you want to allow two (or more) IP addresses to one MAC address, you\n"
+"can specify two (or more) pairs with the same MAC, e.g.\n"
+" 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n"
+	);
+}
+
+static void
+parse_nft_among_pair(char *buf, struct nft_among_pair *pair, bool have_ip)
+{
+	char *sep = index(buf, '=');
+	struct ether_addr *ether;
+
+	if (have_ip ^ !!sep)
+		xtables_error(PARAMETER_PROBLEM,
+			      "among: Mixed MAC and MAC=IP not allowed.");
+
+	if (sep) {
+		*sep = '\0';
+
+		if (!inet_aton(sep + 1, &pair->in))
+			xtables_error(PARAMETER_PROBLEM,
+				      "Invalid IP address '%s'\n", sep + 1);
+	}
+	ether = ether_aton(buf);
+	if (!ether)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Invalid MAC address '%s'\n", buf);
+	memcpy(&pair->ether, ether, sizeof(*ether));
+}
+
+static void
+parse_nft_among_pairs(struct nft_among_pair *pairs, char *buf,
+		      size_t cnt, bool have_ip)
+{
+	size_t tmpcnt = 0;
+
+	buf = strtok(buf, ",");
+	while (buf) {
+		struct nft_among_pair pair = {};
+
+		parse_nft_among_pair(buf, &pair, have_ip);
+		nft_among_insert_pair(pairs, &tmpcnt, &pair);
+		buf = strtok(NULL, ",");
+	}
+}
+
+static size_t count_nft_among_pairs(char *buf)
+{
+	size_t cnt = 0;
+	char *p = buf;
+
+	if (!*buf)
+		return 0;
+
+	do {
+		cnt++;
+		p = index(++p, ',');
+	} while (p);
+
+	return cnt;
+}
+
+static bool nft_among_pairs_have_ip(char *buf)
+{
+	return !!index(buf, '=');
+}
+
+static int bramong_parse(int c, char **argv, int invert,
+		 unsigned int *flags, const void *entry,
+		 struct xt_entry_match **match)
+{
+	struct nft_among_data *data = (struct nft_among_data *)(*match)->data;
+	struct xt_entry_match *new_match;
+	bool have_ip, dst = false;
+	size_t new_size, cnt;
+	struct stat stats;
+	int fd = -1, poff;
+	long flen = 0;
+
+	switch (c) {
+	case AMONG_DST_F:
+		dst = true;
+		/* fall through */
+	case AMONG_SRC_F:
+		if ((fd = open(optarg, O_RDONLY)) == -1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Couldn't open file '%s'", optarg);
+		fstat(fd, &stats);
+		flen = stats.st_size;
+		/* use mmap because the file will probably be big */
+		optarg = mmap(0, flen, PROT_READ | PROT_WRITE,
+			      MAP_PRIVATE, fd, 0);
+		if (optarg == MAP_FAILED)
+			xtables_error(PARAMETER_PROBLEM,
+				      "Couldn't map file to memory");
+		if (optarg[flen-1] != '\n')
+			xtables_error(PARAMETER_PROBLEM,
+				      "File should end with a newline");
+		if (strchr(optarg, '\n') != optarg+flen-1)
+			xtables_error(PARAMETER_PROBLEM,
+				      "File should only contain one line");
+		optarg[flen-1] = '\0';
+		/* fall through */
+	case AMONG_DST:
+		if (c == AMONG_DST)
+			dst = true;
+		/* fall through */
+	case AMONG_SRC:
+		break;
+	default:
+		return 0;
+	}
+
+	cnt = count_nft_among_pairs(optarg);
+	if (cnt == 0)
+		return 0;
+
+	new_size = data->src.cnt + data->dst.cnt + cnt;
+	new_size *= sizeof(struct nft_among_pair);
+	new_size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+			sizeof(struct nft_among_data);
+	new_match = xtables_calloc(1, new_size);
+	memcpy(new_match, *match, (*match)->u.match_size);
+	new_match->u.match_size = new_size;
+
+	data = (struct nft_among_data *)new_match->data;
+	have_ip = nft_among_pairs_have_ip(optarg);
+	poff = nft_among_prepare_data(data, dst, cnt, invert, have_ip);
+	parse_nft_among_pairs(data->pairs + poff, optarg, cnt, have_ip);
+
+	free(*match);
+	*match = new_match;
+
+	if (c == AMONG_DST_F || c == AMONG_SRC_F) {
+		munmap(argv, flen);
+		close(fd);
+	}
+	return 1;
+}
+
+static void __bramong_print(struct nft_among_pair *pairs,
+			    int cnt, bool inv, bool have_ip)
+{
+	const char *isep = inv ? "! " : "";
+	int i;
+
+	for (i = 0; i < cnt; i++) {
+		printf("%s", isep);
+		isep = ",";
+
+		printf("%s", ether_ntoa(&pairs[i].ether));
+		if (have_ip)
+			printf("=%s", inet_ntoa(pairs[i].in));
+	}
+	printf(" ");
+}
+
+static void bramong_print(const void *ip, const struct xt_entry_match *match,
+			  int numeric)
+{
+	struct nft_among_data *data = (struct nft_among_data *)match->data;
+
+	if (data->src.cnt) {
+		printf("--among-src ");
+		__bramong_print(data->pairs,
+				data->src.cnt, data->src.inv, data->src.ip);
+	}
+	if (data->dst.cnt) {
+		printf("--among-dst ");
+		__bramong_print(data->pairs + data->src.cnt,
+				data->dst.cnt, data->dst.inv, data->dst.ip);
+	}
+}
+
+static struct xtables_match bramong_match = {
+	.name		= "among",
+	.revision	= 0,
+	.version	= XTABLES_VERSION,
+	.family		= NFPROTO_BRIDGE,
+	.size		= XT_ALIGN(sizeof(struct nft_among_data)),
+	.userspacesize	= XT_ALIGN(sizeof(struct nft_among_data)),
+	.help		= bramong_print_help,
+	.parse		= bramong_parse,
+	.print		= bramong_print,
+	.extra_opts	= bramong_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&bramong_match);
+}
diff --git a/extensions/libebt_among.t b/extensions/libebt_among.t
new file mode 100644
index 0000000000000000000000000000000000000000..56b299161ff3187b1240bb799478bbb4de477567
--- /dev/null
+++ b/extensions/libebt_among.t
@@ -0,0 +1,16 @@
+:INPUT,FORWARD,OUTPUT
+--among-dst de:ad:0:be:ee:ff,c0:ff:ee:0:ba:be;--among-dst c0:ff:ee:0:ba:be,de:ad:0:be:ee:ff;OK
+--among-dst ! c0:ff:ee:0:ba:be,de:ad:0:be:ee:ff;=;OK
+--among-src be:ef:0:c0:ff:ee,c0:ff:ee:0:ba:be,de:ad:0:be:ee:ff;=;OK
+--among-src de:ad:0:be:ee:ff=10.0.0.1,c0:ff:ee:0:ba:be=192.168.1.1;--among-src c0:ff:ee:0:ba:be=192.168.1.1,de:ad:0:be:ee:ff=10.0.0.1;OK
+--among-src ! c0:ff:ee:0:ba:be=192.168.1.1,de:ad:0:be:ee:ff=10.0.0.1;=;OK
+--among-src de:ad:0:be:ee:ff --among-dst c0:ff:ee:0:ba:be;=;OK
+--among-src de:ad:0:be:ee:ff=10.0.0.1 --among-dst c0:ff:ee:0:ba:be=192.168.1.1;=;OK
+--among-src ! de:ad:0:be:ee:ff --among-dst c0:ff:ee:0:ba:be;=;OK
+--among-src de:ad:0:be:ee:ff=10.0.0.1 --among-dst ! c0:ff:ee:0:ba:be=192.168.1.1;=;OK
+--among-src ! de:ad:0:be:ee:ff --among-dst c0:ff:ee:0:ba:be=192.168.1.1;=;OK
+--among-src de:ad:0:be:ee:ff=10.0.0.1 --among-dst ! c0:ff:ee:0:ba:be=192.168.1.1;=;OK
+--among-src;=;FAIL
+--among-src 00:11=10.0.0.1;=;FAIL
+--among-src de:ad:0:be:ee:ff=10.256.0.1;=;FAIL
+--among-src de:ad:0:be:ee:ff,c0:ff:ee:0:ba:be=192.168.1.1;=;FAIL
diff --git a/extensions/libebt_standard.t b/extensions/libebt_standard.t
index 0d678fb23c439a1a9bb9cd6c07189b0d3c36c0cd..c6c3172748d7b83dc35e684728260fdc26891e4d 100644
--- a/extensions/libebt_standard.t
+++ b/extensions/libebt_standard.t
@@ -9,3 +9,20 @@
 -p ! ARP -j ACCEPT;=;OK
 -p 0 -j ACCEPT;=;FAIL
 -p ! 0 -j ACCEPT;=;FAIL
+:INPUT
+-i foobar;=;OK
+-o foobar;=;FAIL
+:FORWARD
+-i foobar;=;OK
+-o foobar;=;OK
+:OUTPUT
+-i foobar;=;FAIL
+-o foobar;=;OK
+:PREROUTING
+*nat
+-i foobar;=;OK
+-o foobar;=;FAIL
+:POSTROUTING
+*nat
+-i foobar;=;FAIL
+-o foobar;=;OK
diff --git a/extensions/libxt_MASQUERADE.man b/extensions/libxt_MASQUERADE.man
index cc1e769043bcb9117c428891efac86de1bfd1e55..7746f4734a31ac2fd7b33f1fad8ff0d030aaddca 100644
--- a/extensions/libxt_MASQUERADE.man
+++ b/extensions/libxt_MASQUERADE.man
@@ -24,6 +24,7 @@ Randomize source port mapping
 If option
 \fB\-\-random\fP
 is used then port mapping will be randomized (kernel >= 2.6.21).
+Since kernel 5.0, \fB\-\-random\fP is identical to \fB\-\-random-fully\fP.
 .TP
 \fB\-\-random-fully\fP
 Full randomize source port mapping
diff --git a/extensions/libxt_REDIRECT.man b/extensions/libxt_REDIRECT.man
index 3400a6df7d3418e76521aee5fb86c4a0bc7b2965..28d4d10b79046e6a0e22da69cee878f40ce5b61a 100644
--- a/extensions/libxt_REDIRECT.man
+++ b/extensions/libxt_REDIRECT.man
@@ -8,7 +8,8 @@ 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).
+127.0.0.1 for IPv4 and ::1 for IPv6, and packets arriving on
+interfaces that don't have an IP address configured are dropped).
 .TP
 \fB\-\-to\-ports\fP \fIport\fP[\fB\-\fP\fIport\fP]
 This specifies a destination port or range of ports to use: without
diff --git a/extensions/libxt_SYNPROXY.c b/extensions/libxt_SYNPROXY.c
index 475590eaa55a0bf0328092befe204dbc1b3d0f48..6a0b913e03b5e32d171e1f67a0ee769c34227f55 100644
--- a/extensions/libxt_SYNPROXY.c
+++ b/extensions/libxt_SYNPROXY.c
@@ -106,6 +106,28 @@ static void SYNPROXY_save(const void *ip, const struct xt_entry_target *target)
 		printf(" --ecn");
 }
 
+static int SYNPROXY_xlate(struct xt_xlate *xl,
+		          const struct xt_xlate_tg_params *params)
+{
+	const struct xt_synproxy_info *info =
+		(const struct xt_synproxy_info *)params->target->data;
+
+	xt_xlate_add(xl, "synproxy ");
+
+	if (info->options & XT_SYNPROXY_OPT_SACK_PERM)
+		xt_xlate_add(xl, "sack-perm ");
+	if (info->options & XT_SYNPROXY_OPT_TIMESTAMP)
+		xt_xlate_add(xl, "timestamp ");
+	if (info->options & XT_SYNPROXY_OPT_WSCALE)
+		xt_xlate_add(xl, "wscale %u ", info->wscale);
+	if (info->options & XT_SYNPROXY_OPT_MSS)
+		xt_xlate_add(xl, "mss %u ", info->mss);
+	if (info->options & XT_SYNPROXY_OPT_ECN)
+		xt_xlate_add(xl, "ecn ");
+
+	return 1;
+}
+
 static struct xtables_target synproxy_tg_reg = {
 	.family        = NFPROTO_UNSPEC,
 	.name          = "SYNPROXY",
@@ -119,6 +141,7 @@ static struct xtables_target synproxy_tg_reg = {
 	.x6_parse      = SYNPROXY_parse,
 	.x6_fcheck     = SYNPROXY_check,
 	.x6_options    = SYNPROXY_opts,
+	.xlate         = SYNPROXY_xlate,
 };
 
 void _init(void)
diff --git a/extensions/libxt_SYNPROXY.txlate b/extensions/libxt_SYNPROXY.txlate
new file mode 100644
index 0000000000000000000000000000000000000000..b3de2b2a8c9dc207a5ade3c034c8205a61df2639
--- /dev/null
+++ b/extensions/libxt_SYNPROXY.txlate
@@ -0,0 +1,2 @@
+iptables-translate -t mangle -A INPUT -i iifname -p tcp -m tcp --dport 80 -m state --state INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
+nft add rule ip mangle INPUT iifname "iifname" tcp dport 80 ct state invalid,untracked  counter synproxy sack-perm timestamp wscale 7 mss 1460
diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c
index 1817d335cb19e034270754c97a7ea438a1c8980c..6f3503933e6648170676f4c1be4ac32e8184ddf9 100644
--- a/extensions/libxt_conntrack.c
+++ b/extensions/libxt_conntrack.c
@@ -1257,8 +1257,6 @@ static int _conntrack3_mt_xlate(struct xt_xlate *xl,
 	}
 
 	if (sinfo->match_flags & XT_CONNTRACK_STATUS) {
-		if (sinfo->status_mask == 1)
-			return 0;
 		xt_xlate_add(xl, "%sct status %s", space,
 			     sinfo->invert_flags & XT_CONNTRACK_STATUS ?
 			     "!= " : "");
diff --git a/extensions/libxt_conntrack.txlate b/extensions/libxt_conntrack.txlate
index e35d5ce87c34e011c78e3e690ad35c76e4a7f134..8a3d0181c71efadaf916b391e4f09539acc59244 100644
--- a/extensions/libxt_conntrack.txlate
+++ b/extensions/libxt_conntrack.txlate
@@ -28,6 +28,9 @@ nft add rule ip filter INPUT ct reply daddr 10.100.2.131 counter accept
 iptables-translate -t filter -A INPUT -m conntrack --ctproto tcp --ctorigsrcport 443:444 -j ACCEPT
 nft add rule ip filter INPUT ct original protocol 6 ct original proto-src 443-444 counter accept
 
+iptables-translate -t filter -A INPUT -m conntrack --ctstatus EXPECTED -j ACCEPT
+nft add rule ip filter INPUT ct status expected counter accept
+
 iptables-translate -t filter -A INPUT -m conntrack ! --ctstatus CONFIRMED -j ACCEPT
 nft add rule ip filter INPUT ct status != confirmed counter accept
 
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index f3b6e04309bde0f10a01d4e193e62e96b4946e92..7f1d2a402c4fd00128e90c4c37fe5fed34179af7 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -772,7 +772,7 @@ static void hashlimit_mt_check(struct xt_fcheck_call *cb)
 		if (cb->xflags & F_BURST) {
 			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
 				xtables_error(PARAMETER_PROBLEM,
-					"burst cannot be smaller than %lub", cost_to_bytes(info->cfg.avg));
+					"burst cannot be smaller than %"PRIu64"b", cost_to_bytes(info->cfg.avg));
 
 			burst = info->cfg.burst;
 			burst /= cost_to_bytes(info->cfg.avg);
diff --git a/extensions/libxt_nfacct.c b/extensions/libxt_nfacct.c
index 2ad59d52f3645ebf37f9850855b37ca321335da4..d9c0309a864e6f26e17b221935441ce10b9d1662 100644
--- a/extensions/libxt_nfacct.c
+++ b/extensions/libxt_nfacct.c
@@ -70,20 +70,36 @@ static void nfacct_save(const void *ip, const struct xt_entry_match *match)
 	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,
+static struct xtables_match nfacct_matches[] = {
+	{
+		.family		= NFPROTO_UNSPEC,
+		.revision	= 0,
+		.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,
+	},
+	{
+		.family		= NFPROTO_UNSPEC,
+		.revision	= 1,
+		.name		= "nfacct",
+		.version	= XTABLES_VERSION,
+		.size		= XT_ALIGN(sizeof(struct xt_nfacct_match_info_v1)),
+		.userspacesize	= offsetof(struct xt_nfacct_match_info_v1, 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);
+	xtables_register_matches(nfacct_matches, ARRAY_SIZE(nfacct_matches));
 }
diff --git a/extensions/libxt_owner.c b/extensions/libxt_owner.c
index 87e4df31256668c2f73688be2d4cc451ee038586..1702b47888725d7bb27c239cd1822d79a681b8ba 100644
--- a/extensions/libxt_owner.c
+++ b/extensions/libxt_owner.c
@@ -56,6 +56,7 @@ enum {
 	O_PROCESS,
 	O_SESSION,
 	O_COMM,
+	O_SUPPL_GROUPS,
 };
 
 static void owner_mt_help_v0(void)
@@ -87,7 +88,8 @@ static void owner_mt_help(void)
 "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");
+"[!] --socket-exists                  Match if socket exists\n"
+"    --suppl-groups                   Also match supplementary groups set with --gid-owner\n");
 }
 
 #define s struct ipt_owner_info
@@ -131,6 +133,7 @@ static const struct xt_option_entry owner_mt_opts[] = {
 	 .flags = XTOPT_INVERT},
 	{.name = "socket-exists", .id = O_SOCK_EXISTS, .type = XTTYPE_NONE,
 	 .flags = XTOPT_INVERT},
+	{.name = "suppl-groups", .id = O_SUPPL_GROUPS, .type = XTTYPE_NONE},
 	XTOPT_TABLEEND,
 };
 
@@ -275,6 +278,11 @@ static void owner_mt_parse(struct xt_option_call *cb)
 			info->invert |= XT_OWNER_SOCKET;
 		info->match |= XT_OWNER_SOCKET;
 		break;
+	case O_SUPPL_GROUPS:
+		if (!(info->match & XT_OWNER_GID))
+			xtables_param_act(XTF_BAD_VALUE, "owner", "--suppl-groups", "you need to use --gid-owner first");
+		info->match |= XT_OWNER_SUPPL_GROUPS;
+		break;
 	}
 }
 
@@ -455,9 +463,10 @@ static void owner_mt_print(const void *ip, const struct xt_entry_match *match,
 {
 	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);
+	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);
+	owner_mt_print_item(info, "incl. suppl. groups", XT_OWNER_SUPPL_GROUPS, numeric);
 }
 
 static void
@@ -487,9 +496,10 @@ 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);
+	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);
+	owner_mt_print_item(info, "--suppl-groups",   XT_OWNER_SUPPL_GROUPS, true);
 }
 
 static int
diff --git a/extensions/libxt_owner.man b/extensions/libxt_owner.man
index 49b58ceef738e88d6a50452b46e338a056701d26..e2479865651187bb82eab65041f471a74f55ee60 100644
--- a/extensions/libxt_owner.man
+++ b/extensions/libxt_owner.man
@@ -15,5 +15,9 @@ given user. You may also specify a numerical UID, or an UID range.
 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\-\-suppl\-groups\fP
+Causes group(s) specified with \fB\-\-gid-owner\fP to be also checked in the
+supplementary groups of a process.
+.TP
 [\fB!\fP] \fB\-\-socket\-exists\fP
 Matches if the packet is associated with a socket.
diff --git a/extensions/libxt_owner.t b/extensions/libxt_owner.t
index aec30b655e9a50537cec02d9ad745a78f52b6e5a..2779e5c1c24cb88c29b0728c307ce24090447774 100644
--- a/extensions/libxt_owner.t
+++ b/extensions/libxt_owner.t
@@ -8,5 +8,9 @@
 -m owner --uid-owner 0-10 --gid-owner 0-10;=;OK
 -m owner ! --uid-owner root;-m owner ! --uid-owner 0;OK
 -m owner --socket-exists;=;OK
+-m owner --gid-owner 0-10 --suppl-groups;=;OK
+-m owner --suppl-groups --gid-owner 0-10;;FAIL
+-m owner --gid-owner 0-10 ! --suppl-groups;;FAIL
+-m owner --suppl-groups;;FAIL
 :INPUT
 -m owner --uid-owner root;;FAIL
diff --git a/include/Makefile.in b/include/Makefile.in
index 19a5efc670975f0bbae6d94bfb0b583be4db2f06..de20fc6936421b414137f13e7769f569f8ea3102 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -92,10 +92,10 @@ host_triplet = @host@
 @ENABLE_LIBIPQ_TRUE@am__append_1 = libipq/libipq.h
 subdir = include
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__include_HEADERS_DIST) \
@@ -208,9 +208,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -244,8 +241,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -290,7 +285,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
diff --git a/include/iptables/internal.h.in b/include/iptables/internal.h
similarity index 81%
rename from include/iptables/internal.h.in
rename to include/iptables/internal.h
index 8568e581ec8db78fdee2d34a0007a0ba6b0e95a2..86ba074a3741f91122a2cd7b09d2992fb2003f1a 100644
--- a/include/iptables/internal.h.in
+++ b/include/iptables/internal.h
@@ -1,8 +1,6 @@
 #ifndef IPTABLES_INTERNAL_H
 #define IPTABLES_INTERNAL_H 1
 
-#define IPTABLES_VERSION "@PACKAGE_VERSION@"
-
 /**
  * Program's own name and version.
  */
diff --git a/include/linux/netfilter/xt_nfacct.h b/include/linux/netfilter/xt_nfacct.h
index 59ab00dd86d09d3a0459be56c21a86c331277076..04ec2b04afd45a0b93df10762f56381d0448ef85 100644
--- a/include/linux/netfilter/xt_nfacct.h
+++ b/include/linux/netfilter/xt_nfacct.h
@@ -14,4 +14,9 @@ struct xt_nfacct_match_info {
 	struct nf_acct	*nfacct;
 };
 
+struct xt_nfacct_match_info_v1 {
+	char		name[NFACCT_NAME_MAX];
+	struct nf_acct	*nfacct __attribute__((aligned(8)));
+};
+
 #endif /* _XT_NFACCT_MATCH_H */
diff --git a/include/linux/netfilter/xt_owner.h b/include/linux/netfilter/xt_owner.h
index 2081761714b56cba415b413f7801ef113230407a..e7731dcc51f46f49f427ee4539b6c2259c915a4e 100644
--- a/include/linux/netfilter/xt_owner.h
+++ b/include/linux/netfilter/xt_owner.h
@@ -4,9 +4,10 @@
 #include <linux/types.h>
 
 enum {
-	XT_OWNER_UID    = 1 << 0,
-	XT_OWNER_GID    = 1 << 1,
-	XT_OWNER_SOCKET = 1 << 2,
+	XT_OWNER_UID          = 1 << 0,
+	XT_OWNER_GID          = 1 << 1,
+	XT_OWNER_SOCKET       = 1 << 2,
+	XT_OWNER_SUPPL_GROUPS = 1 << 3,
 };
 
 struct xt_owner_match_info {
diff --git a/iptables-test.py b/iptables-test.py
index 532dee7c9000fbdd1e46347eb5a6087f3b011f41..fdb4e6a3644e4743f22abbe6fc9f049c6493ece1 100755
--- a/iptables-test.py
+++ b/iptables-test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 #
 # (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
 #
@@ -10,6 +10,7 @@
 # This software has been sponsored by Sophos Astaro <http://www.sophos.com>
 #
 
+from __future__ import print_function
 import sys
 import os
 import subprocess
@@ -45,7 +46,7 @@ def print_error(reason, filename=None, lineno=None):
     '''
     Prints an error with nice colors, indicating file and line number.
     '''
-    print (filename + ": " + Colors.RED + "ERROR" +
+    print(filename + ": " + Colors.RED + "ERROR" +
         Colors.ENDC + ": line %d (%s)" % (lineno, reason))
 
 
@@ -140,7 +141,7 @@ def run_test(iptables, rule, rule_save, res, filename, lineno, netns):
         return -1
 
     # find the rule
-    matching = out.find(rule_save)
+    matching = out.find(rule_save.encode('utf-8'))
     if matching < 0:
         reason = "cannot find: " + iptables + " -I " + rule
         print_error(reason, filename, lineno)
@@ -166,7 +167,7 @@ def execute_cmd(cmd, filename, lineno):
     if cmd.startswith('iptables ') or cmd.startswith('ip6tables ') or cmd.startswith('ebtables ') or cmd.startswith('arptables '):
         cmd = os.path.abspath(os.path.curdir) + "/iptables/" + EXECUTEABLE + " " + cmd
 
-    print >> log_file, "command: %s" % cmd
+    print("command: {}".format(cmd), file=log_file)
     ret = subprocess.call(cmd, shell=True, universal_newlines=True,
         stderr=subprocess.STDOUT, stdout=log_file)
     log_file.flush()
@@ -249,7 +250,7 @@ def run_test_file(filename, netns):
             continue
 
         if len(chain_array) == 0:
-            print "broken test, missing chain, leaving"
+            print("broken test, missing chain, leaving")
             sys.exit()
 
         test_passed = True
@@ -282,7 +283,7 @@ def run_test_file(filename, netns):
     if netns:
         execute_cmd("ip netns del ____iptables-container-test", filename, 0)
     if total_test_passed:
-        print filename + ": " + Colors.GREEN + "OK" + Colors.ENDC
+        print(filename + ": " + Colors.GREEN + "OK" + Colors.ENDC)
 
     f.close()
     return tests, passed
@@ -302,7 +303,7 @@ def show_missing():
     missing = [test_name(i) for i in libfiles
                if not test_name(i) in testfiles]
 
-    print '\n'.join(missing)
+    print('\n'.join(missing))
 
 
 #
@@ -313,6 +314,8 @@ def main():
     parser.add_argument('filename', nargs='?',
                         metavar='path/to/file.t',
                         help='Run only this test')
+    parser.add_argument('-H', '--host', action='store_true',
+                        help='Run tests against installed binaries')
     parser.add_argument('-l', '--legacy', action='store_true',
                         help='Test iptables-legacy')
     parser.add_argument('-m', '--missing', action='store_true',
@@ -336,11 +339,13 @@ def main():
         EXECUTEABLE = "xtables-nft-multi"
 
     if os.getuid() != 0:
-        print "You need to be root to run this, sorry"
+        print("You need to be root to run this, sorry")
         return
 
-    os.putenv("XTABLES_LIBDIR", os.path.abspath(EXTENSIONS_PATH))
-    os.putenv("PATH", "%s/iptables:%s" % (os.path.abspath(os.path.curdir), os.getenv("PATH")))
+    if not args.host:
+        os.putenv("XTABLES_LIBDIR", os.path.abspath(EXTENSIONS_PATH))
+        os.putenv("PATH", "%s/iptables:%s" % (os.path.abspath(os.path.curdir),
+                                              os.getenv("PATH")))
 
     test_files = 0
     tests = 0
@@ -351,13 +356,17 @@ def main():
     try:
         log_file = open(LOGFILE, 'w')
     except IOError:
-        print "Couldn't open log file %s" % LOGFILE
+        print("Couldn't open log file %s" % LOGFILE)
         return
 
-    file_list = [os.path.join(EXTENSIONS_PATH, i)
-                 for i in os.listdir(EXTENSIONS_PATH)]
     if args.filename:
         file_list = [args.filename]
+    else:
+        file_list = [os.path.join(EXTENSIONS_PATH, i)
+                     for i in os.listdir(EXTENSIONS_PATH)
+                     if i.endswith('.t')]
+        file_list.sort()
+
     for filename in file_list:
         file_tests, file_passed = run_test_file(filename, args.netns)
         if file_tests:
@@ -365,8 +374,7 @@ def main():
             passed += file_passed
             test_files += 1
 
-    print ("%d test files, %d unit tests, %d passed" %
-           (test_files, tests, passed))
+    print("%d test files, %d unit tests, %d passed" % (test_files, tests, passed))
 
 
 if __name__ == '__main__':
diff --git a/iptables/.gitignore b/iptables/.gitignore
index c638139b8a1d085aad49aa9dd3ec0b54c0aacb68..cd7d87b127ae689a14eb7ce6546c3e26ad2e0e9c 100644
--- a/iptables/.gitignore
+++ b/iptables/.gitignore
@@ -3,6 +3,7 @@
 /ip6tables-restore
 /ip6tables-static
 /ip6tables-translate.8
+/ip6tables-restore-translate.8
 /iptables
 /iptables.8
 /iptables-extensions.8
@@ -13,14 +14,12 @@
 /iptables-restore.8
 /iptables-static
 /iptables-translate.8
+/iptables-restore-translate.8
 /iptables-xml
 /iptables-xml.1
 /xtables-multi
 /xtables-legacy-multi
 /xtables-nft-multi
-/xtables-config-parser.c
-/xtables-config-parser.h
-/xtables-config-syntax.c
 /xtables-monitor.8
 
 /xtables.pc
diff --git a/iptables/Makefile.am b/iptables/Makefile.am
index 3ff85893b2fa8fb48592b8696b055de5ce39d040..fc834e0f7b89e65d7ee65f29cfb332b80322348b 100644
--- a/iptables/Makefile.am
+++ b/iptables/Makefile.am
@@ -2,7 +2,6 @@
 
 AM_CFLAGS        = ${regular_CFLAGS}
 AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir} ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftnl_CFLAGS} ${libnetfilter_conntrack_CFLAGS}
-AM_YFLAGS = -d
 
 BUILT_SOURCES =
 
@@ -27,7 +26,6 @@ xtables_legacy_multi_LDADD   += ../libxtables/libxtables.la -lm
 
 # iptables using nf_tables api
 if ENABLE_NFTABLES
-BUILT_SOURCES += xtables-config-parser.h
 xtables_nft_multi_SOURCES  = xtables-nft-multi.c iptables-xml.c
 xtables_nft_multi_CFLAGS   = ${AM_CFLAGS}
 xtables_nft_multi_LDADD    = ../extensions/libext.a ../extensions/libext_ebt.a
@@ -35,19 +33,16 @@ if ENABLE_STATIC
 xtables_nft_multi_CFLAGS  += -DALL_INCLUSIVE
 endif
 xtables_nft_multi_CFLAGS  += -DENABLE_NFTABLES -DENABLE_IPV4 -DENABLE_IPV6
-xtables_nft_multi_SOURCES += xtables-config-parser.y xtables-config-syntax.l
 xtables_nft_multi_SOURCES += xtables-save.c xtables-restore.c \
 				xtables-standalone.c xtables.c nft.c \
 				nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \
-				xtables-monitor.c \
+				xtables-monitor.c nft-cache.c \
 				xtables-arp-standalone.c xtables-arp.c \
 				nft-bridge.c \
 				xtables-eb-standalone.c xtables-eb.c \
 				xtables-eb-translate.c \
 				xtables-translate.c
 xtables_nft_multi_LDADD   += ${libmnl_LIBS} ${libnftnl_LIBS} ${libnetfilter_conntrack_LIBS} ../extensions/libext4.a ../extensions/libext6.a ../extensions/libext_ebt.a ../extensions/libext_arpt.a
-# yacc and lex generate dirty code
-xtables_nft_multi-xtables-config-parser.o xtables_nft_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
 xtables_nft_multi_SOURCES += xshared.c
 xtables_nft_multi_LDADD   += ../libxtables/libxtables.la -lm
 endif
@@ -58,17 +53,17 @@ sbin_PROGRAMS	+= xtables-nft-multi
 endif
 man_MANS         = iptables.8 iptables-restore.8 iptables-save.8 \
                    iptables-xml.1 ip6tables.8 ip6tables-restore.8 \
-                   ip6tables-save.8 iptables-extensions.8 \
-                   xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
-                   iptables-translate.8 ip6tables-translate.8 \
-                   xtables-monitor.8
+                   ip6tables-save.8 iptables-extensions.8
 if ENABLE_NFTABLES
-man_MANS	+= arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
-		   ebtables-nft.8
+man_MANS	+= xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
+                   iptables-translate.8 ip6tables-translate.8 \
+		   iptables-restore-translate.8 ip6tables-restore-translate.8 \
+                   xtables-monitor.8 \
+                   arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
+                   ebtables-nft.8
 endif
 CLEANFILES       = iptables.8 xtables-monitor.8 \
-		   iptables-translate.8 ip6tables-translate.8 \
-		   xtables-config-parser.c xtables-config-syntax.c
+		   iptables-translate.8 ip6tables-translate.8
 
 vx_bin_links   = iptables-xml
 if ENABLE_IPV4
@@ -98,7 +93,7 @@ iptables-extensions.8: iptables-extensions.8.tmpl ../extensions/matches.man ../e
 		-e '/@MATCH@/ r ../extensions/matches.man' \
 		-e '/@TARGET@/ r ../extensions/targets.man' $< >$@;
 
-iptables-translate.8 ip6tables-translate.8:
+iptables-translate.8 ip6tables-translate.8 iptables-restore-translate.8 ip6tables-restore-translate.8:
 	${AM_VERBOSE_GEN} echo '.so man8/xtables-translate.8' >$@
 
 pkgconfig_DATA = xtables.pc
diff --git a/iptables/Makefile.in b/iptables/Makefile.in
index 0b4c92b3ce0766483dbf90982c67e2f95c8e744b..ee429b3f646095a93882a7178e86320be13eaa2c 100644
--- a/iptables/Makefile.in
+++ b/iptables/Makefile.in
@@ -97,21 +97,22 @@ host_triplet = @host@
 @ENABLE_IPV6_TRUE@am__append_5 = ip6tables-standalone.c ip6tables.c
 @ENABLE_IPV6_TRUE@am__append_6 = -DENABLE_IPV6
 @ENABLE_IPV6_TRUE@am__append_7 = ../libiptc/libip6tc.la ../extensions/libext6.a
-
-# iptables using nf_tables api
-@ENABLE_NFTABLES_TRUE@am__append_8 = xtables-config-parser.h
-@ENABLE_NFTABLES_TRUE@@ENABLE_STATIC_TRUE@am__append_9 = -DALL_INCLUSIVE
+@ENABLE_NFTABLES_TRUE@@ENABLE_STATIC_TRUE@am__append_8 = -DALL_INCLUSIVE
 sbin_PROGRAMS = xtables-legacy-multi$(EXEEXT) $(am__EXEEXT_1)
-@ENABLE_NFTABLES_TRUE@am__append_10 = xtables-nft-multi
-@ENABLE_NFTABLES_TRUE@am__append_11 = arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
-@ENABLE_NFTABLES_TRUE@		   ebtables-nft.8
+@ENABLE_NFTABLES_TRUE@am__append_9 = xtables-nft-multi
+@ENABLE_NFTABLES_TRUE@am__append_10 = xtables-nft.8 xtables-translate.8 xtables-legacy.8 \
+@ENABLE_NFTABLES_TRUE@                   iptables-translate.8 ip6tables-translate.8 \
+@ENABLE_NFTABLES_TRUE@		   iptables-restore-translate.8 ip6tables-restore-translate.8 \
+@ENABLE_NFTABLES_TRUE@                   xtables-monitor.8 \
+@ENABLE_NFTABLES_TRUE@                   arptables-nft.8 arptables-nft-restore.8 arptables-nft-save.8 \
+@ENABLE_NFTABLES_TRUE@                   ebtables-nft.8
 
 subdir = iptables
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
@@ -151,16 +152,14 @@ xtables_legacy_multi_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 	$(xtables_legacy_multi_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
 	$(LDFLAGS) -o $@
 am__xtables_nft_multi_SOURCES_DIST = xtables-nft-multi.c \
-	iptables-xml.c xtables-config-parser.y xtables-config-syntax.l \
-	xtables-save.c xtables-restore.c xtables-standalone.c \
-	xtables.c nft.c nft-shared.c nft-ipv4.c nft-ipv6.c nft-arp.c \
-	xtables-monitor.c xtables-arp-standalone.c xtables-arp.c \
-	nft-bridge.c xtables-eb-standalone.c xtables-eb.c \
-	xtables-eb-translate.c xtables-translate.c xshared.c
+	iptables-xml.c xtables-save.c xtables-restore.c \
+	xtables-standalone.c xtables.c nft.c nft-shared.c nft-ipv4.c \
+	nft-ipv6.c nft-arp.c xtables-monitor.c nft-cache.c \
+	xtables-arp-standalone.c xtables-arp.c nft-bridge.c \
+	xtables-eb-standalone.c xtables-eb.c xtables-eb-translate.c \
+	xtables-translate.c xshared.c
 @ENABLE_NFTABLES_TRUE@am_xtables_nft_multi_OBJECTS = xtables_nft_multi-xtables-nft-multi.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-iptables-xml.$(OBJEXT) \
-@ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-config-parser.$(OBJEXT) \
-@ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-config-syntax.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-save.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-restore.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-standalone.$(OBJEXT) \
@@ -171,6 +170,7 @@ am__xtables_nft_multi_SOURCES_DIST = xtables-nft-multi.c \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-nft-ipv6.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-nft-arp.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-monitor.$(OBJEXT) \
+@ENABLE_NFTABLES_TRUE@	xtables_nft_multi-nft-cache.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-arp-standalone.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-xtables-arp.$(OBJEXT) \
 @ENABLE_NFTABLES_TRUE@	xtables_nft_multi-nft-bridge.$(OBJEXT) \
@@ -230,23 +230,6 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
-AM_V_LEX = $(am__v_LEX_@AM_V@)
-am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
-am__v_LEX_0 = @echo "  LEX     " $@;
-am__v_LEX_1 = 
-YLWRAP = $(top_srcdir)/build-aux/ylwrap
-am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
-		   -e s/c++$$/h++/ -e s/c$$/h/
-YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
-AM_V_YACC = $(am__v_YACC_@AM_V@)
-am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
-am__v_YACC_0 = @echo "  YACC    " $@;
-am__v_YACC_1 = 
 SOURCES = $(xtables_legacy_multi_SOURCES) $(xtables_nft_multi_SOURCES)
 DIST_SOURCES = $(am__xtables_legacy_multi_SOURCES_DIST) \
 	$(am__xtables_nft_multi_SOURCES_DIST)
@@ -311,9 +294,7 @@ am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/iptables-apply.8.in \
 	$(srcdir)/iptables-restore.8.in $(srcdir)/iptables-save.8.in \
 	$(srcdir)/iptables-xml.1.in $(srcdir)/iptables.8.in \
 	$(srcdir)/xtables-monitor.8.in $(srcdir)/xtables.pc.in \
-	$(top_srcdir)/build-aux/depcomp $(top_srcdir)/build-aux/ylwrap \
-	xtables-config-parser.c xtables-config-parser.h \
-	xtables-config-syntax.c
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 pkgdatadir = @pkgdatadir@
 ACLOCAL = @ACLOCAL@
@@ -349,9 +330,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -385,8 +363,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -431,7 +407,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
@@ -468,8 +443,7 @@ top_srcdir = @top_srcdir@
 xtlibdir = @xtlibdir@
 AM_CFLAGS = ${regular_CFLAGS}
 AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}/include -I${top_srcdir} ${kinclude_CPPFLAGS} ${libmnl_CFLAGS} ${libnftnl_CFLAGS} ${libnetfilter_conntrack_CFLAGS}
-AM_YFLAGS = -d
-BUILT_SOURCES = $(am__append_8)
+BUILT_SOURCES = 
 xtables_legacy_multi_SOURCES = xtables-legacy-multi.c iptables-xml.c \
 	$(am__append_2) $(am__append_5) xshared.c iptables-restore.c \
 	iptables-save.c
@@ -477,18 +451,20 @@ xtables_legacy_multi_CFLAGS = ${AM_CFLAGS} $(am__append_1) \
 	$(am__append_3) $(am__append_6)
 xtables_legacy_multi_LDADD = ../extensions/libext.a $(am__append_4) \
 	$(am__append_7) ../libxtables/libxtables.la -lm
+
+# iptables using nf_tables api
 @ENABLE_NFTABLES_TRUE@xtables_nft_multi_SOURCES = xtables-nft-multi.c \
-@ENABLE_NFTABLES_TRUE@	iptables-xml.c xtables-config-parser.y \
-@ENABLE_NFTABLES_TRUE@	xtables-config-syntax.l xtables-save.c \
+@ENABLE_NFTABLES_TRUE@	iptables-xml.c xtables-save.c \
 @ENABLE_NFTABLES_TRUE@	xtables-restore.c xtables-standalone.c \
 @ENABLE_NFTABLES_TRUE@	xtables.c nft.c nft-shared.c nft-ipv4.c \
 @ENABLE_NFTABLES_TRUE@	nft-ipv6.c nft-arp.c xtables-monitor.c \
-@ENABLE_NFTABLES_TRUE@	xtables-arp-standalone.c xtables-arp.c \
-@ENABLE_NFTABLES_TRUE@	nft-bridge.c xtables-eb-standalone.c \
-@ENABLE_NFTABLES_TRUE@	xtables-eb.c xtables-eb-translate.c \
+@ENABLE_NFTABLES_TRUE@	nft-cache.c xtables-arp-standalone.c \
+@ENABLE_NFTABLES_TRUE@	xtables-arp.c nft-bridge.c \
+@ENABLE_NFTABLES_TRUE@	xtables-eb-standalone.c xtables-eb.c \
+@ENABLE_NFTABLES_TRUE@	xtables-eb-translate.c \
 @ENABLE_NFTABLES_TRUE@	xtables-translate.c xshared.c
 @ENABLE_NFTABLES_TRUE@xtables_nft_multi_CFLAGS = ${AM_CFLAGS} \
-@ENABLE_NFTABLES_TRUE@	$(am__append_9) -DENABLE_NFTABLES \
+@ENABLE_NFTABLES_TRUE@	$(am__append_8) -DENABLE_NFTABLES \
 @ENABLE_NFTABLES_TRUE@	-DENABLE_IPV4 -DENABLE_IPV6
 @ENABLE_NFTABLES_TRUE@xtables_nft_multi_LDADD =  \
 @ENABLE_NFTABLES_TRUE@	../extensions/libext.a \
@@ -502,12 +478,9 @@ xtables_legacy_multi_LDADD = ../extensions/libext.a $(am__append_4) \
 @ENABLE_NFTABLES_TRUE@	../libxtables/libxtables.la -lm
 man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \
 	iptables-xml.1 ip6tables.8 ip6tables-restore.8 \
-	ip6tables-save.8 iptables-extensions.8 xtables-nft.8 \
-	xtables-translate.8 xtables-legacy.8 iptables-translate.8 \
-	ip6tables-translate.8 xtables-monitor.8 $(am__append_11)
+	ip6tables-save.8 iptables-extensions.8 $(am__append_10)
 CLEANFILES = iptables.8 xtables-monitor.8 \
-		   iptables-translate.8 ip6tables-translate.8 \
-		   xtables-config-parser.c xtables-config-syntax.c
+		   iptables-translate.8 ip6tables-translate.8
 
 vx_bin_links = iptables-xml
 @ENABLE_IPV4_TRUE@v4_sbin_links = iptables-legacy iptables-legacy-restore iptables-legacy-save \
@@ -533,7 +506,7 @@ all: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) all-am
 
 .SUFFIXES:
-.SUFFIXES: .c .l .lo .o .obj .y
+.SUFFIXES: .c .lo .o .obj
 $(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -632,9 +605,6 @@ clean-sbinPROGRAMS:
 xtables-legacy-multi$(EXEEXT): $(xtables_legacy_multi_OBJECTS) $(xtables_legacy_multi_DEPENDENCIES) $(EXTRA_xtables_legacy_multi_DEPENDENCIES) 
 	@rm -f xtables-legacy-multi$(EXEEXT)
 	$(AM_V_CCLD)$(xtables_legacy_multi_LINK) $(xtables_legacy_multi_OBJECTS) $(xtables_legacy_multi_LDADD) $(LIBS)
-xtables-config-parser.h: xtables-config-parser.c
-	@if test ! -f $@; then rm -f xtables-config-parser.c; else :; fi
-	@if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) xtables-config-parser.c; else :; fi
 
 xtables-nft-multi$(EXEEXT): $(xtables_nft_multi_OBJECTS) $(xtables_nft_multi_DEPENDENCIES) $(EXTRA_xtables_nft_multi_DEPENDENCIES) 
 	@rm -f xtables-nft-multi$(EXEEXT)
@@ -658,6 +628,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-iptables-xml.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-arp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-bridge.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-cache.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-ipv4.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-ipv6.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-nft-shared.Po@am__quote@
@@ -665,8 +636,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xshared.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-arp-standalone.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-arp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-config-parser.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-eb-standalone.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-eb-translate.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xtables_nft_multi-xtables-eb.Po@am__quote@
@@ -853,34 +822,6 @@ xtables_nft_multi-iptables-xml.obj: iptables-xml.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-iptables-xml.obj `if test -f 'iptables-xml.c'; then $(CYGPATH_W) 'iptables-xml.c'; else $(CYGPATH_W) '$(srcdir)/iptables-xml.c'; fi`
 
-xtables_nft_multi-xtables-config-parser.o: xtables-config-parser.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-config-parser.o -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Tpo -c -o xtables_nft_multi-xtables-config-parser.o `test -f 'xtables-config-parser.c' || echo '$(srcdir)/'`xtables-config-parser.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Tpo $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='xtables-config-parser.c' object='xtables_nft_multi-xtables-config-parser.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xtables-config-parser.o `test -f 'xtables-config-parser.c' || echo '$(srcdir)/'`xtables-config-parser.c
-
-xtables_nft_multi-xtables-config-parser.obj: xtables-config-parser.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-config-parser.obj -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Tpo -c -o xtables_nft_multi-xtables-config-parser.obj `if test -f 'xtables-config-parser.c'; then $(CYGPATH_W) 'xtables-config-parser.c'; else $(CYGPATH_W) '$(srcdir)/xtables-config-parser.c'; fi`
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Tpo $(DEPDIR)/xtables_nft_multi-xtables-config-parser.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='xtables-config-parser.c' object='xtables_nft_multi-xtables-config-parser.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xtables-config-parser.obj `if test -f 'xtables-config-parser.c'; then $(CYGPATH_W) 'xtables-config-parser.c'; else $(CYGPATH_W) '$(srcdir)/xtables-config-parser.c'; fi`
-
-xtables_nft_multi-xtables-config-syntax.o: xtables-config-syntax.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-config-syntax.o -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Tpo -c -o xtables_nft_multi-xtables-config-syntax.o `test -f 'xtables-config-syntax.c' || echo '$(srcdir)/'`xtables-config-syntax.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Tpo $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='xtables-config-syntax.c' object='xtables_nft_multi-xtables-config-syntax.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xtables-config-syntax.o `test -f 'xtables-config-syntax.c' || echo '$(srcdir)/'`xtables-config-syntax.c
-
-xtables_nft_multi-xtables-config-syntax.obj: xtables-config-syntax.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-config-syntax.obj -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Tpo -c -o xtables_nft_multi-xtables-config-syntax.obj `if test -f 'xtables-config-syntax.c'; then $(CYGPATH_W) 'xtables-config-syntax.c'; else $(CYGPATH_W) '$(srcdir)/xtables-config-syntax.c'; fi`
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Tpo $(DEPDIR)/xtables_nft_multi-xtables-config-syntax.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='xtables-config-syntax.c' object='xtables_nft_multi-xtables-config-syntax.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xtables-config-syntax.obj `if test -f 'xtables-config-syntax.c'; then $(CYGPATH_W) 'xtables-config-syntax.c'; else $(CYGPATH_W) '$(srcdir)/xtables-config-syntax.c'; fi`
-
 xtables_nft_multi-xtables-save.o: xtables-save.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-save.o -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-save.Tpo -c -o xtables_nft_multi-xtables-save.o `test -f 'xtables-save.c' || echo '$(srcdir)/'`xtables-save.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-save.Tpo $(DEPDIR)/xtables_nft_multi-xtables-save.Po
@@ -1021,6 +962,20 @@ xtables_nft_multi-xtables-monitor.obj: xtables-monitor.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xtables-monitor.obj `if test -f 'xtables-monitor.c'; then $(CYGPATH_W) 'xtables-monitor.c'; else $(CYGPATH_W) '$(srcdir)/xtables-monitor.c'; fi`
 
+xtables_nft_multi-nft-cache.o: nft-cache.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-nft-cache.o -MD -MP -MF $(DEPDIR)/xtables_nft_multi-nft-cache.Tpo -c -o xtables_nft_multi-nft-cache.o `test -f 'nft-cache.c' || echo '$(srcdir)/'`nft-cache.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-nft-cache.Tpo $(DEPDIR)/xtables_nft_multi-nft-cache.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nft-cache.c' object='xtables_nft_multi-nft-cache.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-nft-cache.o `test -f 'nft-cache.c' || echo '$(srcdir)/'`nft-cache.c
+
+xtables_nft_multi-nft-cache.obj: nft-cache.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-nft-cache.obj -MD -MP -MF $(DEPDIR)/xtables_nft_multi-nft-cache.Tpo -c -o xtables_nft_multi-nft-cache.obj `if test -f 'nft-cache.c'; then $(CYGPATH_W) 'nft-cache.c'; else $(CYGPATH_W) '$(srcdir)/nft-cache.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-nft-cache.Tpo $(DEPDIR)/xtables_nft_multi-nft-cache.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nft-cache.c' object='xtables_nft_multi-nft-cache.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-nft-cache.obj `if test -f 'nft-cache.c'; then $(CYGPATH_W) 'nft-cache.c'; else $(CYGPATH_W) '$(srcdir)/nft-cache.c'; fi`
+
 xtables_nft_multi-xtables-arp-standalone.o: xtables-arp-standalone.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -MT xtables_nft_multi-xtables-arp-standalone.o -MD -MP -MF $(DEPDIR)/xtables_nft_multi-xtables-arp-standalone.Tpo -c -o xtables_nft_multi-xtables-arp-standalone.o `test -f 'xtables-arp-standalone.c' || echo '$(srcdir)/'`xtables-arp-standalone.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/xtables_nft_multi-xtables-arp-standalone.Tpo $(DEPDIR)/xtables_nft_multi-xtables-arp-standalone.Po
@@ -1133,12 +1088,6 @@ xtables_nft_multi-xshared.obj: xshared.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(xtables_nft_multi_CFLAGS) $(CFLAGS) -c -o xtables_nft_multi-xshared.obj `if test -f 'xshared.c'; then $(CYGPATH_W) 'xshared.c'; else $(CYGPATH_W) '$(srcdir)/xshared.c'; fi`
 
-.l.c:
-	$(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
-
-.y.c:
-	$(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
-
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -1374,9 +1323,6 @@ distclean-generic:
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
 	@echo "it deletes files that may require special tools to rebuild."
-	-rm -f xtables-config-parser.c
-	-rm -f xtables-config-parser.h
-	-rm -f xtables-config-syntax.c
 	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
 clean: clean-am
 
@@ -1474,15 +1420,13 @@ uninstall-man: uninstall-man1 uninstall-man8
 
 .PRECIOUS: Makefile
 
-# yacc and lex generate dirty code
-@ENABLE_NFTABLES_TRUE@xtables_nft_multi-xtables-config-parser.o xtables_nft_multi-xtables-config-syntax.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
 
 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' $< >$@;
 
-iptables-translate.8 ip6tables-translate.8:
+iptables-translate.8 ip6tables-translate.8 iptables-restore-translate.8 ip6tables-restore-translate.8:
 	${AM_VERBOSE_GEN} echo '.so man8/xtables-translate.8' >$@
 
 # Using if..fi avoids an ugly "error (ignored)" message :)
diff --git a/iptables/ebtables-nft.8 b/iptables/ebtables-nft.8
index db8b2ab28cca5b370d2072abd0df75374985ec1c..a91f0c1aacb0f8ab23c5ba327906a8642527b710 100644
--- a/iptables/ebtables-nft.8
+++ b/iptables/ebtables-nft.8
@@ -522,35 +522,39 @@ If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must
 be consulted to determine the payload protocol.  This is a two byte
 (hexadecimal) argument.  Only 802.3 frames with DSAP/SSAP 0xaa are
 checked for type.
-.\" .SS among
-.\" Match a MAC address or MAC/IP address pair versus a list of MAC addresses
-.\" and MAC/IP address pairs.
-.\" A list entry has the following format:
-.\" .IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
-.\" list entries are separated by a comma, specifying an IP address corresponding to
-.\" the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
-.\" but different IP address (and vice versa) can be specified. If the MAC address doesn't
-.\" match any entry from the list, the frame doesn't match the rule (unless "!" was used).
-.\" .TP
-.\" .BR "--among-dst " "[!] \fIlist\fP"
-.\" Compare the MAC destination to the given list. If the Ethernet frame has type
-.\" .IR IPv4 " or " ARP ,
-.\" then comparison with MAC/IP destination address pairs from the
-.\" list is possible.
-.\" .TP
-.\" .BR "--among-src " "[!] \fIlist\fP"
-.\" Compare the MAC source to the given list. If the Ethernet frame has type
-.\" .IR IPv4 " or " ARP ,
-.\" then comparison with MAC/IP source address pairs from the list
-.\" is possible.
-.\" .TP
-.\" .BR "--among-dst-file " "[!] \fIfile\fP"
-.\" Same as
-.\" .BR --among-dst " but the list is read in from the specified file."
-.\" .TP
-.\" .BR "--among-src-file " "[!] \fIfile\fP"
-.\" Same as
-.\" .BR --among-src " but the list is read in from the specified file."
+.SS among
+Match a MAC address or MAC/IP address pair versus a list of MAC addresses
+and MAC/IP address pairs.
+A list entry has the following format:
+.IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
+list entries are separated by a comma, specifying an IP address corresponding to
+the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
+but different IP address (and vice versa) can be specified. If the MAC address doesn't
+match any entry from the list, the frame doesn't match the rule (unless "!" was used).
+.TP
+.BR "--among-dst " "[!] \fIlist\fP"
+Compare the MAC destination to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP destination address pairs from the
+list is possible.
+.TP
+.BR "--among-src " "[!] \fIlist\fP"
+Compare the MAC source to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP source address pairs from the list
+is possible.
+.TP
+.BR "--among-dst-file " "[!] \fIfile\fP"
+Same as
+.BR --among-dst " but the list is read in from the specified file."
+.TP
+.BR "--among-src-file " "[!] \fIfile\fP"
+Same as
+.BR --among-src " but the list is read in from the specified file."
+.PP
+Note that in this implementation of ebtables, among lists uses must be
+internally homogeneous regarding whether IP addresses are present or not. Mixed
+use of MAC addresses and MAC/IP address pairs is not supported yet.
 .SS arp
 Specify (R)ARP fields. The protocol must be specified as
 .IR ARP " or " RARP .
@@ -1108,8 +1112,8 @@ arp message and the hardware address length in the arp header is 6 bytes.
 The version of ebtables this man page ships with does not support the
 .B broute
 table. Also there is no support for
-.BR among " and " string
-matches. And finally, this list is probably not complete.
+.B string
+match. And finally, this list is probably not complete.
 .SH SEE ALSO
 .BR xtables-nft "(8), " iptables "(8), " ip (8)
 .PP
diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
index 050afa9a36458c3721c4595244d9032f3159d270..576c2cf8b0d9f05e1dc7c1e692ff08c1c6d02fff 100644
--- a/iptables/ip6tables.c
+++ b/iptables/ip6tables.c
@@ -24,7 +24,7 @@
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <string.h>
 #include <netdb.h>
@@ -45,33 +45,6 @@
 #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'};
@@ -121,7 +94,7 @@ static struct option original_opts[] = {
 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,
+	.program_version = PACKAGE_VERSION,
 	.orig_opts = original_opts,
 	.exit_err = ip6tables_exit_error,
 	.compat_rev = xtables_compatible_revision,
@@ -175,12 +148,6 @@ static const unsigned int inverse_for_options[NUMBER_OF_OPT] =
 #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)
@@ -342,27 +309,6 @@ opt2char(int option)
 	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.
@@ -381,19 +327,6 @@ static int is_exthdr(uint16_t proto)
 		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)
 {
@@ -1228,6 +1161,7 @@ int do_command6(int argc, char *argv[], char **table,
 	struct xtables_rule_match *matchp;
 	struct xtables_target *t;
 	unsigned long long cnt;
+	bool table_set = false;
 
 	/* re-set optind to 0 in case do_command6 gets called
 	 * a second time */
@@ -1508,7 +1442,12 @@ int do_command6(int argc, char *argv[], char **table,
 			if (cs.invert)
 				xtables_error(PARAMETER_PROBLEM,
 					   "unexpected ! flag before --table");
+			if (restore && table_set)
+				xtables_error(PARAMETER_PROBLEM,
+					      "The -t option (seen in line %u) cannot be used in %s.\n",
+					      line, xt_params->program_name);
 			*table = optarg;
+			table_set = true;
 			break;
 
 		case 'x':
@@ -1578,7 +1517,7 @@ int do_command6(int argc, char *argv[], char **table,
 					xtables_error(PARAMETER_PROBLEM,
 						   "multiple consecutive ! not"
 						   " allowed");
-				cs.invert = TRUE;
+				cs.invert = true;
 				optarg[0] = '\0';
 				continue;
 			}
@@ -1590,12 +1529,12 @@ int do_command6(int argc, char *argv[], char **table,
 				/*
 				 * If new options were loaded, we must retry
 				 * getopt immediately and not allow
-				 * cs.invert=FALSE to be executed.
+				 * cs.invert=false to be executed.
 				 */
 				continue;
 			break;
 		}
-		cs.invert = FALSE;
+		cs.invert = false;
 	}
 
 	if (!wait && wait_interval_set)
diff --git a/iptables/iptables-restore.c b/iptables/iptables-restore.c
index 575e619cc30db2620bd4fbbfd2798feb77147fa9..b0a51d491c5081b2330c168a94be6c2f2af0424d 100644
--- a/iptables/iptables-restore.c
+++ b/iptables/iptables-restore.c
@@ -4,7 +4,7 @@
  *
  * This code is distributed under the terms of GNU GPL v2
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <errno.h>
 #include <stdbool.h>
@@ -43,7 +43,7 @@ static const struct option options[] = {
 
 static void print_usage(const char *name, const char *version)
 {
-	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command]\n"
+	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command] [file]\n"
 			"	   [ --counters ]\n"
 			"	   [ --verbose ]\n"
 			"	   [ --version]\n"
@@ -70,7 +70,7 @@ struct iptables_restore_cb {
 };
 
 static struct xtc_handle *
-create_handle(struct iptables_restore_cb *cb, const char *tablename)
+create_handle(const struct iptables_restore_cb *cb, const char *tablename)
 {
 	struct xtc_handle *handle;
 
@@ -82,18 +82,19 @@ create_handle(struct iptables_restore_cb *cb, const char *tablename)
 		handle = cb->ops->init(tablename);
 	}
 
-	if (!handle) {
+	if (!handle)
 		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
 			"table '%s'\n", xt_params->program_name, tablename);
-		exit(1);
-	}
+
 	return handle;
 }
 
 static int
-ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
+ip46tables_restore_main(const struct iptables_restore_cb *cb,
+			int argc, char *argv[])
 {
 	struct xtc_handle *handle = NULL;
+	struct argv_store av_store = {};
 	char buffer[10240];
 	int c, lock;
 	char curtable[XT_TABLE_MAXNAMELEN + 1] = {};
@@ -125,7 +126,7 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 				break;
 			case 'h':
 				print_usage(xt_params->program_name,
-					    IPTABLES_VERSION);
+					    PACKAGE_VERSION);
 				exit(0);
 			case 'n':
 				noflush = 1;
@@ -207,12 +208,11 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 
 			table = strtok(buffer+1, " \t\n");
 			DEBUGP("line %u, table '%s'\n", line, table);
-			if (!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';
 
@@ -248,12 +248,10 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 
 			chain = strtok(buffer+1, " \t\n");
 			DEBUGP("line %u, chain '%s'\n", line, chain);
-			if (!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,
@@ -281,12 +279,10 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 
 			policy = strtok(NULL, " \t\n");
 			DEBUGP("line %u, policy '%s'\n", line, policy);
-			if (!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 = {};
@@ -316,61 +312,31 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 			ret = 1;
 
 		} else if (in_table) {
-			int a;
 			char *pcnt = NULL;
 			char *bcnt = NULL;
-			char *parsestart;
-
-			if (buffer[0] == '[') {
-				/* we have counters in our input */
-				char *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;
-			}
+			char *parsestart = buffer;
 
-			add_argv(argv[0], 0);
-			add_argv("-t", 0);
-			add_argv(curtable, 0);
+			add_argv(&av_store, argv[0], 0);
+			add_argv(&av_store, "-t", 0);
+			add_argv(&av_store, curtable, 0);
 
+			tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
 			if (counters && pcnt && bcnt) {
-				add_argv("--set-counters", 0);
-				add_argv((char *) pcnt, 0);
-				add_argv((char *) bcnt, 0);
+				add_argv(&av_store, "--set-counters", 0);
+				add_argv(&av_store, pcnt, 0);
+				add_argv(&av_store, bcnt, 0);
 			}
 
-			add_param_to_argv(parsestart, line);
+			add_param_to_argv(&av_store, parsestart, line);
 
 			DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
-				newargc, curtable);
-
-			for (a = 0; a < newargc; a++)
-				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+				av_store.argc, curtable);
+			debug_print_argv(&av_store);
 
-			ret = cb->do_command(newargc, newargv,
-					 &newargv[2], &handle, true);
+			ret = cb->do_command(av_store.argc, av_store.argv,
+					 &av_store.argv[2], &handle, true);
 
-			free_argv();
+			free_argv(&av_store);
 			fflush(stdout);
 		}
 		if (tablename && strcmp(tablename, curtable) != 0)
@@ -393,7 +359,7 @@ ip46tables_restore_main(struct iptables_restore_cb *cb, int argc, char *argv[])
 
 
 #if defined ENABLE_IPV4
-struct iptables_restore_cb ipt_restore_cb = {
+static const struct iptables_restore_cb ipt_restore_cb = {
 	.ops		= &iptc_ops,
 	.for_each_chain	= for_each_chain4,
 	.flush_entries	= flush_entries4,
@@ -424,7 +390,7 @@ iptables_restore_main(int argc, char *argv[])
 #endif
 
 #if defined ENABLE_IPV6
-struct iptables_restore_cb ip6t_restore_cb = {
+static const struct iptables_restore_cb ip6t_restore_cb = {
 	.ops		= &ip6tc_ops,
 	.for_each_chain	= for_each_chain6,
 	.flush_entries	= flush_entries6,
diff --git a/iptables/iptables-save.c b/iptables/iptables-save.c
index 826cb1e4f7b941b7c254e7546cc5a84d59e4693b..c7251e35ad763fe39a142375af275c04f9517f34 100644
--- a/iptables/iptables-save.c
+++ b/iptables/iptables-save.c
@@ -5,6 +5,7 @@
  * This code is distributed under the terms of GNU GPL v2
  *
  */
+#include "config.h"
 #include <getopt.h>
 #include <errno.h>
 #include <stdio.h>
@@ -90,7 +91,7 @@ static int do_output(struct iptables_save_cb *cb, const char *tablename)
 	time_t now = time(NULL);
 
 	printf("# Generated by %s v%s on %s",
-	       xt_params->program_name, IPTABLES_VERSION, ctime(&now));
+	       xt_params->program_name, PACKAGE_VERSION, ctime(&now));
 	printf("*%s\n", tablename);
 
 	/* Dump out chain names first,
diff --git a/iptables/iptables-xml.c b/iptables/iptables-xml.c
index 07300efc25eb41bed296f472de1f46b3c6db5e9a..98d03dda98d2b7ff38199293da6095cec44694c8 100644
--- a/iptables/iptables-xml.c
+++ b/iptables/iptables-xml.c
@@ -5,7 +5,7 @@
  *
  * This code is distributed under the terms of GNU GPL v2
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <errno.h>
 #include <string.h>
@@ -20,7 +20,7 @@
 
 struct xtables_globals iptables_xml_globals = {
 	.option_offset = 0,
-	.program_version = IPTABLES_VERSION,
+	.program_version = PACKAGE_VERSION,
 	.program_name = "iptables-xml",
 };
 #define prog_name iptables_xml_globals.program_name
@@ -208,12 +208,11 @@ needChain(char *chain)
 static void
 saveChain(char *chain, char *policy, struct xt_counters *ctr)
 {
-	if (nextChain >= maxChains) {
+	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;
@@ -441,7 +440,7 @@ do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
 }
 
 static int
-compareRules(void)
+compareRules(int newargc, char *newargv[], int oldargc, char *oldargv[])
 {
 	/* Compare arguments up to -j or -g for match.
 	 * NOTE: We don't want to combine actions if there were no criteria
@@ -490,11 +489,13 @@ compareRules(void)
 
 /* has a nice parsed rule starting with -A */
 static void
-do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
+do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[],
+	int oldargc, char *oldargv[])
 {
 	/* 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()) {
+	if (combine && argc > 2 && !isTarget(argv[2]) &&
+	    compareRules(argc, argv, oldargc, oldargv)) {
 		xmlComment("Combine action from next rule");
 	} else {
 
@@ -540,6 +541,7 @@ do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
 int
 iptables_xml_main(int argc, char *argv[])
 {
+	struct argv_store last_rule = {}, cur_rule = {};
 	char buffer[10240];
 	int c;
 	FILE *in;
@@ -557,7 +559,7 @@ iptables_xml_main(int argc, char *argv[])
 			verbose = 1;
 			break;
 		case 'h':
-			print_usage("iptables-xml", IPTABLES_VERSION);
+			print_usage("iptables-xml", PACKAGE_VERSION);
 			break;
 		}
 	}
@@ -606,12 +608,11 @@ iptables_xml_main(int argc, char *argv[])
 
 			table = strtok(buffer + 1, " \t\n");
 			DEBUGP("line %u, table '%s'\n", line, table);
-			if (!table) {
+			if (!table)
 				xtables_error(PARAMETER_PROBLEM,
 					   "%s: line %u table name invalid\n",
 					   prog_name, line);
-				exit(1);
-			}
+
 			openTable(table);
 
 			ret = 1;
@@ -623,23 +624,19 @@ iptables_xml_main(int argc, char *argv[])
 
 			chain = strtok(buffer + 1, " \t\n");
 			DEBUGP("line %u, chain '%s'\n", line, chain);
-			if (!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) {
+			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);
@@ -650,126 +647,32 @@ iptables_xml_main(int argc, char *argv[])
 			unsigned int a;
 			char *pcnt = NULL;
 			char *bcnt = NULL;
-			char *parsestart;
+			char *parsestart = buffer;
 			char *chain = NULL;
 
-			/* the parser */
-			char *param_start, *curchar;
-			int quote_open, quoted;
-			char param_buffer[1024];
-
-			if (buffer[0] == '[') {
-				/* we have counters in our input */
-				char *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 ((param_buffer[0] == '-' &&
-					     param_buffer[1] != '-' &&
-					     strchr(param_buffer, 't')) ||
-					    (!strncmp(param_buffer, "--t", 3) &&
-					     !strncmp(param_buffer, "--table", strlen(param_buffer)))) {
-						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 */
-				}
-			}
+			tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
+			add_param_to_argv(&cur_rule, parsestart, line);
 
 			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]);
+			       cur_rule.argc, curTable);
+			debug_print_argv(&cur_rule);
 
+			for (a = 1; a < cur_rule.argc; a++) {
+				if (strcmp(cur_rule.argv[a - 1], "-A"))
+					continue;
+				chain = cur_rule.argv[a];
+				break;
+			}
 			if (!chain) {
 				fprintf(stderr, "%s: line %u failed - no chain found\n",
 					prog_name, line);
 				exit(1);
 			}
 			needChain(chain);// Should we explicitly look for -A
-			do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
+			do_rule(pcnt, bcnt, cur_rule.argc, cur_rule.argv,
+				cur_rule.argvattr, last_rule.argc, last_rule.argv);
 
-			save_argv();
+			save_argv(&last_rule, &cur_rule);
 			ret = 1;
 		}
 		if (!ret) {
@@ -786,7 +689,7 @@ iptables_xml_main(int argc, char *argv[])
 
 	fclose(in);
 	printf("</iptables-rules>\n");
-	free_argv();
+	free_argv(&last_rule);
 
 	return 0;
 }
diff --git a/iptables/iptables.c b/iptables/iptables.c
index 38c4bfe8ecf5c7d9d8e24afa67db876688b870d9..88ef6cf666d4bac22e4a82d9e3b585c695fbdbb2 100644
--- a/iptables/iptables.c
+++ b/iptables/iptables.c
@@ -24,7 +24,7 @@
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <string.h>
 #include <netdb.h>
@@ -41,33 +41,6 @@
 #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[]
@@ -120,7 +93,7 @@ void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __a
 
 struct xtables_globals iptables_globals = {
 	.option_offset = 0,
-	.program_version = IPTABLES_VERSION,
+	.program_version = PACKAGE_VERSION,
 	.orig_opts = original_opts,
 	.exit_err = iptables_exit_error,
 	.compat_rev = xtables_compatible_revision,
@@ -335,27 +308,6 @@ opt2char(int option)
 	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.
@@ -366,18 +318,6 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds,
 */
 
 /* 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)
@@ -1217,6 +1157,7 @@ int do_command4(int argc, char *argv[], char **table,
 	struct xtables_rule_match *matchp;
 	struct xtables_target *t;
 	unsigned long long cnt;
+	bool table_set = false;
 
 	/* re-set optind to 0 in case do_command4 gets called
 	 * a second time */
@@ -1494,7 +1435,12 @@ int do_command4(int argc, char *argv[], char **table,
 			if (cs.invert)
 				xtables_error(PARAMETER_PROBLEM,
 					   "unexpected ! flag before --table");
+			if (restore && table_set)
+				xtables_error(PARAMETER_PROBLEM,
+					      "The -t option (seen in line %u) cannot be used in %s.\n",
+					      line, xt_params->program_name);
 			*table = optarg;
+			table_set = true;
 			break;
 
 		case 'x':
@@ -1564,7 +1510,7 @@ int do_command4(int argc, char *argv[], char **table,
 					xtables_error(PARAMETER_PROBLEM,
 						   "multiple consecutive ! not"
 						   " allowed");
-				cs.invert = TRUE;
+				cs.invert = true;
 				optarg[0] = '\0';
 				continue;
 			}
@@ -1577,7 +1523,7 @@ int do_command4(int argc, char *argv[], char **table,
 				continue;
 			break;
 		}
-		cs.invert = FALSE;
+		cs.invert = false;
 	}
 
 	if (!wait && wait_interval_set)
diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
index 9805bbe0de87b276a79c8ae41115fd8261addd55..d4a86610ec2171a0663968d79ff8dd81bc43d1ad 100644
--- a/iptables/nft-arp.c
+++ b/iptables/nft-arp.c
@@ -114,29 +114,6 @@ mask_to_dotted(const struct in_addr *mask)
 	return buf;
 }
 
-static void print_mac(const unsigned char *mac, int l)
-{
-	int j;
-
-	for (j = 0; j < l; j++)
-		printf("%02x%s", mac[j],
-			(j==l-1) ? "" : ":");
-}
-
-static void print_mac_and_mask(const unsigned char *mac, const unsigned char *mask, int l)
-{
-	int i;
-
-	print_mac(mac, l);
-	for (i = 0; i < l ; i++)
-		if (mask[i] != 255)
-			break;
-	if (i == l)
-		return;
-	printf("/");
-	print_mac(mask, l);
-}
-
 static bool need_devaddr(struct arpt_devaddr_info *info)
 {
 	int i;
@@ -149,7 +126,7 @@ static bool need_devaddr(struct arpt_devaddr_info *info)
 	return false;
 }
 
-static int nft_arp_add(struct nftnl_rule *r, void *data)
+static int nft_arp_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
 {
 	struct iptables_command_state *cs = data;
 	struct arpt_entry *fw = &cs->arp;
@@ -506,8 +483,8 @@ static void nft_arp_print_rule_details(const struct iptables_command_state *cs,
 	printf("%s%s", sep, fw->arp.invflags & ARPT_INV_SRCDEVADDR
 		? "! " : "");
 	printf("--src-mac ");
-	print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
-		(unsigned char *)fw->arp.src_devaddr.mask, ETH_ALEN);
+	xtables_print_mac_and_mask((unsigned char *)fw->arp.src_devaddr.addr,
+				   (unsigned char *)fw->arp.src_devaddr.mask);
 	sep = " ";
 after_devsrc:
 
@@ -532,8 +509,8 @@ after_devsrc:
 	printf("%s%s", sep, fw->arp.invflags & ARPT_INV_TGTDEVADDR
 		? "! " : "");
 	printf("--dst-mac ");
-	print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
-		(unsigned char *)fw->arp.tgt_devaddr.mask, ETH_ALEN);
+	xtables_print_mac_and_mask((unsigned char *)fw->arp.tgt_devaddr.addr,
+				   (unsigned char *)fw->arp.tgt_devaddr.mask);
 	sep = " ";
 
 after_devdst:
@@ -605,14 +582,15 @@ nft_arp_save_rule(const void *data, unsigned int format)
 }
 
 static void
-nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+nft_arp_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+		   unsigned int num, unsigned int format)
 {
 	struct iptables_command_state cs = {};
 
 	if (format & FMT_LINENUMBERS)
 		printf("%u ", num);
 
-	nft_rule_to_iptables_command_state(r, &cs);
+	nft_rule_to_iptables_command_state(h, r, &cs);
 
 	nft_arp_print_rule_details(&cs, format);
 	print_matches_and_target(&cs, format);
@@ -655,7 +633,7 @@ static bool nft_arp_is_same(const void *data_a,
 				  (unsigned char *)b->arp.outiface_mask);
 }
 
-static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+static bool nft_arp_rule_find(struct nft_handle *h, struct nftnl_rule *r,
 			      void *data)
 {
 	const struct iptables_command_state *cs = data;
@@ -663,7 +641,7 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
 	bool ret = false;
 
 	/* Delete by matching rule case */
-	nft_rule_to_iptables_command_state(r, &this);
+	nft_rule_to_iptables_command_state(h, r, &this);
 
 	if (!nft_arp_is_same(&cs->arp, &this.arp))
 		goto out;
@@ -676,7 +654,7 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
 
 	ret = true;
 out:
-	ops->clear_cs(&this);
+	h->ops->clear_cs(&this);
 	return ret;
 }
 
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
index ddfbee165da937c827e8d416aececd1b1224cb7f..3f85cbbf5e4cf9d5010097d6ad151d3dd49a42ff 100644
--- a/iptables/nft-bridge.c
+++ b/iptables/nft-bridge.c
@@ -17,12 +17,13 @@
 #include <libiptc/libxtc.h>
 #include <linux/netfilter/nf_tables.h>
 
+#include <libnftnl/set.h>
+
 #include "nft-shared.h"
 #include "nft-bridge.h"
+#include "nft-cache.h"
 #include "nft.h"
 
-static bool ebt_legacy_counter_fmt;
-
 void ebt_cs_clean(struct iptables_command_state *cs)
 {
 	struct ebt_match *m, *nm;
@@ -128,7 +129,8 @@ static int _add_action(struct nftnl_rule *r, struct iptables_command_state *cs)
 	return add_action(r, cs, false);
 }
 
-static int nft_bridge_add(struct nftnl_rule *r, void *data)
+static int nft_bridge_add(struct nft_handle *h,
+			  struct nftnl_rule *r, void *data)
 {
 	struct iptables_command_state *cs = data;
 	struct ebt_match *iter;
@@ -184,7 +186,7 @@ static int nft_bridge_add(struct nftnl_rule *r, void *data)
 
 	for (iter = cs->match_list; iter; iter = iter->next) {
 		if (iter->ismatch) {
-			if (add_match(r, iter->u.match->m))
+			if (add_match(h, r, iter->u.match->m))
 				break;
 		} else {
 			if (add_target(r, iter->u.watcher->t))
@@ -292,6 +294,212 @@ static void nft_bridge_parse_immediate(const char *jumpto, bool nft_goto,
 	cs->jumpto = jumpto;
 }
 
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_ether_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+	if (base != 0 || len != ETH_ALEN)
+		return -1;
+
+	switch (offset) {
+	case offsetof(struct ether_header, ether_dhost):
+		return 1;
+	case offsetof(struct ether_header, ether_shost):
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/* return 0 if saddr, 1 if daddr, -1 on error */
+static int
+lookup_check_iphdr_payload(uint32_t base, uint32_t offset, uint32_t len)
+{
+	if (base != 1 || len != 4)
+		return -1;
+
+	switch (offset) {
+	case offsetof(struct iphdr, daddr):
+		return 1;
+	case offsetof(struct iphdr, saddr):
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+/* Make sure previous payload expression(s) is/are consistent and extract if
+ * matching on source or destination address and if matching on MAC and IP or
+ * only MAC address. */
+static int lookup_analyze_payloads(const struct nft_xt_ctx *ctx,
+				   bool *dst, bool *ip)
+{
+	int val, val2 = -1;
+
+	if (ctx->flags & NFT_XT_CTX_PREV_PAYLOAD) {
+		val = lookup_check_ether_payload(ctx->prev_payload.base,
+						 ctx->prev_payload.offset,
+						 ctx->prev_payload.len);
+		if (val < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       ctx->prev_payload.base, ctx->prev_payload.offset,
+			       ctx->prev_payload.len);
+			return -1;
+		}
+		if (!(ctx->flags & NFT_XT_CTX_PAYLOAD)) {
+			DEBUGP("Previous but no current payload?\n");
+			return -1;
+		}
+		val2 = lookup_check_iphdr_payload(ctx->payload.base,
+						  ctx->payload.offset,
+						  ctx->payload.len);
+		if (val2 < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       ctx->payload.base, ctx->payload.offset,
+			       ctx->payload.len);
+			return -1;
+		} else if (val != val2) {
+			DEBUGP("mismatching payload match offsets\n");
+			return -1;
+		}
+	} else if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+		val = lookup_check_ether_payload(ctx->payload.base,
+						 ctx->payload.offset,
+						 ctx->payload.len);
+		if (val < 0) {
+			DEBUGP("unknown payload base/offset/len %d/%d/%d\n",
+			       ctx->payload.base, ctx->payload.offset,
+			       ctx->payload.len);
+			return -1;
+		}
+	} else {
+		DEBUGP("unknown LHS of lookup expression\n");
+		return -1;
+	}
+
+	if (dst)
+		*dst = (val == 1);
+	if (ip)
+		*ip = (val2 != -1);
+	return 0;
+}
+
+static int set_elems_to_among_pairs(struct nft_among_pair *pairs,
+				    const struct nftnl_set *s, int cnt)
+{
+	struct nftnl_set_elems_iter *iter = nftnl_set_elems_iter_create(s);
+	struct nftnl_set_elem *elem;
+	size_t tmpcnt = 0;
+	const void *data;
+	uint32_t datalen;
+	int ret = -1;
+
+	if (!iter) {
+		fprintf(stderr, "BUG: set elems iter allocation failed\n");
+		return ret;
+	}
+
+	while ((elem = nftnl_set_elems_iter_next(iter))) {
+		data = nftnl_set_elem_get(elem, NFTNL_SET_ELEM_KEY, &datalen);
+		if (!data) {
+			fprintf(stderr, "BUG: set elem without key\n");
+			goto err;
+		}
+		if (datalen > sizeof(*pairs)) {
+			fprintf(stderr, "BUG: overlong set elem\n");
+			goto err;
+		}
+		nft_among_insert_pair(pairs, &tmpcnt, data);
+	}
+	ret = 0;
+err:
+	nftnl_set_elems_iter_destroy(iter);
+	return ret;
+}
+
+static struct nftnl_set *set_from_lookup_expr(struct nft_xt_ctx *ctx,
+					      const struct nftnl_expr *e)
+{
+	const char *set_name = nftnl_expr_get_str(e, NFTNL_EXPR_LOOKUP_SET);
+	struct nftnl_set_list *slist;
+
+	slist = nft_set_list_get(ctx->h, ctx->table, set_name);
+	if (slist)
+		return nftnl_set_list_lookup_byname(slist, set_name);
+
+	return NULL;
+}
+
+static void nft_bridge_parse_lookup(struct nft_xt_ctx *ctx,
+				    struct nftnl_expr *e, void *data)
+{
+	struct xtables_match *match = NULL;
+	struct nft_among_data *among_data;
+	bool is_dst, have_ip, inv;
+	struct ebt_match *ematch;
+	struct nftnl_set *s;
+	size_t poff, size;
+	uint32_t cnt;
+
+	if (lookup_analyze_payloads(ctx, &is_dst, &have_ip))
+		return;
+
+	s = set_from_lookup_expr(ctx, e);
+	if (!s)
+		xtables_error(OTHER_PROBLEM,
+			      "BUG: lookup expression references unknown set");
+
+	cnt = nftnl_set_get_u32(s, NFTNL_SET_DESC_SIZE);
+
+	for (ematch = ctx->cs->match_list; ematch; ematch = ematch->next) {
+		if (!ematch->ismatch || strcmp(ematch->u.match->name, "among"))
+			continue;
+
+		match = ematch->u.match;
+		among_data = (struct nft_among_data *)match->m->data;
+
+		size = cnt + among_data->src.cnt + among_data->dst.cnt;
+		size *= sizeof(struct nft_among_pair);
+
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+			sizeof(struct nft_among_data);
+
+		match->m = xtables_realloc(match->m, size);
+		break;
+	}
+	if (!match) {
+		match = xtables_find_match("among", XTF_TRY_LOAD,
+					   &ctx->cs->matches);
+
+		size = cnt * sizeof(struct nft_among_pair);
+		size += XT_ALIGN(sizeof(struct xt_entry_match)) +
+			sizeof(struct nft_among_data);
+
+		match->m = xtables_calloc(1, size);
+		strcpy(match->m->u.user.name, match->name);
+		match->m->u.user.revision = match->revision;
+		xs_init_match(match);
+
+		if (ctx->h->ops->parse_match != NULL)
+			ctx->h->ops->parse_match(match, ctx->cs);
+	}
+	if (!match)
+		return;
+
+	match->m->u.match_size = size;
+
+	inv = !!(nftnl_expr_get_u32(e, NFTNL_EXPR_LOOKUP_FLAGS) &
+				    NFT_LOOKUP_F_INV);
+
+	among_data = (struct nft_among_data *)match->m->data;
+	poff = nft_among_prepare_data(among_data, is_dst, cnt, inv, have_ip);
+	if (set_elems_to_among_pairs(among_data->pairs + poff, s, cnt))
+		xtables_error(OTHER_PROBLEM,
+			      "ebtables among pair parsing failed");
+
+	ctx->flags &= ~(NFT_XT_CTX_PAYLOAD | NFT_XT_CTX_PREV_PAYLOAD);
+}
+
 static void parse_watcher(void *object, struct ebt_match **match_list,
 			  bool ismatch)
 {
@@ -334,11 +542,12 @@ static void nft_bridge_parse_target(struct xtables_target *t, void *data)
 	cs->target = t;
 }
 
-static void nft_rule_to_ebtables_command_state(const struct nftnl_rule *r,
+static void nft_rule_to_ebtables_command_state(struct nft_handle *h,
+					       const struct nftnl_rule *r,
 					       struct iptables_command_state *cs)
 {
 	cs->eb.bitmask = EBT_NOPROTO;
-	nft_rule_to_iptables_command_state(r, cs);
+	nft_rule_to_iptables_command_state(h, r, cs);
 }
 
 static void print_iface(const char *option, const char *name, bool invert)
@@ -422,22 +631,6 @@ static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
 		printf("%s ", ent->e_name);
 }
 
-static void nft_bridge_save_counters(const void *data)
-{
-	const char *ctr;
-
-	if (ebt_legacy_counter_fmt)
-		return;
-
-	ctr = getenv("EBTABLES_SAVE_COUNTER");
-	if (ctr) {
-		ebt_legacy_counter_fmt = true;
-		return;
-	}
-
-	save_counters(data);
-}
-
 static void nft_bridge_save_rule(const void *data, unsigned int format)
 {
 	const struct iptables_command_state *cs = data;
@@ -474,29 +667,30 @@ static void nft_bridge_save_rule(const void *data, unsigned int format)
 		cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC);
 	}
 
-	if (format & FMT_EBT_SAVE)
-		printf(" -c %"PRIu64" %"PRIu64"",
-		       (uint64_t)cs->counters.pcnt,
-		       (uint64_t)cs->counters.bcnt);
-
-	if (!(format & FMT_NOCOUNTS))
-		printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
-		       (uint64_t)cs->counters.pcnt,
-		       (uint64_t)cs->counters.bcnt);
+	if ((format & (FMT_NOCOUNTS | FMT_C_COUNTS)) == FMT_C_COUNTS) {
+		if (format & FMT_EBT_SAVE)
+			printf(" -c %"PRIu64" %"PRIu64"",
+			       (uint64_t)cs->counters.pcnt,
+			       (uint64_t)cs->counters.bcnt);
+		else
+			printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
+			       (uint64_t)cs->counters.pcnt,
+			       (uint64_t)cs->counters.bcnt);
+	}
 
 	if (!(format & FMT_NONEWLINE))
 		fputc('\n', stdout);
 }
 
-static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num,
-				  unsigned int format)
+static void nft_bridge_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+				  unsigned int num, unsigned int format)
 {
 	struct iptables_command_state cs = {};
 
 	if (format & FMT_LINENUMBERS)
 		printf("%d ", num);
 
-	nft_rule_to_ebtables_command_state(r, &cs);
+	nft_rule_to_ebtables_command_state(h, r, &cs);
 	nft_bridge_save_rule(&cs, format);
 	ebt_cs_clean(&cs);
 }
@@ -553,14 +747,14 @@ static bool nft_bridge_is_same(const void *data_a, const void *data_b)
 	return strcmp(a->in, b->in) == 0 && strcmp(a->out, b->out) == 0;
 }
 
-static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+static bool nft_bridge_rule_find(struct nft_handle *h, struct nftnl_rule *r,
 				 void *data)
 {
 	struct iptables_command_state *cs = data;
 	struct iptables_command_state this = {};
 	bool ret = false;
 
-	nft_rule_to_ebtables_command_state(r, &this);
+	nft_rule_to_ebtables_command_state(h, r, &this);
 
 	DEBUGP("comparing with... ");
 
@@ -584,7 +778,7 @@ static bool nft_bridge_rule_find(struct nft_family_ops *ops, struct nftnl_rule *
 
 	ret = true;
 out:
-	ops->clear_cs(&this);
+	h->ops->clear_cs(&this);
 	return ret;
 }
 
@@ -757,13 +951,14 @@ struct nft_family_ops nft_family_ops_bridge = {
 	.parse_meta		= nft_bridge_parse_meta,
 	.parse_payload		= nft_bridge_parse_payload,
 	.parse_immediate	= nft_bridge_parse_immediate,
+	.parse_lookup		= nft_bridge_parse_lookup,
 	.parse_match		= nft_bridge_parse_match,
 	.parse_target		= nft_bridge_parse_target,
 	.print_table_header	= nft_bridge_print_table_header,
 	.print_header		= nft_bridge_print_header,
 	.print_rule		= nft_bridge_print_rule,
 	.save_rule		= nft_bridge_save_rule,
-	.save_counters		= nft_bridge_save_counters,
+	.save_counters		= save_counters,
 	.save_chain		= nft_bridge_save_chain,
 	.post_parse		= NULL,
 	.rule_to_cs		= nft_rule_to_ebtables_command_state,
diff --git a/iptables/nft-bridge.h b/iptables/nft-bridge.h
index d90066f1030a2cc5ef703256044c80bba6694f9b..eb1b3928b6543e7db0bd74cbbe44120e320811b0 100644
--- a/iptables/nft-bridge.h
+++ b/iptables/nft-bridge.h
@@ -122,4 +122,60 @@ void ebt_add_watcher(struct xtables_target *watcher,
                      struct iptables_command_state *cs);
 int ebt_command_default(struct iptables_command_state *cs);
 
+struct nft_among_pair {
+	struct ether_addr ether;
+	struct in_addr in __attribute__((aligned (4)));
+};
+
+struct nft_among_data {
+	struct {
+		size_t cnt;
+		bool inv;
+		bool ip;
+	} src, dst;
+	/* first source, then dest pairs */
+	struct nft_among_pair pairs[0];
+};
+
+/* initialize fields, return offset into pairs array to write pairs to */
+static inline size_t
+nft_among_prepare_data(struct nft_among_data *data, bool dst,
+		       size_t cnt, bool inv, bool ip)
+{
+	size_t poff;
+
+	if (dst) {
+		data->dst.cnt = cnt;
+		data->dst.inv = inv;
+		data->dst.ip = ip;
+		poff = data->src.cnt;
+	} else {
+		data->src.cnt = cnt;
+		data->src.inv = inv;
+		data->src.ip = ip;
+		poff = 0;
+		memmove(data->pairs + cnt, data->pairs,
+			data->dst.cnt * sizeof(*data->pairs));
+	}
+	return poff;
+}
+
+static inline void
+nft_among_insert_pair(struct nft_among_pair *pairs,
+		      size_t *pcount, const struct nft_among_pair *new)
+{
+	int i;
+
+	/* nftables automatically sorts set elements from smallest to largest,
+	 * insert sorted so extension comparison works */
+
+	for (i = 0; i < *pcount; i++) {
+		if (memcmp(new, &pairs[i], sizeof(*new)) < 0)
+			break;
+	}
+	memmove(&pairs[i + 1], &pairs[i], sizeof(*pairs) * (*pcount - i));
+	memcpy(&pairs[i], new, sizeof(*new));
+	(*pcount)++;
+}
+
 #endif
diff --git a/iptables/nft-cache.c b/iptables/nft-cache.c
new file mode 100644
index 0000000000000000000000000000000000000000..7345a27e2894b58bad4224b9ea9066c00f29dc3c
--- /dev/null
+++ b/iptables/nft-cache.c
@@ -0,0 +1,688 @@
+/*
+ * (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 as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <xtables.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libmnl/libmnl.h>
+#include <libnftnl/gen.h>
+#include <libnftnl/set.h>
+#include <libnftnl/table.h>
+
+#include "nft.h"
+#include "nft-cache.h"
+
+static int genid_cb(const struct nlmsghdr *nlh, void *data)
+{
+	uint32_t *genid = data;
+	struct nftnl_gen *gen;
+
+	gen = nftnl_gen_alloc();
+	if (!gen)
+		return MNL_CB_ERROR;
+
+	if (nftnl_gen_nlmsg_parse(nlh, gen) < 0)
+		goto out;
+
+	*genid = nftnl_gen_get_u32(gen, NFTNL_GEN_ID);
+
+	nftnl_gen_free(gen);
+	return MNL_CB_STOP;
+out:
+	nftnl_gen_free(gen);
+	return MNL_CB_ERROR;
+}
+
+static void mnl_genid_get(struct nft_handle *h, uint32_t *genid)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	int ret;
+
+	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETGEN, 0, 0, h->seq);
+	ret = mnl_talk(h, nlh, genid_cb, genid);
+	if (ret == 0)
+		return;
+
+	xtables_error(RESOURCE_PROBLEM,
+		      "Could not fetch rule set generation id: %s\n", nft_strerror(errno));
+}
+
+static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nftnl_table *t;
+	struct nftnl_table_list *list = data;
+
+	t = nftnl_table_alloc();
+	if (t == NULL)
+		goto err;
+
+	if (nftnl_table_nlmsg_parse(nlh, t) < 0)
+		goto out;
+
+	nftnl_table_list_add_tail(t, list);
+
+	return MNL_CB_OK;
+out:
+	nftnl_table_free(t);
+err:
+	return MNL_CB_OK;
+}
+
+static int fetch_table_cache(struct nft_handle *h)
+{
+	char buf[16536];
+	struct nlmsghdr *nlh;
+	struct nftnl_table_list *list;
+	int ret;
+
+	if (h->cache->tables)
+		return 0;
+
+	list = nftnl_table_list_alloc();
+	if (list == NULL)
+		return 0;
+
+	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
+					NLM_F_DUMP, h->seq);
+
+	ret = mnl_talk(h, nlh, nftnl_table_list_cb, list);
+	if (ret < 0 && errno == EINTR)
+		assert(nft_restart(h) >= 0);
+
+	h->cache->tables = list;
+
+	return 1;
+}
+
+struct nftnl_chain_list_cb_data {
+	struct nft_handle *h;
+	const struct builtin_table *t;
+};
+
+static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nftnl_chain_list_cb_data *d = data;
+	const struct builtin_table *t = d->t;
+	struct nftnl_chain_list *list;
+	struct nft_handle *h = d->h;
+	const char *tname, *cname;
+	struct nftnl_chain *c;
+
+	c = nftnl_chain_alloc();
+	if (c == NULL)
+		goto err;
+
+	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
+		goto out;
+
+	tname = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+
+	if (!t) {
+		t = nft_table_builtin_find(h, tname);
+		if (!t)
+			goto out;
+	} else if (strcmp(t->name, tname)) {
+		goto out;
+	}
+
+	list = h->cache->table[t->type].chains;
+	cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+
+	if (nftnl_chain_list_lookup_byname(list, cname))
+		goto out;
+
+	nftnl_chain_list_add_tail(c, list);
+
+	return MNL_CB_OK;
+out:
+	nftnl_chain_free(c);
+err:
+	return MNL_CB_OK;
+}
+
+struct nftnl_set_list_cb_data {
+	struct nft_handle *h;
+	const struct builtin_table *t;
+};
+
+static int nftnl_set_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nftnl_set_list_cb_data *d = data;
+	const struct builtin_table *t = d->t;
+	struct nftnl_set_list *list;
+	struct nft_handle *h = d->h;
+	const char *tname, *sname;
+	struct nftnl_set *s;
+
+	s = nftnl_set_alloc();
+	if (s == NULL)
+		return MNL_CB_OK;
+
+	if (nftnl_set_nlmsg_parse(nlh, s) < 0)
+		goto out_free;
+
+	tname = nftnl_set_get_str(s, NFTNL_SET_TABLE);
+
+	if (!t)
+		t = nft_table_builtin_find(h, tname);
+	else if (strcmp(t->name, tname))
+		goto out_free;
+
+	if (!t)
+		goto out_free;
+
+	list = h->cache->table[t->type].sets;
+	sname = nftnl_set_get_str(s, NFTNL_SET_NAME);
+
+	if (nftnl_set_list_lookup_byname(list, sname))
+		goto out_free;
+
+	nftnl_set_list_add_tail(s, list);
+
+	return MNL_CB_OK;
+out_free:
+	nftnl_set_free(s);
+	return MNL_CB_OK;
+}
+
+static int set_elem_cb(const struct nlmsghdr *nlh, void *data)
+{
+	return nftnl_set_elems_nlmsg_parse(nlh, data) ? -1 : MNL_CB_OK;
+}
+
+static bool set_has_elements(struct nftnl_set *s)
+{
+	struct nftnl_set_elems_iter *iter;
+	bool ret = false;
+
+	iter = nftnl_set_elems_iter_create(s);
+	if (iter) {
+		ret = !!nftnl_set_elems_iter_cur(iter);
+		nftnl_set_elems_iter_destroy(iter);
+	}
+	return ret;
+}
+
+static int set_fetch_elem_cb(struct nftnl_set *s, void *data)
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nft_handle *h = data;
+	struct nlmsghdr *nlh;
+
+	if (set_has_elements(s))
+		return 0;
+
+	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, h->family,
+				    NLM_F_DUMP, h->seq);
+	nftnl_set_elems_nlmsg_build_payload(nlh, s);
+
+	return mnl_talk(h, nlh, set_elem_cb, s);
+}
+
+static int fetch_set_cache(struct nft_handle *h,
+			   const struct builtin_table *t, const char *set)
+{
+	struct nftnl_set_list_cb_data d = {
+		.h = h,
+		.t = t,
+	};
+	struct nlmsghdr *nlh;
+	char buf[16536];
+	int i, ret;
+
+	if (!t) {
+		for (i = 0; i < NFT_TABLE_MAX; i++) {
+			enum nft_table_type type = h->tables[i].type;
+
+			if (!h->tables[i].name)
+				continue;
+
+			h->cache->table[type].sets = nftnl_set_list_alloc();
+			if (!h->cache->table[type].sets)
+				return -1;
+		}
+	} else if (!h->cache->table[t->type].sets) {
+		h->cache->table[t->type].sets = nftnl_set_list_alloc();
+	}
+
+	if (t && set) {
+		struct nftnl_set *s = nftnl_set_alloc();
+
+		if (!s)
+			return -1;
+
+		nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
+						NLM_F_ACK, h->seq);
+		nftnl_set_set_str(s, NFTNL_SET_TABLE, t->name);
+		nftnl_set_set_str(s, NFTNL_SET_NAME, set);
+		nftnl_set_nlmsg_build_payload(nlh, s);
+		nftnl_set_free(s);
+	} else {
+		nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, h->family,
+						NLM_F_DUMP, h->seq);
+	}
+
+	ret = mnl_talk(h, nlh, nftnl_set_list_cb, &d);
+	if (ret < 0 && errno == EINTR) {
+		assert(nft_restart(h) >= 0);
+		return ret;
+	}
+
+	if (t && set) {
+		struct nftnl_set *s;
+
+		s = nftnl_set_list_lookup_byname(h->cache->table[t->type].sets,
+						 set);
+		set_fetch_elem_cb(s, h);
+	} else if (t) {
+		nftnl_set_list_foreach(h->cache->table[t->type].sets,
+				       set_fetch_elem_cb, h);
+	} else {
+		for (i = 0; i < NFT_TABLE_MAX; i++) {
+			enum nft_table_type type = h->tables[i].type;
+
+			if (!h->tables[i].name)
+				continue;
+
+			nftnl_set_list_foreach(h->cache->table[type].sets,
+					       set_fetch_elem_cb, h);
+		}
+	}
+	return ret;
+}
+
+static int fetch_chain_cache(struct nft_handle *h,
+			     const struct builtin_table *t,
+			     const char *chain)
+{
+	struct nftnl_chain_list_cb_data d = {
+		.h = h,
+		.t = t,
+	};
+	char buf[16536];
+	struct nlmsghdr *nlh;
+	int i, ret;
+
+	if (!t) {
+		for (i = 0; i < NFT_TABLE_MAX; i++) {
+			enum nft_table_type type = h->tables[i].type;
+
+			if (!h->tables[i].name)
+				continue;
+
+			if (h->cache->table[type].chains)
+				continue;
+
+			h->cache->table[type].chains = nftnl_chain_list_alloc();
+			if (!h->cache->table[type].chains)
+				return -1;
+		}
+	} else if (!h->cache->table[t->type].chains) {
+		h->cache->table[t->type].chains = nftnl_chain_list_alloc();
+		if (!h->cache->table[t->type].chains)
+			return -1;
+	}
+
+	if (t && chain) {
+		struct nftnl_chain *c = nftnl_chain_alloc();
+
+		if (!c)
+			return -1;
+
+		nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
+						  h->family, NLM_F_ACK,
+						  h->seq);
+		nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, t->name);
+		nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
+		nftnl_chain_nlmsg_build_payload(nlh, c);
+		nftnl_chain_free(c);
+	} else {
+		nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN,
+						  h->family, NLM_F_DUMP,
+						  h->seq);
+	}
+
+	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, &d);
+	if (ret < 0 && errno == EINTR)
+		assert(nft_restart(h) >= 0);
+
+	return ret;
+}
+
+static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nftnl_chain *c = data;
+	struct nftnl_rule *r;
+
+	r = nftnl_rule_alloc();
+	if (r == NULL)
+		return MNL_CB_OK;
+
+	if (nftnl_rule_nlmsg_parse(nlh, r) < 0) {
+		nftnl_rule_free(r);
+		return MNL_CB_OK;
+	}
+
+	nftnl_chain_rule_add_tail(r, c);
+	return MNL_CB_OK;
+}
+
+static int nft_rule_list_update(struct nftnl_chain *c, void *data)
+{
+	struct nft_handle *h = data;
+	char buf[16536];
+	struct nlmsghdr *nlh;
+	struct nftnl_rule *rule;
+	int ret;
+
+	if (nftnl_rule_lookup_byindex(c, 0))
+		return 0;
+
+	rule = nftnl_rule_alloc();
+	if (!rule)
+		return -1;
+
+	nftnl_rule_set_str(rule, NFTNL_RULE_TABLE,
+			   nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
+	nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN,
+			   nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
+
+	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
+					NLM_F_DUMP, h->seq);
+	nftnl_rule_nlmsg_build_payload(nlh, rule);
+
+	ret = mnl_talk(h, nlh, nftnl_rule_list_cb, c);
+	if (ret < 0 && errno == EINTR)
+		assert(nft_restart(h) >= 0);
+
+	nftnl_rule_free(rule);
+
+	if (h->family == NFPROTO_BRIDGE)
+		nft_bridge_chain_postprocess(h, c);
+
+	return 0;
+}
+
+static int fetch_rule_cache(struct nft_handle *h,
+			    const struct builtin_table *t, const char *chain)
+{
+	int i;
+
+	if (t) {
+		struct nftnl_chain_list *list;
+		struct nftnl_chain *c;
+
+		list = h->cache->table[t->type].chains;
+
+		if (chain) {
+			c = nftnl_chain_list_lookup_byname(list, chain);
+			return nft_rule_list_update(c, h);
+		}
+		return nftnl_chain_list_foreach(list, nft_rule_list_update, h);
+	}
+
+	for (i = 0; i < NFT_TABLE_MAX; i++) {
+		enum nft_table_type type = h->tables[i].type;
+
+		if (!h->tables[i].name)
+			continue;
+
+		if (nftnl_chain_list_foreach(h->cache->table[type].chains,
+					     nft_rule_list_update, h))
+			return -1;
+	}
+	return 0;
+}
+
+static void
+__nft_build_cache(struct nft_handle *h, enum nft_cache_level level,
+		  const struct builtin_table *t, const char *set,
+		  const char *chain)
+{
+	uint32_t genid_start, genid_stop;
+
+	if (level <= h->cache_level)
+		return;
+retry:
+	mnl_genid_get(h, &genid_start);
+
+	if (h->cache_level && genid_start != h->nft_genid)
+		flush_chain_cache(h, NULL);
+
+	switch (h->cache_level) {
+	case NFT_CL_NONE:
+		fetch_table_cache(h);
+		if (level == NFT_CL_TABLES)
+			break;
+		/* fall through */
+	case NFT_CL_TABLES:
+		fetch_chain_cache(h, t, chain);
+		if (level == NFT_CL_CHAINS)
+			break;
+		/* fall through */
+	case NFT_CL_CHAINS:
+		fetch_set_cache(h, t, set);
+		if (level == NFT_CL_SETS)
+			break;
+		/* fall through */
+	case NFT_CL_SETS:
+		fetch_rule_cache(h, t, chain);
+		if (level == NFT_CL_RULES)
+			break;
+		/* fall through */
+	case NFT_CL_RULES:
+		break;
+	}
+
+	mnl_genid_get(h, &genid_stop);
+	if (genid_start != genid_stop) {
+		flush_chain_cache(h, NULL);
+		goto retry;
+	}
+
+	if (!t && !chain)
+		h->cache_level = level;
+	else if (h->cache_level < NFT_CL_TABLES)
+		h->cache_level = NFT_CL_TABLES;
+
+	h->nft_genid = genid_start;
+}
+
+void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c)
+{
+	const struct builtin_table *t;
+	const char *table, *chain;
+
+	if (!c)
+		return __nft_build_cache(h, NFT_CL_RULES, NULL, NULL, NULL);
+
+	table = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+	chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+	t = nft_table_builtin_find(h, table);
+	__nft_build_cache(h, NFT_CL_RULES, t, NULL, chain);
+}
+
+void nft_fake_cache(struct nft_handle *h)
+{
+	int i;
+
+	fetch_table_cache(h);
+	for (i = 0; i < NFT_TABLE_MAX; i++) {
+		enum nft_table_type type = h->tables[i].type;
+
+		if (!h->tables[i].name)
+			continue;
+
+		h->cache->table[type].chains = nftnl_chain_list_alloc();
+	}
+	h->cache_level = NFT_CL_RULES;
+	mnl_genid_get(h, &h->nft_genid);
+}
+
+static void __nft_flush_cache(struct nft_handle *h)
+{
+	if (!h->cache_index) {
+		h->cache_index++;
+		h->cache = &h->__cache[h->cache_index];
+	} else {
+		flush_chain_cache(h, NULL);
+	}
+}
+
+static int ____flush_rule_cache(struct nftnl_rule *r, void *data)
+{
+	nftnl_rule_list_del(r);
+	nftnl_rule_free(r);
+
+	return 0;
+}
+
+static int __flush_rule_cache(struct nftnl_chain *c, void *data)
+{
+	return nftnl_rule_foreach(c, ____flush_rule_cache, NULL);
+}
+
+int flush_rule_cache(struct nft_handle *h, const char *table,
+		     struct nftnl_chain *c)
+{
+	const struct builtin_table *t;
+
+	if (c)
+		return __flush_rule_cache(c, NULL);
+
+	t = nft_table_builtin_find(h, table);
+	if (!t || !h->cache->table[t->type].chains)
+		return 0;
+
+	return nftnl_chain_list_foreach(h->cache->table[t->type].chains,
+					__flush_rule_cache, NULL);
+}
+
+static int __flush_chain_cache(struct nftnl_chain *c, void *data)
+{
+	nftnl_chain_list_del(c);
+	nftnl_chain_free(c);
+
+	return 0;
+}
+
+static int __flush_set_cache(struct nftnl_set *s, void *data)
+{
+	nftnl_set_list_del(s);
+	nftnl_set_free(s);
+
+	return 0;
+}
+
+static int flush_cache(struct nft_handle *h, struct nft_cache *c,
+		       const char *tablename)
+{
+	const struct builtin_table *table;
+	int i;
+
+	if (tablename) {
+		table = nft_table_builtin_find(h, tablename);
+		if (!table)
+			return 0;
+		if (c->table[table->type].chains)
+			nftnl_chain_list_foreach(c->table[table->type].chains,
+						 __flush_chain_cache, NULL);
+		if (c->table[table->type].sets)
+			nftnl_set_list_foreach(c->table[table->type].sets,
+					       __flush_set_cache, NULL);
+		return 0;
+	}
+
+	for (i = 0; i < NFT_TABLE_MAX; i++) {
+		if (h->tables[i].name == NULL)
+			continue;
+
+		if (!c->table[i].chains)
+			continue;
+
+		nftnl_chain_list_free(c->table[i].chains);
+		c->table[i].chains = NULL;
+		if (c->table[i].sets)
+			nftnl_set_list_free(c->table[i].sets);
+		c->table[i].sets = NULL;
+	}
+	nftnl_table_list_free(c->tables);
+	c->tables = NULL;
+
+	return 1;
+}
+
+void flush_chain_cache(struct nft_handle *h, const char *tablename)
+{
+	if (!h->cache_level)
+		return;
+
+	if (flush_cache(h, h->cache, tablename))
+		h->cache_level = NFT_CL_NONE;
+}
+
+void nft_rebuild_cache(struct nft_handle *h)
+{
+	enum nft_cache_level level = h->cache_level;
+
+	if (h->cache_level)
+		__nft_flush_cache(h);
+
+	h->cache_level = NFT_CL_NONE;
+	__nft_build_cache(h, level, NULL, NULL, NULL);
+}
+
+void nft_release_cache(struct nft_handle *h)
+{
+	if (h->cache_index)
+		flush_cache(h, &h->__cache[0], NULL);
+}
+
+struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
+{
+	__nft_build_cache(h, NFT_CL_TABLES, NULL, NULL, NULL);
+
+	return h->cache->tables;
+}
+
+struct nftnl_set_list *
+nft_set_list_get(struct nft_handle *h, const char *table, const char *set)
+{
+	const struct builtin_table *t;
+
+	t = nft_table_builtin_find(h, table);
+	if (!t)
+		return NULL;
+
+	__nft_build_cache(h, NFT_CL_RULES, t, set, NULL);
+
+	return h->cache->table[t->type].sets;
+}
+
+struct nftnl_chain_list *
+nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain)
+{
+	const struct builtin_table *t;
+
+	t = nft_table_builtin_find(h, table);
+	if (!t)
+		return NULL;
+
+	__nft_build_cache(h, NFT_CL_CHAINS, t, NULL, chain);
+
+	return h->cache->table[t->type].chains;
+}
+
diff --git a/iptables/nft-cache.h b/iptables/nft-cache.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed498835676e2f88d187e6a5b43c7ec5d8078a87
--- /dev/null
+++ b/iptables/nft-cache.h
@@ -0,0 +1,20 @@
+#ifndef _NFT_CACHE_H_
+#define _NFT_CACHE_H_
+
+struct nft_handle;
+
+void nft_fake_cache(struct nft_handle *h);
+void nft_build_cache(struct nft_handle *h, struct nftnl_chain *c);
+void nft_rebuild_cache(struct nft_handle *h);
+void nft_release_cache(struct nft_handle *h);
+void flush_chain_cache(struct nft_handle *h, const char *tablename);
+int flush_rule_cache(struct nft_handle *h, const char *table,
+		     struct nftnl_chain *c);
+
+struct nftnl_chain_list *
+nft_chain_list_get(struct nft_handle *h, const char *table, const char *chain);
+struct nftnl_set_list *
+nft_set_list_get(struct nft_handle *h, const char *table, const char *set);
+struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h);
+
+#endif /* _NFT_CACHE_H_ */
diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
index 4497eb9b9347c4ba18a7f32ec78d9c62f9201a09..70634f8fad84d9fa28f33f623c1ac942c65b30f4 100644
--- a/iptables/nft-ipv4.c
+++ b/iptables/nft-ipv4.c
@@ -26,7 +26,7 @@
 #include "nft.h"
 #include "nft-shared.h"
 
-static int nft_ipv4_add(struct nftnl_rule *r, void *data)
+static int nft_ipv4_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
 {
 	struct iptables_command_state *cs = data;
 	struct xtables_rule_match *matchp;
@@ -77,7 +77,7 @@ static int nft_ipv4_add(struct nftnl_rule *r, void *data)
 	add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
 
 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
-		ret = add_match(r, matchp->match->m);
+		ret = add_match(h, r, matchp->match->m);
 		if (ret < 0)
 			return ret;
 	}
@@ -261,12 +261,12 @@ static void print_fragment(unsigned int flags, unsigned int invflags,
 	fputc(' ', stdout);
 }
 
-static void nft_ipv4_print_rule(struct nftnl_rule *r, unsigned int num,
-				unsigned int format)
+static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+				unsigned int num, unsigned int format)
 {
 	struct iptables_command_state cs = {};
 
-	nft_rule_to_iptables_command_state(r, &cs);
+	nft_rule_to_iptables_command_state(h, r, &cs);
 
 	print_rule_details(&cs, cs.jumpto, cs.fw.ip.flags,
 			   cs.fw.ip.invflags, cs.fw.ip.proto, num, format);
diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
index cacb1c9e141f27c3840d3e34ddcf0c34215175c0..d01491bfdb689572e14bea876b939332d401c39b 100644
--- a/iptables/nft-ipv6.c
+++ b/iptables/nft-ipv6.c
@@ -25,7 +25,7 @@
 #include "nft.h"
 #include "nft-shared.h"
 
-static int nft_ipv6_add(struct nftnl_rule *r, void *data)
+static int nft_ipv6_add(struct nft_handle *h, struct nftnl_rule *r, void *data)
 {
 	struct iptables_command_state *cs = data;
 	struct xtables_rule_match *matchp;
@@ -66,7 +66,7 @@ static int nft_ipv6_add(struct nftnl_rule *r, void *data)
 	add_compat(r, cs->fw6.ipv6.proto, cs->fw6.ipv6.invflags & XT_INV_PROTO);
 
 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
-		ret = add_match(r, matchp->match->m);
+		ret = add_match(h, r, matchp->match->m);
 		if (ret < 0)
 			return ret;
 	}
@@ -187,12 +187,12 @@ static void nft_ipv6_parse_immediate(const char *jumpto, bool nft_goto,
 		cs->fw6.ipv6.flags |= IP6T_F_GOTO;
 }
 
-static void nft_ipv6_print_rule(struct nftnl_rule *r, unsigned int num,
-				unsigned int format)
+static void nft_ipv6_print_rule(struct nft_handle *h, struct nftnl_rule *r,
+				unsigned int num, unsigned int format)
 {
 	struct iptables_command_state cs = {};
 
-	nft_rule_to_iptables_command_state(r, &cs);
+	nft_rule_to_iptables_command_state(h, r, &cs);
 
 	print_rule_details(&cs, cs.jumpto, cs.fw6.ipv6.flags,
 			   cs.fw6.ipv6.invflags, cs.fw6.ipv6.proto,
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 1c09277d85fb5b0dab276d1ab5715b7fd9a304ca..78e422781723f3910ce9dec5c214f9d4d1dd7029 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -69,7 +69,7 @@ void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base)
 }
 
 /* bitwise operation is = sreg & mask ^ xor */
-void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor)
+void add_bitwise_u16(struct nftnl_rule *r, uint16_t mask, uint16_t xor)
 {
 	struct nftnl_expr *expr;
 
@@ -310,7 +310,6 @@ static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	struct xtables_target *target;
 	struct xt_entry_target *t;
 	size_t size;
-	struct nft_family_ops *ops;
 	void *data = ctx->cs;
 
 	target = xtables_find_target(targname, XTF_TRY_LOAD);
@@ -327,8 +326,7 @@ static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 	target->t = t;
 
-	ops = nft_family_ops_lookup(ctx->family);
-	ops->parse_target(target, data);
+	ctx->h->ops->parse_target(target, data);
 }
 
 static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -339,9 +337,8 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	struct xtables_match *match;
 	struct xtables_rule_match **matches;
 	struct xt_entry_match *m;
-	struct nft_family_ops *ops;
 
-	switch (ctx->family) {
+	switch (ctx->h->family) {
 	case NFPROTO_IPV4:
 	case NFPROTO_IPV6:
 	case NFPROTO_BRIDGE:
@@ -349,7 +346,7 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 		break;
 	default:
 		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
-			ctx->family);
+			ctx->h->family);
 		exit(EXIT_FAILURE);
 	}
 
@@ -365,9 +362,8 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 	match->m = m;
 
-	ops = nft_family_ops_lookup(ctx->family);
-	if (ops->parse_match != NULL)
-		ops->parse_match(match, ctx->cs);
+	if (ctx->h->ops->parse_match != NULL)
+		ctx->h->ops->parse_match(match, ctx->cs);
 }
 
 void print_proto(uint16_t proto, int invert)
@@ -400,7 +396,6 @@ void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv)
 
 static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
 {
-	const struct nft_family_ops *ops;
 	struct xtables_target *target;
 	struct xt_entry_target *t;
 	unsigned int size;
@@ -429,8 +424,7 @@ static void nft_meta_set_to_target(struct nft_xt_ctx *ctx)
 
 	target->t = t;
 
-	ops = nft_family_ops_lookup(ctx->family);
-	ops->parse_target(target, ctx->cs);
+	ctx->h->ops->parse_target(target, ctx->cs);
 }
 
 static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -451,8 +445,16 @@ static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
+	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
+		memcpy(&ctx->prev_payload, &ctx->payload,
+		       sizeof(ctx->prev_payload));
+		ctx->flags |= NFT_XT_CTX_PREV_PAYLOAD;
+	}
+
 	ctx->reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
+	ctx->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
 	ctx->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
+	ctx->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
 	ctx->flags |= NFT_XT_CTX_PAYLOAD;
 }
 
@@ -474,7 +476,6 @@ static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 
 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
-	struct nft_family_ops *ops = nft_family_ops_lookup(ctx->family);
 	void *data = ctx->cs;
 	uint32_t reg;
 
@@ -483,12 +484,12 @@ static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 		return;
 
 	if (ctx->flags & NFT_XT_CTX_META) {
-		ops->parse_meta(ctx, e, data);
+		ctx->h->ops->parse_meta(ctx, e, data);
 		ctx->flags &= ~NFT_XT_CTX_META;
 	}
 	/* bitwise context is interpreted from payload */
 	if (ctx->flags & NFT_XT_CTX_PAYLOAD) {
-		ops->parse_payload(ctx, e, data);
+		ctx->h->ops->parse_payload(ctx, e, data);
 		ctx->flags &= ~NFT_XT_CTX_PAYLOAD;
 	}
 }
@@ -502,7 +503,6 @@ static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters
 static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 {
 	const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
-	struct nft_family_ops *ops;
 	const char *jumpto = NULL;
 	bool nft_goto = false;
 	void *data = ctx->cs;
@@ -544,8 +544,7 @@ static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 		break;
 	}
 
-	ops = nft_family_ops_lookup(ctx->family);
-	ops->parse_immediate(jumpto, nft_goto, data);
+	ctx->h->ops->parse_immediate(jumpto, nft_goto, data);
 }
 
 static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
@@ -555,19 +554,18 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
 	struct xtables_rule_match **matches;
 	struct xtables_match *match;
-	struct nft_family_ops *ops;
 	struct xt_rateinfo *rinfo;
 	size_t size;
 
-	switch (ctx->family) {
+	switch (ctx->h->family) {
 	case NFPROTO_IPV4:
 	case NFPROTO_IPV6:
 	case NFPROTO_BRIDGE:
 		matches = &ctx->cs->matches;
 		break;
 	default:
-		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
-			ctx->family);
+		fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
+			ctx->h->family);
 		exit(EXIT_FAILURE);
 	}
 
@@ -586,20 +584,27 @@ static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
 	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
 	rinfo->burst = burst;
 
-	ops = nft_family_ops_lookup(ctx->family);
-	if (ops->parse_match != NULL)
-		ops->parse_match(match, ctx->cs);
+	if (ctx->h->ops->parse_match != NULL)
+		ctx->h->ops->parse_match(match, ctx->cs);
+}
+
+static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
+			     struct nftnl_expr *e)
+{
+	if (ctx->h->ops->parse_lookup)
+		ctx->h->ops->parse_lookup(ctx, e, NULL);
 }
 
-void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+void nft_rule_to_iptables_command_state(struct nft_handle *h,
+					const struct nftnl_rule *r,
 					struct iptables_command_state *cs)
 {
 	struct nftnl_expr_iter *iter;
 	struct nftnl_expr *expr;
-	int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
 	struct nft_xt_ctx ctx = {
 		.cs = cs,
-		.family = family,
+		.h = h,
+		.table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
 	};
 
 	iter = nftnl_expr_iter_create(r);
@@ -630,6 +635,8 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
 			nft_parse_target(&ctx, expr);
 		else if (strcmp(name, "limit") == 0)
 			nft_parse_limit(&ctx, expr);
+		else if (strcmp(name, "lookup") == 0)
+			nft_parse_lookup(&ctx, h, expr);
 
 		expr = nftnl_expr_iter_next(iter);
 	}
@@ -982,19 +989,18 @@ void nft_ipv46_parse_target(struct xtables_target *t, void *data)
 	cs->target = t;
 }
 
-bool nft_ipv46_rule_find(struct nft_family_ops *ops,
-			 struct nftnl_rule *r, void *data)
+bool nft_ipv46_rule_find(struct nft_handle *h, struct nftnl_rule *r, void *data)
 {
 	struct iptables_command_state *cs = data, this = {};
 	bool ret = false;
 
-	nft_rule_to_iptables_command_state(r, &this);
+	nft_rule_to_iptables_command_state(h, r, &this);
 
 	DEBUGP("comparing with... ");
 #ifdef DEBUG_DEL
 	nft_rule_print_save(r, NFT_RULE_APPEND, 0);
 #endif
-	if (!ops->is_same(cs, &this))
+	if (!h->ops->is_same(cs, &this))
 		goto out;
 
 	if (!compare_matches(cs->matches, this.matches)) {
@@ -1014,7 +1020,7 @@ bool nft_ipv46_rule_find(struct nft_family_ops *ops,
 
 	ret = true;
 out:
-	ops->clear_cs(&this);
+	h->ops->clear_cs(&this);
 	return ret;
 }
 
diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
index de889ead7b60582d0c36ddb227e0e0834ff25beb..bee99a7dd0c9301d50211a57b08ffec4440001d0 100644
--- a/iptables/nft-shared.h
+++ b/iptables/nft-shared.h
@@ -35,6 +35,7 @@
 #define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
 
 struct xtables_args;
+struct nft_handle;
 struct xt_xlate;
 
 enum {
@@ -42,19 +43,22 @@ enum {
 	NFT_XT_CTX_META		= (1 << 1),
 	NFT_XT_CTX_BITWISE	= (1 << 2),
 	NFT_XT_CTX_IMMEDIATE	= (1 << 3),
+	NFT_XT_CTX_PREV_PAYLOAD	= (1 << 4),
 };
 
 struct nft_xt_ctx {
 	struct iptables_command_state *cs;
 	struct nftnl_expr_iter *iter;
-	int family;
+	struct nft_handle *h;
 	uint32_t flags;
+	const char *table;
 
 	uint32_t reg;
 	struct {
+		uint32_t base;
 		uint32_t offset;
 		uint32_t len;
-	} payload;
+	} payload, prev_payload;
 	struct {
 		uint32_t key;
 	} meta;
@@ -69,7 +73,7 @@ struct nft_xt_ctx {
 };
 
 struct nft_family_ops {
-	int (*add)(struct nftnl_rule *r, void *data);
+	int (*add)(struct nft_handle *h, struct nftnl_rule *r, void *data);
 	bool (*is_same)(const void *data_a,
 			const void *data_b);
 	void (*print_payload)(struct nftnl_expr *e,
@@ -82,6 +86,8 @@ struct nft_family_ops {
 			      void *data);
 	void (*parse_cmp)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
 			  void *data);
+	void (*parse_lookup)(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
+			     void *data);
 	void (*parse_immediate)(const char *jumpto, bool nft_goto, void *data);
 
 	void (*print_table_header)(const char *tablename);
@@ -89,8 +95,8 @@ struct nft_family_ops {
 			     const char *pol,
 			     const struct xt_counters *counters, bool basechain,
 			     uint32_t refs, uint32_t entries);
-	void (*print_rule)(struct nftnl_rule *r, unsigned int num,
-			   unsigned int format);
+	void (*print_rule)(struct nft_handle *h, struct nftnl_rule *r,
+			   unsigned int num, unsigned int format);
 	void (*save_rule)(const void *data, unsigned int format);
 	void (*save_counters)(const void *data);
 	void (*save_chain)(const struct nftnl_chain *c, const char *policy);
@@ -100,10 +106,10 @@ struct nft_family_ops {
 			   struct xtables_args *args);
 	void (*parse_match)(struct xtables_match *m, void *data);
 	void (*parse_target)(struct xtables_target *t, void *data);
-	void (*rule_to_cs)(const struct nftnl_rule *r,
+	void (*rule_to_cs)(struct nft_handle *h, const struct nftnl_rule *r,
 			   struct iptables_command_state *cs);
 	void (*clear_cs)(struct iptables_command_state *cs);
-	bool (*rule_find)(struct nft_family_ops *ops, struct nftnl_rule *r,
+	bool (*rule_find)(struct nft_handle *h, struct nftnl_rule *r,
 			  void *data);
 	int (*xlate)(const void *data, struct xt_xlate *xl);
 };
@@ -111,7 +117,7 @@ struct nft_family_ops {
 void add_meta(struct nftnl_rule *r, uint32_t key);
 void add_payload(struct nftnl_rule *r, int offset, int len, uint32_t base);
 void add_bitwise(struct nftnl_rule *r, uint8_t *mask, size_t len);
-void add_bitwise_u16(struct nftnl_rule *r, int mask, int xor);
+void add_bitwise_u16(struct nftnl_rule *r, uint16_t mask, uint16_t xor);
 void add_cmp_ptr(struct nftnl_rule *r, uint32_t op, void *data, size_t len);
 void add_cmp_u8(struct nftnl_rule *r, uint8_t val, uint32_t op);
 void add_cmp_u16(struct nftnl_rule *r, uint16_t val, uint32_t op);
@@ -137,7 +143,8 @@ int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface,
 		unsigned char *outiface_mask, uint8_t *invflags);
 void print_proto(uint16_t proto, int invert);
 void get_cmp_data(struct nftnl_expr *e, void *data, size_t dlen, bool *inv);
-void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+void nft_rule_to_iptables_command_state(struct nft_handle *h,
+					const struct nftnl_rule *r,
 					struct iptables_command_state *cs);
 void nft_clear_iptables_command_state(struct iptables_command_state *cs);
 void print_header(unsigned int format, const char *chain, const char *pol,
@@ -163,9 +170,8 @@ void save_matches_and_target(const struct iptables_command_state *cs,
 
 struct nft_family_ops *nft_family_ops_lookup(int family);
 
-struct nft_handle;
 void nft_ipv46_parse_target(struct xtables_target *t, void *data);
-bool nft_ipv46_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+bool nft_ipv46_rule_find(struct nft_handle *h, struct nftnl_rule *r,
 			 void *data);
 
 bool compare_matches(struct xtables_rule_match *mt1, struct xtables_rule_match *mt2);
@@ -199,23 +205,6 @@ struct xtables_args {
 	unsigned long long pcnt_cnt, bcnt_cnt;
 };
 
-#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
-
 struct nft_xt_cmd_parse {
 	unsigned int			command;
 	unsigned int			rulenum;
@@ -232,19 +221,10 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 	      struct nft_xt_cmd_parse *p, struct iptables_command_state *cs,
 	      struct xtables_args *args);
 
-struct nft_xt_restore_parse {
-	FILE		*in;
-	int		testing;
-	const char	*tablename;
-	bool		commit;
-};
-
 struct nftnl_chain_list;
 
 struct nft_xt_restore_cb {
 	void (*table_new)(struct nft_handle *h, const char *table);
-	struct nftnl_chain_list *(*chain_list)(struct nft_handle *h,
-					       const char *table);
 	int (*chain_set)(struct nft_handle *h, const char *table,
 			 const char *chain, const char *policy,
 			 const struct xt_counters *counters);
@@ -260,10 +240,16 @@ struct nft_xt_restore_cb {
 	int (*abort)(struct nft_handle *h);
 };
 
+struct nft_xt_restore_parse {
+	FILE				*in;
+	int				testing;
+	const char			*tablename;
+	bool				commit;
+	const struct nft_xt_restore_cb	*cb;
+};
+
 void xtables_restore_parse(struct nft_handle *h,
-			   struct nft_xt_restore_parse *p,
-			   struct nft_xt_restore_cb *cb,
-			   int argc, char *argv[]);
+			   const struct nft_xt_restore_parse *p);
 
 void nft_check_xt_legacy(int family, bool is_ipt_save);
 #endif
diff --git a/iptables/nft.c b/iptables/nft.c
index 2c61521455de85275f00160d0a7fdcfd9a8923f9..3f2a62ae12c07a9cabfd334a787b5a2b33f60c19 100644
--- a/iptables/nft.c
+++ b/iptables/nft.c
@@ -55,48 +55,18 @@
 
 #include "nft.h"
 #include "xshared.h" /* proto_to_name */
+#include "nft-cache.h"
 #include "nft-shared.h"
 #include "nft-bridge.h" /* EBT_NOPROTO */
-#include "xtables-config-parser.h"
 
 static void *nft_fn;
 
-static int genid_cb(const struct nlmsghdr *nlh, void *data)
-{
-	uint32_t *genid = data;
-	struct nftnl_gen *gen;
-
-	gen = nftnl_gen_alloc();
-	if (!gen)
-		return MNL_CB_ERROR;
-
-	if (nftnl_gen_nlmsg_parse(nlh, gen) < 0)
-		goto out;
-
-	*genid = nftnl_gen_get_u32(gen, NFTNL_GEN_ID);
-
-	nftnl_gen_free(gen);
-	return MNL_CB_STOP;
-out:
-	nftnl_gen_free(gen);
-	return MNL_CB_ERROR;
-}
-
-static int mnl_genid_get(struct nft_handle *h, uint32_t *genid)
-{
-	char buf[MNL_SOCKET_BUFFER_SIZE];
-	struct nlmsghdr *nlh;
-
-	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETGEN, 0, 0, h->seq);
-	return mnl_talk(h, nlh, genid_cb, genid);
-}
-
 int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
 	     int (*cb)(const struct nlmsghdr *nlh, void *data),
 	     void *data)
 {
 	int ret;
-	char buf[16536];
+	char buf[32768];
 
 	if (mnl_socket_sendto(h->nl, nlh, nlh->nlmsg_len) < 0)
 		return -1;
@@ -186,33 +156,42 @@ static void mnl_err_list_free(struct mnl_err *err)
 	free(err);
 }
 
-static int nlbuffsiz;
-
-static void mnl_set_sndbuffer(const struct mnl_socket *nl,
-			      struct nftnl_batch *batch)
+static void mnl_set_sndbuffer(struct nft_handle *h)
 {
-	int newbuffsiz;
+	int newbuffsiz = nftnl_batch_iovec_len(h->batch) * BATCH_PAGE_SIZE;
 
-	if (nftnl_batch_iovec_len(batch) * BATCH_PAGE_SIZE <= nlbuffsiz)
+	if (newbuffsiz <= h->nlsndbuffsiz)
 		return;
 
-	newbuffsiz = nftnl_batch_iovec_len(batch) * BATCH_PAGE_SIZE;
-
 	/* Rise sender buffer length to avoid hitting -EMSGSIZE */
-	if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE,
+	if (setsockopt(mnl_socket_get_fd(h->nl), SOL_SOCKET, SO_SNDBUFFORCE,
+		       &newbuffsiz, sizeof(socklen_t)) < 0)
+		return;
+
+	h->nlsndbuffsiz = newbuffsiz;
+}
+
+static void mnl_set_rcvbuffer(struct nft_handle *h, int numcmds)
+{
+	int newbuffsiz = getpagesize() * numcmds;
+
+	if (newbuffsiz <= h->nlrcvbuffsiz)
+		return;
+
+	/* Rise receiver buffer length to avoid hitting -ENOBUFS */
+	if (setsockopt(mnl_socket_get_fd(h->nl), SOL_SOCKET, SO_RCVBUFFORCE,
 		       &newbuffsiz, sizeof(socklen_t)) < 0)
 		return;
 
-	nlbuffsiz = newbuffsiz;
+	h->nlrcvbuffsiz = newbuffsiz;
 }
 
-static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nf_sock,
-				      struct nftnl_batch *batch)
+static ssize_t mnl_nft_socket_sendmsg(struct nft_handle *h, int numcmds)
 {
 	static const struct sockaddr_nl snl = {
 		.nl_family = AF_NETLINK
 	};
-	uint32_t iov_len = nftnl_batch_iovec_len(batch);
+	uint32_t iov_len = nftnl_batch_iovec_len(h->batch);
 	struct iovec iov[iov_len];
 	struct msghdr msg = {
 		.msg_name	= (struct sockaddr *) &snl,
@@ -221,16 +200,16 @@ static ssize_t mnl_nft_socket_sendmsg(const struct mnl_socket *nf_sock,
 		.msg_iovlen	= iov_len,
 	};
 
-	mnl_set_sndbuffer(nf_sock, batch);
-	nftnl_batch_iovec(batch, iov, iov_len);
+	mnl_set_sndbuffer(h);
+	mnl_set_rcvbuffer(h, numcmds);
+	nftnl_batch_iovec(h->batch, iov, iov_len);
 
-	return sendmsg(mnl_socket_get_fd(nf_sock), &msg, 0);
+	return sendmsg(mnl_socket_get_fd(h->nl), &msg, 0);
 }
 
-static int mnl_batch_talk(const struct mnl_socket *nf_sock,
-			  struct nftnl_batch *batch, struct list_head *err_list)
+static int mnl_batch_talk(struct nft_handle *h, int numcmds)
 {
-	const struct mnl_socket *nl = nf_sock;
+	const struct mnl_socket *nl = h->nl;
 	int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl);
 	char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
 	fd_set readfds;
@@ -240,7 +219,7 @@ static int mnl_batch_talk(const struct mnl_socket *nf_sock,
 	};
 	int err = 0;
 
-	ret = mnl_nft_socket_sendmsg(nf_sock, batch);
+	ret = mnl_nft_socket_sendmsg(h, numcmds);
 	if (ret == -1)
 		return -1;
 
@@ -262,7 +241,8 @@ static int mnl_batch_talk(const struct mnl_socket *nf_sock,
 		ret = mnl_cb_run(rcv_buf, ret, 0, portid, NULL, NULL);
 		/* Continue on error, make sure we get all acknowledgments */
 		if (ret == -1) {
-			mnl_err_list_node_add(err_list, errno, nlh->nlmsg_seq);
+			mnl_err_list_node_add(&h->err_list, errno,
+					      nlh->nlmsg_seq);
 			err = -1;
 		}
 
@@ -291,6 +271,7 @@ enum obj_update_type {
 	NFT_COMPAT_RULE_REPLACE,
 	NFT_COMPAT_RULE_DELETE,
 	NFT_COMPAT_RULE_FLUSH,
+	NFT_COMPAT_SET_ADD,
 };
 
 enum obj_action {
@@ -308,6 +289,7 @@ struct obj_update {
 		struct nftnl_table	*table;
 		struct nftnl_chain	*chain;
 		struct nftnl_rule	*rule;
+		struct nftnl_set	*set;
 		void			*ptr;
 	};
 	struct {
@@ -335,6 +317,7 @@ static int mnl_append_error(const struct nft_handle *h,
 		[NFT_COMPAT_RULE_REPLACE] = "RULE_REPLACE",
 		[NFT_COMPAT_RULE_DELETE] = "RULE_DELETE",
 		[NFT_COMPAT_RULE_FLUSH] = "RULE_FLUSH",
+		[NFT_COMPAT_SET_ADD] = "SET_ADD",
 	};
 	char errmsg[256];
 	char tcr[128];
@@ -371,10 +354,14 @@ static int mnl_append_error(const struct nft_handle *h,
 			 nftnl_rule_get_str(o->rule, NFTNL_RULE_CHAIN));
 #if 0
 		{
-			nft_rule_print_save(o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
+			nft_rule_print_save(h, o->rule, NFT_RULE_APPEND, FMT_NOCOUNTS);
 		}
 #endif
 		break;
+	case NFT_COMPAT_SET_ADD:
+		snprintf(tcr, sizeof(tcr), "set %s",
+			 nftnl_set_get_str(o->set, NFTNL_SET_NAME));
+		break;
 	}
 
 	return snprintf(buf, len, "%s: %s", errmsg, tcr);
@@ -404,6 +391,13 @@ batch_table_add(struct nft_handle *h, enum obj_update_type type,
 	return batch_add(h, type, t);
 }
 
+static struct obj_update *
+batch_set_add(struct nft_handle *h, enum obj_update_type type,
+	      struct nftnl_set *s)
+{
+	return batch_add(h, type, s);
+}
+
 static int batch_chain_add(struct nft_handle *h, enum obj_update_type type,
 			   struct nftnl_chain *c)
 {
@@ -647,7 +641,7 @@ static int nft_table_builtin_add(struct nft_handle *h,
 	if (t == NULL)
 		return -1;
 
-	nftnl_table_set(t, NFTNL_TABLE_NAME, (char *)_t->name);
+	nftnl_table_set_str(t, NFTNL_TABLE_NAME, _t->name);
 
 	ret = batch_table_add(h, NFT_COMPAT_TABLE_ADD, t) ? 0 : - 1;
 
@@ -664,12 +658,12 @@ nft_chain_builtin_alloc(const struct builtin_table *table,
 	if (c == NULL)
 		return NULL;
 
-	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table->name);
-	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain->name);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table->name);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain->name);
 	nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, chain->hook);
 	nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, chain->prio);
 	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, policy);
-	nftnl_chain_set(c, NFTNL_CHAIN_TYPE, (char *)chain->type);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_TYPE, chain->type);
 
 	return c;
 }
@@ -688,31 +682,25 @@ static void nft_chain_builtin_add(struct nft_handle *h,
 	nftnl_chain_list_add_tail(c, h->cache->table[table->type].chains);
 }
 
-static const struct builtin_table *
-__nft_table_builtin_find(const struct builtin_table *tables, const char *table)
+/* find if built-in table already exists */
+const struct builtin_table *
+nft_table_builtin_find(struct nft_handle *h, const char *table)
 {
 	int i;
 	bool found = false;
 
 	for (i = 0; i < NFT_TABLE_MAX; i++) {
-		if (tables[i].name == NULL)
+		if (h->tables[i].name == NULL)
 			continue;
 
-		if (strcmp(tables[i].name, table) != 0)
+		if (strcmp(h->tables[i].name, table) != 0)
 			continue;
 
 		found = true;
 		break;
 	}
 
-	return found ? &tables[i] : NULL;
-}
-
-/* find if built-in table already exists */
-const struct builtin_table *
-nft_table_builtin_find(struct nft_handle *h, const char *table)
-{
-	return __nft_table_builtin_find(h->tables, table);
+	return found ? &h->tables[i] : NULL;
 }
 
 /* find if built-in chain already exists */
@@ -735,15 +723,16 @@ nft_chain_builtin_find(const struct builtin_table *t, const char *chain)
 static void nft_chain_builtin_init(struct nft_handle *h,
 				   const struct builtin_table *table)
 {
-	struct nftnl_chain_list *list = nft_chain_list_get(h, table->name);
+	struct nftnl_chain_list *list;
 	struct nftnl_chain *c;
 	int i;
 
-	if (!list)
-		return;
-
 	/* Initialize built-in chains if they don't exist yet */
 	for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
+		list = nft_chain_list_get(h, table->name,
+					  table->chains[i].name);
+		if (!list)
+			continue;
 
 		c = nftnl_chain_list_lookup_byname(list, table->chains[i].name);
 		if (c != NULL)
@@ -782,7 +771,7 @@ static bool nft_chain_builtin(struct nftnl_chain *c)
 	return nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM) != NULL;
 }
 
-static int nft_restart(struct nft_handle *h)
+int nft_restart(struct nft_handle *h)
 {
 	mnl_socket_close(h->nl);
 
@@ -794,7 +783,8 @@ static int nft_restart(struct nft_handle *h)
 		return -1;
 
 	h->portid = mnl_socket_get_portid(h->nl);
-	nlbuffsiz = 0;
+	h->nlsndbuffsiz = 0;
+	h->nlrcvbuffsiz = 0;
 
 	return 0;
 }
@@ -820,67 +810,6 @@ int nft_init(struct nft_handle *h, const struct builtin_table *t)
 	return 0;
 }
 
-static int __flush_rule_cache(struct nftnl_rule *r, void *data)
-{
-	nftnl_rule_list_del(r);
-	nftnl_rule_free(r);
-
-	return 0;
-}
-
-static void flush_rule_cache(struct nftnl_chain *c)
-{
-	nftnl_rule_foreach(c, __flush_rule_cache, NULL);
-}
-
-static int __flush_chain_cache(struct nftnl_chain *c, void *data)
-{
-	nftnl_chain_list_del(c);
-	nftnl_chain_free(c);
-
-	return 0;
-}
-
-static int flush_cache(struct nft_cache *c, const struct builtin_table *tables,
-		       const char *tablename)
-{
-	const struct builtin_table *table;
-	int i;
-
-	if (tablename) {
-		table = __nft_table_builtin_find(tables, tablename);
-		if (!table || !c->table[table->type].chains)
-			return 0;
-		nftnl_chain_list_foreach(c->table[table->type].chains,
-					 __flush_chain_cache, NULL);
-		return 0;
-	}
-
-	for (i = 0; i < NFT_TABLE_MAX; i++) {
-		if (tables[i].name == NULL)
-			continue;
-
-		if (!c->table[i].chains)
-			continue;
-
-		nftnl_chain_list_free(c->table[i].chains);
-		c->table[i].chains = NULL;
-	}
-	nftnl_table_list_free(c->tables);
-	c->tables = NULL;
-
-	return 1;
-}
-
-static void flush_chain_cache(struct nft_handle *h, const char *tablename)
-{
-	if (!h->have_cache)
-		return;
-
-	if (flush_cache(h->cache, h->tables, tablename))
-		h->have_cache = false;
-}
-
 void nft_fini(struct nft_handle *h)
 {
 	flush_chain_cache(h, NULL);
@@ -1015,13 +944,163 @@ static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m)
 	return 0;
 }
 
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m)
+static struct nftnl_set *add_anon_set(struct nft_handle *h, const char *table,
+				      uint32_t flags, uint32_t key_type,
+				      uint32_t key_len, uint32_t size)
+{
+	static uint32_t set_id = 0;
+	struct nftnl_set *s;
+
+	s = nftnl_set_alloc();
+	if (!s)
+		return NULL;
+
+	nftnl_set_set_u32(s, NFTNL_SET_FAMILY, h->family);
+	nftnl_set_set_str(s, NFTNL_SET_TABLE, table);
+	nftnl_set_set_str(s, NFTNL_SET_NAME, "__set%d");
+	nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id);
+	nftnl_set_set_u32(s, NFTNL_SET_FLAGS,
+			  NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | flags);
+	nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, key_type);
+	nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, key_len);
+	nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, size);
+
+	return batch_set_add(h, NFT_COMPAT_SET_ADD, s) ? s : NULL;
+}
+
+static struct nftnl_expr *
+gen_payload(uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
+{
+	struct nftnl_expr *e = nftnl_expr_alloc("payload");
+
+	if (!e)
+		return NULL;
+	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
+	return e;
+}
+
+static struct nftnl_expr *
+gen_lookup(uint32_t sreg, const char *set_name, uint32_t set_id, uint32_t flags)
+{
+	struct nftnl_expr *e = nftnl_expr_alloc("lookup");
+
+	if (!e)
+		return NULL;
+	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, sreg);
+	nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
+	nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, flags);
+	return e;
+}
+
+/* simplified nftables:include/netlink.h, netlink_padded_len() */
+#define NETLINK_ALIGN		4
+
+/* from nftables:include/datatype.h, TYPE_BITS */
+#define CONCAT_TYPE_BITS	6
+
+/* from nftables:include/datatype.h, enum datatypes */
+#define NFT_DATATYPE_IPADDR	7
+#define NFT_DATATYPE_ETHERADDR	9
+
+static int __add_nft_among(struct nft_handle *h, const char *table,
+			   struct nftnl_rule *r, struct nft_among_pair *pairs,
+			   int cnt, bool dst, bool inv, bool ip)
+{
+	uint32_t set_id, type = NFT_DATATYPE_ETHERADDR, len = ETH_ALEN;
+	/* { !dst, dst } */
+	static const int eth_addr_off[] = {
+		offsetof(struct ether_header, ether_shost),
+		offsetof(struct ether_header, ether_dhost)
+	};
+	static const int ip_addr_off[] = {
+		offsetof(struct iphdr, saddr),
+		offsetof(struct iphdr, daddr)
+	};
+	struct nftnl_expr *e;
+	struct nftnl_set *s;
+	int idx = 0;
+
+	if (ip) {
+		type = type << CONCAT_TYPE_BITS | NFT_DATATYPE_IPADDR;
+		len += sizeof(struct in_addr) + NETLINK_ALIGN - 1;
+		len &= ~(NETLINK_ALIGN - 1);
+	}
+
+	s = add_anon_set(h, table, 0, type, len, cnt);
+	if (!s)
+		return -ENOMEM;
+	set_id = nftnl_set_get_u32(s, NFTNL_SET_ID);
+
+	for (idx = 0; idx < cnt; idx++) {
+		struct nftnl_set_elem *elem = nftnl_set_elem_alloc();
+
+		if (!elem)
+			return -ENOMEM;
+		nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY,
+				   &pairs[idx], len);
+		nftnl_set_elem_add(s, elem);
+	}
+
+	e = gen_payload(NFT_PAYLOAD_LL_HEADER,
+			eth_addr_off[dst], ETH_ALEN, NFT_REG_1);
+	if (!e)
+		return -ENOMEM;
+	nftnl_rule_add_expr(r, e);
+
+	if (ip) {
+		e = gen_payload(NFT_PAYLOAD_NETWORK_HEADER, ip_addr_off[dst],
+				sizeof(struct in_addr), NFT_REG32_02);
+		if (!e)
+			return -ENOMEM;
+		nftnl_rule_add_expr(r, e);
+	}
+
+	e = gen_lookup(NFT_REG_1, "__set%d", set_id, inv);
+	if (!e)
+		return -ENOMEM;
+	nftnl_rule_add_expr(r, e);
+
+	return 0;
+}
+
+static int add_nft_among(struct nft_handle *h,
+			 struct nftnl_rule *r, struct xt_entry_match *m)
+{
+	struct nft_among_data *data = (struct nft_among_data *)m->data;
+	const char *table = nftnl_rule_get(r, NFTNL_RULE_TABLE);
+
+	if ((data->src.cnt && data->src.ip) ||
+	    (data->dst.cnt && data->dst.ip)) {
+		uint16_t eth_p_ip = htons(ETH_P_IP);
+
+		add_meta(r, NFT_META_PROTOCOL);
+		add_cmp_ptr(r, NFT_CMP_EQ, &eth_p_ip, 2);
+	}
+
+	if (data->src.cnt)
+		__add_nft_among(h, table, r, data->pairs, data->src.cnt,
+				false, data->src.inv, data->src.ip);
+	if (data->dst.cnt)
+		__add_nft_among(h, table, r, data->pairs + data->src.cnt,
+				data->dst.cnt, true, data->dst.inv,
+				data->dst.ip);
+	return 0;
+}
+
+int add_match(struct nft_handle *h,
+	      struct nftnl_rule *r, struct xt_entry_match *m)
 {
 	struct nftnl_expr *expr;
 	int ret;
 
 	if (!strcmp(m->u.user.name, "limit"))
 		return add_nft_limit(r, m);
+	else if (!strcmp(m->u.user.name, "among"))
+		return add_nft_among(h, r, m);
 
 	expr = nftnl_expr_alloc("match");
 	if (expr == NULL)
@@ -1234,10 +1313,10 @@ nft_rule_new(struct nft_handle *h, const char *chain, const char *table,
 		return NULL;
 
 	nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, h->family);
-	nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
-	nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
+	nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
+	nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
 
-	if (h->ops->add(r, data) < 0)
+	if (h->ops->add(h, r, data) < 0)
 		goto err;
 
 	return r;
@@ -1257,9 +1336,15 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
 	struct nftnl_rule *r;
 	int type;
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
+
+	/* Since ebtables user-defined chain policies are implemented as last
+	 * rule in nftables, rule cache is required here to treat them right. */
+	if (h->family == NFPROTO_BRIDGE) {
+		c = nft_chain_find(h, table, chain);
+		if (c && !nft_chain_builtin(c))
+			nft_build_cache(h, c);
+	}
 
 	nft_fn = nft_rule_append;
 
@@ -1280,7 +1365,7 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
 	}
 
 	if (verbose)
-		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
 
 	if (ref) {
 		nftnl_chain_rule_insert_at(r, ref);
@@ -1298,16 +1383,14 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
 }
 
 void
-nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
-		    unsigned int format)
+nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+		    enum nft_rule_print type, unsigned int format)
 {
 	const char *chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
-	int family = nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY);
 	struct iptables_command_state cs = {};
-	struct nft_family_ops *ops;
+	struct nft_family_ops *ops = h->ops;
 
-	ops = nft_family_ops_lookup(family);
-	ops->rule_to_cs(r, &cs);
+	ops->rule_to_cs(h, r, &cs);
 
 	if (!(format & (FMT_NOCOUNTS | FMT_C_COUNTS)) && ops->save_counters)
 		ops->save_counters(&cs);
@@ -1329,106 +1412,6 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
 		ops->clear_cs(&cs);
 }
 
-static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct nft_handle *h = data;
-	const struct builtin_table *t;
-	struct nftnl_chain *c;
-
-	c = nftnl_chain_alloc();
-	if (c == NULL)
-		goto err;
-
-	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
-		goto out;
-
-	t = nft_table_builtin_find(h,
-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
-	if (!t)
-		goto out;
-
-	nftnl_chain_list_add_tail(c, h->cache->table[t->type].chains);
-
-	return MNL_CB_OK;
-out:
-	nftnl_chain_free(c);
-err:
-	return MNL_CB_OK;
-}
-
-static int nftnl_table_list_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct nftnl_table *t;
-	struct nftnl_table_list *list = data;
-
-	t = nftnl_table_alloc();
-	if (t == NULL)
-		goto err;
-
-	if (nftnl_table_nlmsg_parse(nlh, t) < 0)
-		goto out;
-
-	nftnl_table_list_add_tail(t, list);
-
-	return MNL_CB_OK;
-out:
-	nftnl_table_free(t);
-err:
-	return MNL_CB_OK;
-}
-
-static int fetch_table_cache(struct nft_handle *h)
-{
-	char buf[16536];
-	struct nlmsghdr *nlh;
-	struct nftnl_table_list *list;
-	int ret;
-
-	list = nftnl_table_list_alloc();
-	if (list == NULL)
-		return 0;
-
-	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, h->family,
-					NLM_F_DUMP, h->seq);
-
-	ret = mnl_talk(h, nlh, nftnl_table_list_cb, list);
-	if (ret < 0 && errno == EINTR)
-		assert(nft_restart(h) >= 0);
-
-	h->cache->tables = list;
-
-	return 1;
-}
-
-static int fetch_chain_cache(struct nft_handle *h)
-{
-	char buf[16536];
-	struct nlmsghdr *nlh;
-	int i, ret;
-
-	fetch_table_cache(h);
-
-	for (i = 0; i < NFT_TABLE_MAX; i++) {
-		enum nft_table_type type = h->tables[i].type;
-
-		if (!h->tables[i].name)
-			continue;
-
-		h->cache->table[type].chains = nftnl_chain_list_alloc();
-		if (!h->cache->table[type].chains)
-			return -1;
-	}
-
-	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
-					NLM_F_DUMP, h->seq);
-
-	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, h);
-	if (ret < 0 && errno == EINTR)
-		assert(nft_restart(h) >= 0);
-
-	return ret;
-}
-
 static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
 {
 	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
@@ -1467,8 +1450,8 @@ static struct nftnl_rule *nft_chain_last_rule(struct nftnl_chain *c)
 	return last;
 }
 
-static void nft_bridge_chain_postprocess(struct nft_handle *h,
-					 struct nftnl_chain *c)
+void nft_bridge_chain_postprocess(struct nft_handle *h,
+				  struct nftnl_chain *c)
 {
 	struct nftnl_rule *last = nft_chain_last_rule(c);
 	struct nftnl_expr_iter *iter;
@@ -1509,138 +1492,6 @@ static void nft_bridge_chain_postprocess(struct nft_handle *h,
 out_iter:
 	nftnl_expr_iter_destroy(iter);
 }
-
-static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
-{
-	struct nftnl_chain *c = data;
-	struct nftnl_rule *r;
-
-	r = nftnl_rule_alloc();
-	if (r == NULL)
-		return MNL_CB_OK;
-
-	if (nftnl_rule_nlmsg_parse(nlh, r) < 0) {
-		nftnl_rule_free(r);
-		return MNL_CB_OK;
-	}
-
-	nftnl_chain_rule_add_tail(r, c);
-	return MNL_CB_OK;
-}
-
-static int nft_rule_list_update(struct nftnl_chain *c, void *data)
-{
-	struct nft_handle *h = data;
-	char buf[16536];
-	struct nlmsghdr *nlh;
-	struct nftnl_rule *rule;
-	int ret;
-
-	rule = nftnl_rule_alloc();
-	if (!rule)
-		return -1;
-
-	nftnl_rule_set_str(rule, NFTNL_RULE_TABLE,
-			   nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
-	nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN,
-			   nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
-
-	nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, h->family,
-					NLM_F_DUMP, h->seq);
-	nftnl_rule_nlmsg_build_payload(nlh, rule);
-
-	ret = mnl_talk(h, nlh, nftnl_rule_list_cb, c);
-	if (ret < 0 && errno == EINTR)
-		assert(nft_restart(h) >= 0);
-
-	nftnl_rule_free(rule);
-
-	if (h->family == NFPROTO_BRIDGE)
-		nft_bridge_chain_postprocess(h, c);
-
-	return 0;
-}
-
-static int fetch_rule_cache(struct nft_handle *h)
-{
-	int i;
-
-	for (i = 0; i < NFT_TABLE_MAX; i++) {
-		enum nft_table_type type = h->tables[i].type;
-
-		if (!h->tables[i].name)
-			continue;
-
-		if (nftnl_chain_list_foreach(h->cache->table[type].chains,
-					     nft_rule_list_update, h))
-			return -1;
-	}
-	return 0;
-}
-
-static void __nft_build_cache(struct nft_handle *h)
-{
-	uint32_t genid_start, genid_stop;
-
-retry:
-	mnl_genid_get(h, &genid_start);
-	fetch_chain_cache(h);
-	fetch_rule_cache(h);
-	h->have_cache = true;
-	mnl_genid_get(h, &genid_stop);
-
-	if (genid_start != genid_stop) {
-		flush_chain_cache(h, NULL);
-		goto retry;
-	}
-
-	h->nft_genid = genid_start;
-}
-
-void nft_build_cache(struct nft_handle *h)
-{
-	if (!h->have_cache)
-		__nft_build_cache(h);
-}
-
-static void __nft_flush_cache(struct nft_handle *h)
-{
-	if (!h->cache_index) {
-		h->cache_index++;
-		h->cache = &h->__cache[h->cache_index];
-	} else {
-		flush_chain_cache(h, NULL);
-	}
-}
-
-static void nft_rebuild_cache(struct nft_handle *h)
-{
-	if (h->have_cache)
-		__nft_flush_cache(h);
-
-	__nft_build_cache(h);
-}
-
-static void nft_release_cache(struct nft_handle *h)
-{
-	if (h->cache_index)
-		flush_cache(&h->__cache[0], h->tables, NULL);
-}
-
-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
-					    const char *table)
-{
-	const struct builtin_table *t;
-
-	t = nft_table_builtin_find(h, table);
-	if (!t)
-		return NULL;
-
-	nft_build_cache(h);
-
-	return h->cache->table[t->type].chains;
-}
-
 static const char *policy_name[NF_ACCEPT+1] = {
 	[NF_DROP] = "DROP",
 	[NF_ACCEPT] = "ACCEPT",
@@ -1648,12 +1499,10 @@ static const char *policy_name[NF_ACCEPT+1] = {
 
 int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
 {
+	struct nft_family_ops *ops = h->ops;
 	struct nftnl_chain_list_iter *iter;
-	struct nft_family_ops *ops;
 	struct nftnl_chain *c;
 
-	ops = nft_family_ops_lookup(h->family);
-
 	iter = nftnl_chain_list_iter_create(list);
 	if (iter == NULL)
 		return 0;
@@ -1702,7 +1551,7 @@ static int nft_chain_save_rules(struct nft_handle *h,
 
 	r = nftnl_rule_iter_next(iter);
 	while (r != NULL) {
-		nft_rule_print_save(r, NFT_RULE_APPEND, format);
+		nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
 		r = nftnl_rule_iter_next(iter);
 	}
 
@@ -1717,7 +1566,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
 	struct nftnl_chain *c;
 	int ret = 0;
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, NULL);
 	if (!list)
 		return 0;
 
@@ -1727,6 +1576,7 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
 
 	c = nftnl_chain_list_iter_next(iter);
 	while (c) {
+		nft_build_cache(h, c);
 		ret = nft_chain_save_rules(h, c, format);
 		if (ret != 0)
 			break;
@@ -1747,15 +1597,16 @@ __nft_rule_flush(struct nft_handle *h, const char *table,
 	struct obj_update *obj;
 	struct nftnl_rule *r;
 
-	if (verbose)
+	if (verbose && chain)
 		fprintf(stdout, "Flushing chain `%s'\n", chain);
 
 	r = nftnl_rule_alloc();
 	if (r == NULL)
 		return;
 
-	nftnl_rule_set(r, NFTNL_RULE_TABLE, (char *)table);
-	nftnl_rule_set(r, NFTNL_RULE_CHAIN, (char *)chain);
+	nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
+	if (chain)
+		nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
 
 	obj = batch_rule_add(h, NFT_COMPAT_RULE_FLUSH, r);
 	if (!obj) {
@@ -1769,29 +1620,34 @@ __nft_rule_flush(struct nft_handle *h, const char *table,
 int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
 		   bool verbose)
 {
-	int ret = 0;
-	struct nftnl_chain_list *list;
 	struct nftnl_chain_list_iter *iter;
-	struct nftnl_chain *c;
+	struct nftnl_chain_list *list;
+	struct nftnl_chain *c = NULL;
+	int ret = 0;
 
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
 
 	nft_fn = nft_rule_flush;
 
-	list = nft_chain_list_get(h, table);
-	if (list == NULL) {
-		ret = 1;
-		goto err;
+	if (chain || verbose) {
+		list = nft_chain_list_get(h, table, chain);
+		if (list == NULL) {
+			ret = 1;
+			goto err;
+		}
 	}
 
 	if (chain) {
 		c = nftnl_chain_list_lookup_byname(list, chain);
-		if (!c)
+		if (!c) {
+			errno = ENOENT;
 			return 0;
+		}
+	}
 
+	if (chain || !verbose) {
 		__nft_rule_flush(h, table, chain, verbose, false);
-		flush_rule_cache(c);
+		flush_rule_cache(h, table, c);
 		return 1;
 	}
 
@@ -1803,11 +1659,10 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
 
 	c = nftnl_chain_list_iter_next(iter);
 	while (c != NULL) {
-		const char *chain_name =
-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+		chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
 
-		__nft_rule_flush(h, table, chain_name, verbose, false);
-		flush_rule_cache(c);
+		__nft_rule_flush(h, table, chain, verbose, false);
+		flush_rule_cache(h, table, c);
 		c = nftnl_chain_list_iter_next(iter);
 	}
 	nftnl_chain_list_iter_destroy(iter);
@@ -1824,9 +1679,7 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
 
 	nft_fn = nft_chain_user_add;
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
 
 	if (nft_chain_exists(h, table, chain)) {
 		errno = EEXIST;
@@ -1837,14 +1690,14 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
 	if (c == NULL)
 		return 0;
 
-	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
-	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
 	if (h->family == NFPROTO_BRIDGE)
 		nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
 
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (list)
 		nftnl_chain_list_add(c, list);
 
@@ -1871,8 +1724,8 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
 		if (!c)
 			return -1;
 
-		nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
-		nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
+		nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
+		nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain);
 		created = true;
 	}
 
@@ -1884,7 +1737,7 @@ int nft_chain_restore(struct nft_handle *h, const char *chain, const char *table
 
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (list)
 		nftnl_chain_list_add(c, list);
 
@@ -1916,6 +1769,10 @@ static int __nft_chain_user_del(struct nftnl_chain *c, void *data)
 		fprintf(stdout, "Deleting chain `%s'\n",
 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
 
+	/* This triggers required policy rule deletion. */
+	if (h->family == NFPROTO_BRIDGE)
+		nft_build_cache(h, c);
+
 	/* XXX This triggers a fast lookup from the kernel. */
 	nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
@@ -1939,7 +1796,7 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
 
 	nft_fn = nft_chain_user_del;
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (list == NULL)
 		return 0;
 
@@ -1967,7 +1824,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
 {
 	struct nftnl_chain_list *list;
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (list == NULL)
 		return NULL;
 
@@ -2003,9 +1860,7 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
 		return 0;
 	}
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
 
 	/* Config load changed errno. Ensure genuine info for our callers. */
 	errno = 0;
@@ -2023,8 +1878,8 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
 	if (c == NULL)
 		return 0;
 
-	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
-	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)newname);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table);
+	nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, newname);
 	nftnl_chain_set_u64(c, NFTNL_CHAIN_HANDLE, handle);
 
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_RENAME, c);
@@ -2033,13 +1888,6 @@ int nft_chain_user_rename(struct nft_handle *h,const char *chain,
 	return ret == 0 ? 1 : 0;
 }
 
-static struct nftnl_table_list *nftnl_table_list_get(struct nft_handle *h)
-{
-	nft_build_cache(h);
-
-	return h->cache->tables;
-}
-
 bool nft_table_find(struct nft_handle *h, const char *tablename)
 {
 	struct nftnl_table_list_iter *iter;
@@ -2075,8 +1923,8 @@ err:
 }
 
 int nft_for_each_table(struct nft_handle *h,
-		       int (*func)(struct nft_handle *h, const char *tablename, bool counters),
-		       bool counters)
+		       int (*func)(struct nft_handle *h, const char *tablename, void *data),
+		       void *data)
 {
 	struct nftnl_table_list *list;
 	struct nftnl_table_list_iter *iter;
@@ -2095,7 +1943,7 @@ int nft_for_each_table(struct nft_handle *h,
 		const char *tablename =
 			nftnl_table_get(t, NFTNL_TABLE_NAME);
 
-		func(h, tablename, counters);
+		func(h, tablename, data);
 
 		t = nftnl_table_list_iter_next(iter);
 	}
@@ -2179,8 +2027,7 @@ err_out:
 
 void nft_table_new(struct nft_handle *h, const char *table)
 {
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
 }
 
 static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
@@ -2189,6 +2036,9 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
 
 	nftnl_rule_list_del(r);
 
+	if (!nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE))
+		nftnl_rule_set_u32(r, NFTNL_RULE_ID, ++h->rule_id);
+
 	obj = batch_rule_add(h, NFT_COMPAT_RULE_DELETE, r);
 	if (!obj) {
 		nftnl_rule_free(r);
@@ -2204,6 +2054,8 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
 	struct nftnl_rule_iter *iter;
 	bool found = false;
 
+	nft_build_cache(h, c);
+
 	if (rulenum >= 0)
 		/* Delete by rule number case */
 		return nftnl_rule_lookup_byindex(c, rulenum);
@@ -2214,7 +2066,7 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
 
 	r = nftnl_rule_iter_next(iter);
 	while (r != NULL) {
-		found = h->ops->rule_find(h->ops, r, data);
+		found = h->ops->rule_find(h, r, data);
 		if (found)
 			break;
 		r = nftnl_rule_iter_next(iter);
@@ -2242,7 +2094,7 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
 		goto fail_enoent;
 
 	if (verbose)
-		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
 
 	return 1;
 fail_enoent:
@@ -2271,7 +2123,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
 		if (ret < 0)
 			errno = ENOMEM;
 		if (verbose)
-			h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+			h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
 	} else
 		errno = ENOENT;
 
@@ -2312,7 +2164,7 @@ nft_rule_add(struct nft_handle *h, const char *chain,
 	}
 
 	if (verbose)
-		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+		h->ops->print_rule(h, r, 0, FMT_PRINT_RULE);
 
 	return r;
 }
@@ -2323,9 +2175,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
 	struct nftnl_rule *r = NULL, *new_rule;
 	struct nftnl_chain *c;
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
 
 	nft_fn = nft_rule_insert;
 
@@ -2423,8 +2273,8 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
 static int
 __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
 		int rulenum, unsigned int format,
-		void (*cb)(struct nftnl_rule *r, unsigned int num,
-			   unsigned int format))
+		void (*cb)(struct nft_handle *h, struct nftnl_rule *r,
+			   unsigned int num, unsigned int format))
 {
 	struct nftnl_rule_iter *iter;
 	struct nftnl_rule *r;
@@ -2437,7 +2287,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
 			 * valid chain but invalid rule number
 			 */
 			return 1;
-		cb(r, rulenum, format);
+		cb(h, r, rulenum, format);
 		return 1;
 	}
 
@@ -2447,7 +2297,7 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
 
 	r = nftnl_rule_iter_next(iter);
 	while (r != NULL) {
-		cb(r, ++rule_ctr, format);
+		cb(h, r, ++rule_ctr, format);
 		r = nftnl_rule_iter_next(iter);
 	}
 
@@ -2476,7 +2326,6 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
 }
 
 static void __nft_print_header(struct nft_handle *h,
-			       const struct nft_family_ops *ops,
 			       struct nftnl_chain *c, unsigned int format)
 {
 	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
@@ -2492,31 +2341,23 @@ static void __nft_print_header(struct nft_handle *h,
 	if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
 		pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
 
-	ops->print_header(format, chain_name, pname,
+	h->ops->print_header(format, chain_name, pname,
 			&ctrs, basechain, refs - entries, entries);
 }
 
 int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
 		  int rulenum, unsigned int format)
 {
-	const struct nft_family_ops *ops;
+	const struct nft_family_ops *ops = h->ops;
 	struct nftnl_chain_list *list;
 	struct nftnl_chain_list_iter *iter;
 	struct nftnl_chain *c;
 	bool found = false;
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
+	nft_assert_table_compatible(h, table, chain);
 
-	ops = nft_family_ops_lookup(h->family);
-
-	if (!nft_is_table_compatible(h, table)) {
-		xtables_error(OTHER_PROBLEM, "table `%s' is incompatible, use 'nft' tool.\n", table);
-		return 0;
-	}
-
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (!list)
 		return 0;
 
@@ -2528,7 +2369,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
 		if (!rulenum) {
 			if (ops->print_table_header)
 				ops->print_table_header(table);
-			__nft_print_header(h, ops, c, format);
+			__nft_print_header(h, c, format);
 		}
 		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
 		return 1;
@@ -2546,7 +2387,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
 		if (found)
 			printf("\n");
 
-		__nft_print_header(h, ops, c, format);
+		__nft_print_header(h, c, format);
 		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
 
 		found = true;
@@ -2557,9 +2398,10 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
 }
 
 static void
-list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+list_save(struct nft_handle *h, struct nftnl_rule *r,
+	  unsigned int num, unsigned int format)
 {
-	nft_rule_print_save(r, NFT_RULE_APPEND, format);
+	nft_rule_print_save(h, r, NFT_RULE_APPEND, format);
 }
 
 static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
@@ -2612,16 +2454,10 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
 	struct nftnl_chain *c;
 	int ret = 0;
 
-	/* If built-in chains don't exist for this table, create them */
-	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
-		nft_xt_builtin_init(h, table);
+	nft_xt_builtin_init(h, table);
+	nft_assert_table_compatible(h, table, chain);
 
-	if (!nft_is_table_compatible(h, table)) {
-		xtables_error(OTHER_PROBLEM, "table `%s' is incompatible, use 'nft' tool.\n", table);
-		return 0;
-	}
-
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (!list)
 		return 0;
 
@@ -2677,7 +2513,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
 		goto error;
 	}
 
-	nft_rule_to_iptables_command_state(r, &cs);
+	nft_rule_to_iptables_command_state(h, r, &cs);
 
 	cs.counters.pcnt = cs.counters.bcnt = 0;
 
@@ -2698,6 +2534,39 @@ static void nft_compat_table_batch_add(struct nft_handle *h, uint16_t type,
 	nftnl_table_nlmsg_build_payload(nlh, table);
 }
 
+static void nft_compat_set_batch_add(struct nft_handle *h, uint16_t type,
+				     uint16_t flags, uint32_t seq,
+				     struct nftnl_set *set)
+{
+	struct nlmsghdr *nlh;
+
+	nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+					type, h->family, flags, seq);
+	nftnl_set_nlmsg_build_payload(nlh, set);
+}
+
+static void nft_compat_setelem_batch_add(struct nft_handle *h, uint16_t type,
+					 uint16_t flags, uint32_t *seq,
+					 struct nftnl_set *set)
+{
+	struct nftnl_set_elems_iter *iter;
+	struct nlmsghdr *nlh;
+
+	iter = nftnl_set_elems_iter_create(set);
+	if (!iter)
+		return;
+
+	while (nftnl_set_elems_iter_cur(iter)) {
+		(*seq)++;
+		mnl_nft_batch_continue(h->batch);
+		nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(h->batch),
+					    type, h->family, flags, *seq);
+		if (nftnl_set_elems_nlmsg_build_payload_iter(nlh, iter) <= 0)
+			break;
+	}
+	nftnl_set_elems_iter_destroy(iter);
+}
+
 static void nft_compat_chain_batch_add(struct nft_handle *h, uint16_t type,
 				       uint16_t flags, uint32_t seq,
 				       struct nftnl_chain *chain)
@@ -2747,6 +2616,9 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
 	case NFT_COMPAT_RULE_FLUSH:
 		nftnl_rule_free(o->rule);
 		break;
+	case NFT_COMPAT_SET_ADD:
+		nftnl_set_free(o->set);
+		break;
 	}
 	h->obj_list_num--;
 	list_del(&o->head);
@@ -2813,6 +2685,7 @@ static void nft_refresh_transaction(struct nft_handle *h)
 		case NFT_COMPAT_RULE_REPLACE:
 		case NFT_COMPAT_RULE_DELETE:
 		case NFT_COMPAT_RULE_FLUSH:
+		case NFT_COMPAT_SET_ADD:
 			break;
 		}
 	}
@@ -2903,6 +2776,13 @@ retry:
 			nft_compat_rule_batch_add(h, NFT_MSG_DELRULE, 0,
 						  n->seq, n->rule);
 			break;
+		case NFT_COMPAT_SET_ADD:
+			nft_compat_set_batch_add(h, NFT_MSG_NEWSET,
+						 NLM_F_CREATE, n->seq, n->set);
+			nft_compat_setelem_batch_add(h, NFT_MSG_NEWSETELEM,
+						     NLM_F_CREATE, &n->seq, n->set);
+			seq = n->seq;
+			break;
 		}
 
 		mnl_nft_batch_continue(h->batch);
@@ -2917,7 +2797,7 @@ retry:
 	}
 
 	errno = 0;
-	ret = mnl_batch_talk(h->nl, h->batch, &h->err_list);
+	ret = mnl_batch_talk(h, seq);
 	if (ret && errno == ERESTART) {
 		nft_rebuild_cache(h);
 
@@ -3039,6 +2919,8 @@ int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
 	else
 		return 0;
 
+	nft_build_cache(h, c);
+
 	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval);
 	return 1;
 }
@@ -3065,11 +2947,15 @@ static void nft_bridge_commit_prepare(struct nft_handle *h)
 
 int nft_commit(struct nft_handle *h)
 {
-	if (h->family == NFPROTO_BRIDGE)
-		nft_bridge_commit_prepare(h);
 	return nft_action(h, NFT_COMPAT_COMMIT);
 }
 
+int nft_bridge_commit(struct nft_handle *h)
+{
+	nft_bridge_commit_prepare(h);
+	return nft_commit(h);
+}
+
 int nft_abort(struct nft_handle *h)
 {
 	return nft_action(h, NFT_COMPAT_ABORT);
@@ -3204,7 +3090,7 @@ const char *nft_strerror(int err)
 	    { NULL, ENOENT, "No chain/target/match by that name" },
 	  };
 
-	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+	for (i = 0; i < ARRAY_SIZE(table); i++) {
 		if ((!table[i].fn || table[i].fn == nft_fn)
 		    && table[i].err == err)
 			return table[i].message;
@@ -3213,136 +3099,42 @@ const char *nft_strerror(int err)
 	return strerror(err);
 }
 
-static void xtables_config_perror(uint32_t flags, const char *fmt, ...)
-{
-	va_list args;
-
-	va_start(args, fmt);
-
-	if (flags & NFT_LOAD_VERBOSE)
-		vfprintf(stderr, fmt, args);
-
-	va_end(args);
-}
-
-static int __nft_xtables_config_load(struct nft_handle *h, const char *filename,
-				     uint32_t flags)
+static int recover_rule_compat(struct nftnl_rule *r)
 {
-	struct nftnl_table_list *table_list = NULL;
-	struct nftnl_chain_list *chain_list = NULL;
-	struct nftnl_table_list_iter *titer = NULL;
-	struct nftnl_chain_list_iter *citer = NULL;
-	struct nftnl_table *table;
-	struct nftnl_chain *chain;
-	uint32_t table_family, chain_family;
-	bool found = false;
-
-	table_list = nftnl_table_list_alloc();
-	chain_list = nftnl_chain_list_alloc();
-
-	if (xtables_config_parse(filename, table_list, chain_list) < 0) {
-		if (errno == ENOENT) {
-			xtables_config_perror(flags,
-				"configuration file `%s' does not exists\n",
-				filename);
-		} else {
-			xtables_config_perror(flags,
-				"Fatal error parsing config file: %s\n",
-				 strerror(errno));
-		}
-		goto err;
-	}
-
-	/* Stage 1) create tables */
-	titer = nftnl_table_list_iter_create(table_list);
-	while ((table = nftnl_table_list_iter_next(titer)) != NULL) {
-		table_family = nftnl_table_get_u32(table,
-						      NFTNL_TABLE_FAMILY);
-		if (h->family != table_family)
-			continue;
-
-		found = true;
-
-		if (batch_table_add(h, NFT_COMPAT_TABLE_ADD, table) < 0) {
-			if (errno == EEXIST) {
-				xtables_config_perror(flags,
-					"table `%s' already exists, skipping\n",
-					(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
-			} else {
-				xtables_config_perror(flags,
-					"table `%s' cannot be create, reason `%s'. Exitting\n",
-					(char *)nftnl_table_get(table, NFTNL_TABLE_NAME),
-					strerror(errno));
-				goto err;
-			}
-			continue;
-		}
-		xtables_config_perror(flags, "table `%s' has been created\n",
-			(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
-	}
-	nftnl_table_list_iter_destroy(titer);
-	nftnl_table_list_free(table_list);
-
-	if (!found)
-		goto err;
-
-	/* Stage 2) create chains */
-	citer = nftnl_chain_list_iter_create(chain_list);
-	while ((chain = nftnl_chain_list_iter_next(citer)) != NULL) {
-		chain_family = nftnl_chain_get_u32(chain,
-						      NFTNL_CHAIN_TABLE);
-		if (h->family != chain_family)
-			continue;
-
-		if (batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, chain) < 0) {
-			if (errno == EEXIST) {
-				xtables_config_perror(flags,
-					"chain `%s' already exists in table `%s', skipping\n",
-					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
-					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
-			} else {
-				xtables_config_perror(flags,
-					"chain `%s' cannot be create, reason `%s'. Exitting\n",
-					(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
-					strerror(errno));
-				goto err;
-			}
-			continue;
-		}
-
-		xtables_config_perror(flags,
-			"chain `%s' in table `%s' has been created\n",
-			(char *)nftnl_chain_get(chain, NFTNL_CHAIN_NAME),
-			(char *)nftnl_chain_get(chain, NFTNL_CHAIN_TABLE));
-	}
-	nftnl_chain_list_iter_destroy(citer);
-	nftnl_chain_list_free(chain_list);
-
-	h->config_done = 1;
+	struct nftnl_expr_iter *iter;
+	struct nftnl_expr *e;
+	uint32_t reg;
+	int ret = -1;
 
-	return 0;
+	iter = nftnl_expr_iter_create(r);
+	if (!iter)
+		return -1;
 
-err:
-	nftnl_table_list_free(table_list);
-	nftnl_chain_list_free(chain_list);
+next_expr:
+	e = nftnl_expr_iter_next(iter);
+	if (!e)
+		goto out;
 
-	if (titer != NULL)
-		nftnl_table_list_iter_destroy(titer);
-	if (citer != NULL)
-		nftnl_chain_list_iter_destroy(citer);
+	if (strcmp("meta", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+	    nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY) != NFT_META_L4PROTO)
+		goto next_expr;
 
-	h->config_done = -1;
+	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG);
 
-	return -1;
-}
+	e = nftnl_expr_iter_next(iter);
+	if (!e)
+		goto out;
 
-int nft_xtables_config_load(struct nft_handle *h, const char *filename,
-			    uint32_t flags)
-{
-	if (!h->config_done)
-		return __nft_xtables_config_load(h, filename, flags);
+	if (strcmp("cmp", nftnl_expr_get_str(e, NFTNL_EXPR_NAME)) ||
+	    reg != nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG))
+		goto next_expr;
 
-	return h->config_done;
+	add_compat(r, nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA),
+		   nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ);
+	ret = 0;
+out:
+	nftnl_expr_iter_destroy(iter);
+	return ret;
 }
 
 struct chain_zero_data {
@@ -3370,6 +3162,8 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
 			return -1;
 	}
 
+	nft_build_cache(h, c);
+
 	iter = nftnl_rule_iter_create(c);
 	if (iter == NULL)
 		return -1;
@@ -3407,6 +3201,7 @@ static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
 			 * Unset RULE_POSITION for older kernels, we want to replace
 			 * rule based on its handle only.
 			 */
+			recover_rule_compat(r);
 			nftnl_rule_unset(r, NFTNL_RULE_POSITION);
 			if (!batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r)) {
 				nftnl_rule_iter_destroy(iter);
@@ -3431,7 +3226,7 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
 	struct nftnl_chain *c;
 	int ret = 0;
 
-	list = nft_chain_list_get(h, table);
+	list = nft_chain_list_get(h, table, chain);
 	if (list == NULL)
 		goto err;
 
@@ -3460,9 +3255,7 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag)
 	return NFT_CMP_EQ;
 }
 
-#define NFT_COMPAT_EXPR_MAX     8
-
-static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
+static const char *supported_exprs[] = {
 	"match",
 	"target",
 	"payload",
@@ -3470,7 +3263,8 @@ static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
 	"cmp",
 	"bitwise",
 	"counter",
-	"immediate"
+	"immediate",
+	"lookup",
 };
 
 
@@ -3479,7 +3273,7 @@ static int nft_is_expr_compatible(struct nftnl_expr *expr, void *data)
 	const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
 	int i;
 
-	for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) {
+	for (i = 0; i < ARRAY_SIZE(supported_exprs); i++) {
 		if (strcmp(supported_exprs[i], name) == 0)
 			return 0;
 	}
@@ -3506,6 +3300,8 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
 	enum nf_inet_hooks hook;
 	int prio;
 
+	nft_build_cache(h, c);
+
 	if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
 		return -1;
 
@@ -3533,11 +3329,12 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
 	return 0;
 }
 
-bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
+bool nft_is_table_compatible(struct nft_handle *h,
+			     const char *table, const char *chain)
 {
 	struct nftnl_chain_list *clist;
 
-	clist = nft_chain_list_get(h, tablename);
+	clist = nft_chain_list_get(h, table, chain);
 	if (clist == NULL)
 		return false;
 
@@ -3546,3 +3343,22 @@ bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
 
 	return true;
 }
+
+void nft_assert_table_compatible(struct nft_handle *h,
+				 const char *table, const char *chain)
+{
+	const char *pfx = "", *sfx = "";
+
+	if (nft_is_table_compatible(h, table, chain))
+		return;
+
+	if (chain) {
+		pfx = "chain `";
+		sfx = "' in ";
+	} else {
+		chain = "";
+	}
+	xtables_error(OTHER_PROBLEM,
+		      "%s%s%stable `%s' is incompatible, use 'nft' tool.\n",
+		      pfx, chain, sfx, table);
+}
diff --git a/iptables/nft.h b/iptables/nft.h
index 43eb8a39dd9c1d5db1cdde344b1717135dc23743..51b5660314c0cbda3d0720ac9868f5a0e2f68d30 100644
--- a/iptables/nft.h
+++ b/iptables/nft.h
@@ -27,10 +27,19 @@ struct builtin_table {
 	struct builtin_chain chains[NF_INET_NUMHOOKS];
 };
 
+enum nft_cache_level {
+	NFT_CL_NONE,
+	NFT_CL_TABLES,
+	NFT_CL_CHAINS,
+	NFT_CL_SETS,
+	NFT_CL_RULES
+};
+
 struct nft_cache {
 	struct nftnl_table_list		*tables;
 	struct {
 		struct nftnl_chain_list *chains;
+		struct nftnl_set_list	*sets;
 		bool			initialized;
 	} table[NFT_TABLE_MAX];
 };
@@ -38,6 +47,8 @@ struct nft_cache {
 struct nft_handle {
 	int			family;
 	struct mnl_socket	*nl;
+	int			nlsndbuffsiz;
+	int			nlrcvbuffsiz;
 	uint32_t		portid;
 	uint32_t		seq;
 	uint32_t		nft_genid;
@@ -51,7 +62,7 @@ struct nft_handle {
 	unsigned int		cache_index;
 	struct nft_cache	__cache[2];
 	struct nft_cache	*cache;
-	bool			have_cache;
+	enum nft_cache_level	cache_level;
 	bool			restore;
 	bool			noflush;
 	int8_t			config_done;
@@ -71,7 +82,7 @@ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
 	     void *data);
 int nft_init(struct nft_handle *h, const struct builtin_table *t);
 void nft_fini(struct nft_handle *h);
-void nft_build_cache(struct nft_handle *h);
+int nft_restart(struct nft_handle *h);
 
 /*
  * Operations with tables.
@@ -79,7 +90,7 @@ void nft_build_cache(struct nft_handle *h);
 struct nftnl_table;
 struct nftnl_chain_list;
 
-int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, bool counters), bool counters);
+int nft_for_each_table(struct nft_handle *h, int (*func)(struct nft_handle *h, const char *tablename, void *data), void *data);
 bool nft_table_find(struct nft_handle *h, const char *tablename);
 int nft_table_purge_chains(struct nft_handle *h, const char *table, struct nftnl_chain_list *list);
 int nft_table_flush(struct nft_handle *h, const char *table);
@@ -92,8 +103,6 @@ const struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const c
 struct nftnl_chain;
 
 int nft_chain_set(struct nft_handle *h, const char *table, const char *chain, const char *policy, const struct xt_counters *counters);
-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
-					    const char *table);
 int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list);
 int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table);
 int nft_chain_user_del(struct nft_handle *h, const char *chain, const char *table, bool verbose);
@@ -102,6 +111,9 @@ int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *t
 int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table, bool verbose);
 const struct builtin_chain *nft_chain_builtin_find(const struct builtin_table *t, const char *chain);
 bool nft_chain_exists(struct nft_handle *h, const char *table, const char *chain);
+void nft_bridge_chain_postprocess(struct nft_handle *h,
+				  struct nftnl_chain *c);
+
 
 /*
  * Operations with rule-set.
@@ -125,7 +137,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain, const char *
  */
 int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes);
 int add_verdict(struct nftnl_rule *r, int verdict);
-int add_match(struct nftnl_rule *r, struct xt_entry_match *m);
+int add_match(struct nft_handle *h, struct nftnl_rule *r, struct xt_entry_match *m);
 int add_target(struct nftnl_rule *r, struct xt_entry_target *t);
 int add_jumpto(struct nftnl_rule *r, const char *name, int verdict);
 int add_action(struct nftnl_rule *r, struct iptables_command_state *cs, bool goto_set);
@@ -136,8 +148,8 @@ enum nft_rule_print {
 	NFT_RULE_DEL,
 };
 
-void nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
-			 unsigned int format);
+void nft_rule_print_save(struct nft_handle *h, const struct nftnl_rule *r,
+			 enum nft_rule_print type, unsigned int format);
 
 uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
 
@@ -145,6 +157,7 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
  * global commit and abort
  */
 int nft_commit(struct nft_handle *h);
+int nft_bridge_commit(struct nft_handle *h);
 int nft_abort(struct nft_handle *h);
 int nft_abort_policy_rule(struct nft_handle *h, const char *table);
 
@@ -168,22 +181,6 @@ int nft_init_eb(struct nft_handle *h, const char *pname);
 int ebt_get_current_chain(const char *chain);
 int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table, bool restore);
 
-/*
- * Parse config for tables and chain helper functions
- */
-#define XTABLES_CONFIG_DEFAULT  "/etc/xtables.conf"
-
-struct nftnl_table_list;
-struct nftnl_chain_list;
-
-extern int xtables_config_parse(const char *filename, struct nftnl_table_list *table_list, struct nftnl_chain_list *chain_list);
-
-enum {
-	NFT_LOAD_VERBOSE = (1 << 0),
-};
-
-int nft_xtables_config_load(struct nft_handle *h, const char *filename, uint32_t flags);
-
 /*
  * Translation from iptables to nft
  */
@@ -211,7 +208,10 @@ int nft_arp_rule_insert(struct nft_handle *h, const char *chain,
 
 void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
 
-bool nft_is_table_compatible(struct nft_handle *h, const char *name);
+bool nft_is_table_compatible(struct nft_handle *h,
+			     const char *table, const char *chain);
+void nft_assert_table_compatible(struct nft_handle *h,
+				 const char *table, const char *chain);
 
 int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
 			      const char *chain, const char *policy);
diff --git a/iptables/tests/shell/run-tests.sh b/iptables/tests/shell/run-tests.sh
index 7bef09f74643d8275a92e947a9b08380e063765c..d71c13729b3ee5f90964cd3870c06011a0287022 100755
--- a/iptables/tests/shell/run-tests.sh
+++ b/iptables/tests/shell/run-tests.sh
@@ -38,6 +38,14 @@ while [ -n "$1" ]; do
 		HOST=y
 		shift
 		;;
+	-l|--legacy)
+		LEGACY_ONLY=y
+		shift
+		;;
+	-n|--nft)
+		NFT_ONLY=y
+		shift
+		;;
 	*${RETURNCODE_SEPARATOR}+([0-9]))
 		SINGLE+=" $1"
 		VERBOSE=y
@@ -98,19 +106,23 @@ do_test() {
 }
 
 echo ""
-for testfile in $(find_tests);do
-	do_test "$testfile" "$XTABLES_LEGACY_MULTI"
-done
-msg_info "legacy results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
+if [ "$NFT_ONLY" != "y" ]; then
+	for testfile in $(find_tests);do
+		do_test "$testfile" "$XTABLES_LEGACY_MULTI"
+	done
+	msg_info "legacy results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
 
+fi
 legacy_ok=$ok
 legacy_fail=$failed
 ok=0
 failed=0
-for testfile in $(find_tests);do
-	do_test "$testfile" "$XTABLES_NFT_MULTI"
-done
-msg_info "nft results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
+if [ "$LEGACY_ONLY" != "y" ]; then
+	for testfile in $(find_tests);do
+		do_test "$testfile" "$XTABLES_NFT_MULTI"
+	done
+	msg_info "nft results: [OK] $ok [FAILED] $failed [TOTAL] $((ok+failed))"
+fi
 
 ok=$((legacy_ok+ok))
 failed=$((legacy_fail+failed))
diff --git a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0 b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
index e10f61cc8f95b045aeecd470e47ef87486e71e03..bf04dc0a3e15acad68a8022556f3d50ca03b8bf9 100755
--- a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
+++ b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
@@ -50,13 +50,12 @@ DUMP='*filter
 -A foo -j MARK --set-mark 12345
 -A foo -j ACCEPT --opcode 1
 -A foo -j ACCEPT --proto-type 0x800
--A foo -j ACCEPT -i lo --opcode 1 --proto-type 0x800
-'
+-A foo -j ACCEPT -i lo --opcode 1 --proto-type 0x800'
 
-diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
+diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save | grep -v "^#")
 
 # make sure dump can be restored and check it didn't change
 
 $XT_MULTI arptables -F
 $XT_MULTI arptables-restore <<<$DUMP
-diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
+diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save | grep -v "^#")
diff --git a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0 b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
index b2ed95e87bb401d57ed1d1aeeab214a23b7c5a4f..38d387f327ebbd42ad5b4ac9a02b25cc9fad2457 100755
--- a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
+++ b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
@@ -11,8 +11,7 @@ set -e
 DUMP='*filter
 :OUTPUT ACCEPT
 -A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
--A OUTPUT -j mangle --mangle-ip-d 10.0.0.2
-'
+-A OUTPUT -j mangle --mangle-ip-d 10.0.0.2'
 
 # note how mangle-ip-s is unset in second rule
 
@@ -20,8 +19,7 @@ EXPECT='*filter
 :INPUT ACCEPT
 :OUTPUT ACCEPT
 -A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
--A OUTPUT -j mangle --mangle-ip-d 10.0.0.2
-'
+-A OUTPUT -j mangle --mangle-ip-d 10.0.0.2'
 
 $XT_MULTI arptables -F
 $XT_MULTI arptables-restore <<<$DUMP
diff --git a/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0 b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
index 3a9807a1cfe0b74e677a9b1b87db9ec53dbb0fcc..10c5ec33ada2c6d103d41bb8e3b64176d358c671 100755
--- a/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
+++ b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
@@ -58,7 +58,6 @@ EXPECT='*filter
 -A INPUT -j MARK -i eth23 --set-mark 42
 -A OUTPUT -j CLASSIFY -o eth23 --set-class 23:42
 -A OUTPUT -j foo -o eth23
--A foo -j mangle -o eth23 --mangle-ip-s 10.0.0.1
-'
+-A foo -j mangle -o eth23 --mangle-ip-s 10.0.0.1'
 
-diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI arptables-save)
+diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI arptables-save | grep -v '^#')
diff --git a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0 b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
index b0db216ae385468b0b39481ed08d7e1cf31f4bc2..c7f24a383f698143b2a5fca5139462ba2531c0fa 100755
--- a/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
+++ b/iptables/tests/shell/testcases/ebtables/0001-ebtables-basic_0
@@ -1,5 +1,9 @@
 #!/bin/sh
 
+get_entries_count() { # (chain)
+	$XT_MULTI ebtables -L $1 | sed -n 's/.*entries: \([0-9]*\).*/\1/p'
+}
+
 set -x
 case "$XT_MULTI" in
 */xtables-nft-multi)
@@ -28,32 +32,32 @@ case "$XT_MULTI" in
 		exit 1
 	fi
 
-	$XT_MULTI ebtables -L FOO | grep -q 'entries: 0'
-	if [ $? -ne 0 ]; then
-		echo "Unexpected entries count in empty unreferenced chain"
+	entries=$(get_entries_count FOO)
+	if [ $entries -ne 0 ]; then
+		echo "Unexpected entries count in empty unreferenced chain (expected 0, have $entries)"
 		$XT_MULTI ebtables -L
 		exit 1
 	fi
 
 	$XT_MULTI ebtables -A FORWARD -j FOO
-	$XT_MULTI ebtables -L FORWARD | grep -q 'entries: 1'
-	if [ $? -ne 0 ]; then
-		echo "Unexpected entries count in FORWARD chain"
+	entries=$(get_entries_count FORWARD)
+	if [ $entries -ne 1 ]; then
+		echo "Unexpected entries count in FORWARD chain (expected 1, have $entries)"
 		$XT_MULTI ebtables -L
 		exit 1
 	fi
 
-	$XT_MULTI ebtables -L FOO | grep -q 'entries: 0'
-	if [ $? -ne 0 ]; then
-		echo "Unexpected entries count in empty referenced chain"
+	entries=$(get_entries_count FOO)
+	if [ $entries -ne 0 ]; then
+		echo "Unexpected entries count in empty referenced chain (expected 0, have $entries)"
 		$XT_MULTI ebtables -L
 		exit 1
 	fi
 
 	$XT_MULTI ebtables -A FOO -j ACCEPT
-	$XT_MULTI ebtables -L FOO | grep -q 'entries: 1'
-	if [ $? -ne 0 ]; then
-		echo "Unexpected entries count in non-empty referenced chain"
+	entries=$(get_entries_count FOO)
+	if [ $entries -ne 1 ]; then
+		echo "Unexpected entries count in non-empty referenced chain (expected 1, have $entries)"
 		$XT_MULTI ebtables -L
 		exit 1
 	fi
diff --git a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
index 080ba49a4974dae6f22a555afa52e32870aae55d..e18d46551509d50f8a206b016a7882cc6f42acd6 100755
--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
+++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
@@ -99,7 +99,6 @@ DUMP='*filter
 -A foo --802_3-sap 0x23 --limit 100/sec --limit-burst 5 -j ACCEPT
 -A foo --pkttype-type multicast --log-level notice --log-prefix "" -j CONTINUE
 -A foo --pkttype-type multicast --limit 100/sec --limit-burst 5 -j ACCEPT
-
 *nat
 :PREROUTING ACCEPT
 :OUTPUT DROP
@@ -107,8 +106,7 @@ DUMP='*filter
 :nat_foo DROP
 -A PREROUTING -j redirect 
 -A OUTPUT -j ACCEPT
--A POSTROUTING -j ACCEPT
-'
+-A POSTROUTING -j ACCEPT'
 
 diff -u <(echo -e "$DUMP") <($XT_MULTI ebtables-save | grep -v '^#')
 
diff --git a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0 b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
index c858054764d7085f6ede730daf1be95aa56e56d9..62d224134456baed668890eedca423e1cb7af144 100755
--- a/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
+++ b/iptables/tests/shell/testcases/ebtables/0003-ebtables-restore-defaults_0
@@ -13,8 +13,7 @@ DUMP='*filter
 -A FORWARD --limit 100 --limit-burst 42 -j ACCEPT
 -A FORWARD --limit 1000 -j ACCEPT
 -A FORWARD --log --log-prefix "foobar"
--A FORWARD --log
-'
+-A FORWARD --log'
 
 # note how limit-burst is 5 in second rule and log-prefix empty in fourth one
 
@@ -25,8 +24,7 @@ EXPECT='*filter
 -A FORWARD --limit 100/sec --limit-burst 42 -j ACCEPT
 -A FORWARD --limit 1000/sec --limit-burst 5 -j ACCEPT
 -A FORWARD --log-level notice --log-prefix "foobar" -j CONTINUE
--A FORWARD --log-level notice --log-prefix "" -j CONTINUE
-'
+-A FORWARD --log-level notice --log-prefix "" -j CONTINUE'
 
 $XT_MULTI ebtables --init-table
 $XT_MULTI ebtables-restore <<<$DUMP
diff --git a/iptables/tests/shell/testcases/ebtables/0004-save-counters_0 b/iptables/tests/shell/testcases/ebtables/0004-save-counters_0
new file mode 100755
index 0000000000000000000000000000000000000000..46966f433139ae4768b13d1d7fc8befb41fd1820
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0004-save-counters_0
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -e
+
+# there is no legacy backend to test
+[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+
+$XT_MULTI ebtables --init-table
+$XT_MULTI ebtables -A FORWARD -i nodev123 -o nodev432 -j ACCEPT
+$XT_MULTI ebtables -A FORWARD -i nodev432 -o nodev123 -j ACCEPT
+
+EXPECT='Bridge table: filter
+
+Bridge chain: FORWARD, entries: 2, policy: ACCEPT
+-i nodev123 -o nodev432 -j ACCEPT
+-i nodev432 -o nodev123 -j ACCEPT'
+
+echo "ebtables -L FORWARD"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables -L FORWARD)
+
+EXPECT='Bridge table: filter
+
+Bridge chain: FORWARD, entries: 2, policy: ACCEPT
+-i nodev123 -o nodev432 -j ACCEPT , pcnt = 0 -- bcnt = 0
+-i nodev432 -o nodev123 -j ACCEPT , pcnt = 0 -- bcnt = 0'
+
+echo "ebtables -L FORWARD --Lc"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables -L FORWARD --Lc)
+
+EXPECT='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+-A FORWARD -i nodev123 -o nodev432 -j ACCEPT
+-A FORWARD -i nodev432 -o nodev123 -j ACCEPT'
+
+echo "ebtables-save"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables-save | grep -v '^#')
+
+EXPECT='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+[0:0] -A FORWARD -i nodev123 -o nodev432 -j ACCEPT
+[0:0] -A FORWARD -i nodev432 -o nodev123 -j ACCEPT'
+
+echo "ebtables-save -c"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables-save -c | grep -v '^#')
+
+export EBTABLES_SAVE_COUNTER=yes
+
+# -c flag overrides EBTABLES_SAVE_COUNTER variable
+echo "EBTABLES_SAVE_COUNTER=yes ebtables-save -c"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables-save -c | grep -v '^#')
+
+EXPECT='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+-A FORWARD -i nodev123 -o nodev432 -j ACCEPT -c 0 0
+-A FORWARD -i nodev432 -o nodev123 -j ACCEPT -c 0 0'
+
+echo "EBTABLES_SAVE_COUNTER=yes ebtables-save"
+diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables-save | grep -v '^#')
diff --git a/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0 b/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0
new file mode 100755
index 0000000000000000000000000000000000000000..2163d364b318b93276d4bcabf5e3f36e45e5039b
--- /dev/null
+++ b/iptables/tests/shell/testcases/ebtables/0005-ifnamechecks_0
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+# there is no legacy backend to test
+[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
+
+EXPECT='*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+:PVEFW-FORWARD ACCEPT
+:PVEFW-FWBR-OUT ACCEPT
+-A FORWARD -j PVEFW-FORWARD
+-A PVEFW-FORWARD -p IPv4 -j ACCEPT
+-A PVEFW-FORWARD -p IPv6 -j ACCEPT
+-A PVEFW-FORWARD -i fwln+ -j ACCEPT
+-A PVEFW-FORWARD -o fwln+ -j PVEFW-FWBR-OUT'
+
+$XT_MULTI ebtables-restore <<<$EXPECT
+exec diff -u <(echo -e "$EXPECT") <($XT_MULTI ebtables-save | grep -v '^#')
diff --git a/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0 b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
index 51f2422e15259c975e1bd7dc39938a4573c6c42a..3f1d229e915ff6ab043fd7faed17e2758b2c6591 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
@@ -14,7 +14,7 @@ ipt_show() {
 
 $XT_MULTI iptables-restore <<EOF
 *filter
--A FORWARD -m comment --comment "appended rule" -j ACCEPT
+-A FORWARD -m comment --comment "rule 4" -j ACCEPT
 -I FORWARD 1 -m comment --comment "rule 1" -j ACCEPT
 -I FORWARD 2 -m comment --comment "rule 2" -j ACCEPT
 -I FORWARD 3 -m comment --comment "rule 3" -j ACCEPT
@@ -24,7 +24,7 @@ EOF
 EXPECT='-A FORWARD -m comment --comment "rule 1" -j ACCEPT
 -A FORWARD -m comment --comment "rule 2" -j ACCEPT
 -A FORWARD -m comment --comment "rule 3" -j ACCEPT
--A FORWARD -m comment --comment "appended rule" -j ACCEPT'
+-A FORWARD -m comment --comment "rule 4" -j ACCEPT'
 
 diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
 
@@ -32,11 +32,14 @@ diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
 
 $XT_MULTI iptables-restore --noflush <<EOF
 *filter
+-A FORWARD -m comment --comment "rule 5" -j ACCEPT
 -I FORWARD 1 -m comment --comment "rule 0.5" -j ACCEPT
 -I FORWARD 3 -m comment --comment "rule 1.5" -j ACCEPT
 -I FORWARD 5 -m comment --comment "rule 2.5" -j ACCEPT
 -I FORWARD 7 -m comment --comment "rule 3.5" -j ACCEPT
--I FORWARD 9 -m comment --comment "appended rule 2" -j ACCEPT
+-I FORWARD 9 -m comment --comment "rule 4.5" -j ACCEPT
+-I FORWARD 11 -m comment --comment "rule 5.5" -j ACCEPT
+-A FORWARD -m comment --comment "rule 6" -j ACCEPT
 COMMIT
 EOF
 
@@ -47,8 +50,11 @@ EXPECT='-A FORWARD -m comment --comment "rule 0.5" -j ACCEPT
 -A FORWARD -m comment --comment "rule 2.5" -j ACCEPT
 -A FORWARD -m comment --comment "rule 3" -j ACCEPT
 -A FORWARD -m comment --comment "rule 3.5" -j ACCEPT
--A FORWARD -m comment --comment "appended rule" -j ACCEPT
--A FORWARD -m comment --comment "appended rule 2" -j ACCEPT'
+-A FORWARD -m comment --comment "rule 4" -j ACCEPT
+-A FORWARD -m comment --comment "rule 4.5" -j ACCEPT
+-A FORWARD -m comment --comment "rule 5" -j ACCEPT
+-A FORWARD -m comment --comment "rule 5.5" -j ACCEPT
+-A FORWARD -m comment --comment "rule 6" -j ACCEPT'
 
 diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
 
@@ -78,6 +84,8 @@ diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
 
 $XT_MULTI iptables-restore --noflush <<EOF
 *filter
+-A FORWARD -m comment --comment "appended rule 4" -j ACCEPT
+-D FORWARD 7
 -D FORWARD -m comment --comment "appended rule 1" -j ACCEPT
 -D FORWARD 3
 -I FORWARD 3 -m comment --comment "manually replaced rule 2" -j ACCEPT
diff --git a/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0 b/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
index a92d18dcee0588b4e9f1bb98391122c0874f8b11..96a5e66d0ab81440e07daac592e530b6fb7102b0 100755
--- a/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
+++ b/iptables/tests/shell/testcases/ipt-restore/0004-restore-race_0
@@ -24,7 +24,7 @@ clean_tempfile()
 
 trap clean_tempfile EXIT
 
-ENTRY_NUM=$((RANDOM%100))
+ENTRY_NUM=$((RANDOM%10))
 UCHAIN_NUM=$((RANDOM%10))
 
 get_target()
@@ -87,7 +87,7 @@ fi
 
 case "$XT_MULTI" in
 */xtables-nft-multi)
-	attempts=$((RANDOM%200))
+	attempts=$((RANDOM%10))
 	attempts=$((attempts+1))
 	;;
 *)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0005-ipt-6_0 b/iptables/tests/shell/testcases/ipt-restore/0005-ipt-6_0
new file mode 100755
index 0000000000000000000000000000000000000000..dd069771022c7caac9b40886c12cfe408ed3303f
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0005-ipt-6_0
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Make sure iptables-restore simply ignores
+# rules starting with -6
+
+set -e
+
+# show rules, drop uninteresting policy settings
+ipt_show() {
+	$XT_MULTI iptables -S | grep -v '^-P'
+}
+
+# issue reproducer for iptables-restore
+
+$XT_MULTI iptables-restore <<EOF
+*filter
+-A FORWARD -m comment --comment any -j ACCEPT
+-4 -A FORWARD -m comment --comment ipv4 -j ACCEPT
+-6 -A FORWARD -m comment --comment ipv6 -j ACCEPT
+COMMIT
+EOF
+
+EXPECT='-A FORWARD -m comment --comment any -j ACCEPT
+-A FORWARD -m comment --comment ipv4 -j ACCEPT'
+
+diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0006-ip6t-4_0 b/iptables/tests/shell/testcases/ipt-restore/0006-ip6t-4_0
new file mode 100755
index 0000000000000000000000000000000000000000..a37253a9d78e27894c50d1cbeaa6d6812905a55b
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0006-ip6t-4_0
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# Make sure ip6tables-restore simply ignores
+# rules starting with -4
+
+set -e
+
+# show rules, drop uninteresting policy settings
+ipt_show() {
+	$XT_MULTI ip6tables -S | grep -v '^-P'
+}
+
+# issue reproducer for ip6tables-restore
+
+$XT_MULTI ip6tables-restore <<EOF
+*filter
+-A FORWARD -m comment --comment any -j ACCEPT
+-4 -A FORWARD -m comment --comment ipv4 -j ACCEPT
+-6 -A FORWARD -m comment --comment ipv6 -j ACCEPT
+COMMIT
+EOF
+
+EXPECT='-A FORWARD -m comment --comment any -j ACCEPT
+-A FORWARD -m comment --comment ipv6 -j ACCEPT'
+
+diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0007-flush-noflush_0 b/iptables/tests/shell/testcases/ipt-restore/0007-flush-noflush_0
new file mode 100755
index 0000000000000000000000000000000000000000..029db2235b9a49b3d1c3cdfdf3b7dd61c18210db
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0007-flush-noflush_0
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+# Make sure iptables-restore without --noflush does not flush tables other than
+# those contained in the dump it's reading from
+
+set -e
+
+$XT_MULTI iptables-restore <<EOF
+*nat
+-A POSTROUTING -j ACCEPT
+COMMIT
+EOF
+
+EXPECT="*nat
+:PREROUTING ACCEPT [0:0]
+:INPUT ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+:POSTROUTING ACCEPT [0:0]
+-A POSTROUTING -j ACCEPT
+COMMIT"
+diff -u -Z <(echo -e "$EXPECT" | sort) <($XT_MULTI iptables-save | grep -v '^#' | sort)
+
+$XT_MULTI iptables-restore <<EOF
+*filter
+-A FORWARD -j ACCEPT
+COMMIT
+EOF
+
+EXPECT="*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A FORWARD -j ACCEPT
+COMMIT
+*nat
+:PREROUTING ACCEPT [0:0]
+:INPUT ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+:POSTROUTING ACCEPT [0:0]
+-A POSTROUTING -j ACCEPT
+COMMIT"
+diff -u -Z <(echo -e "$EXPECT" | sort) <($XT_MULTI iptables-save | grep -v '^#' | sort)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0 b/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0
new file mode 100755
index 0000000000000000000000000000000000000000..5ac70682b76bf7d23fab856d54f38ea1242553e6
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0008-restore-counters_0
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+DUMP="*filter
+:foo - [23:42]
+[13:37] -A foo -j ACCEPT
+COMMIT
+"
+
+EXPECT=":foo - [0:0]
+[0:0] -A foo -j ACCEPT"
+
+$XT_MULTI iptables-restore <<< "$DUMP"
+diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables-save --counters | grep foo)
+
+# iptables-*-restore ignores custom chain counters :(
+EXPECT=":foo - [0:0]
+[13:37] -A foo -j ACCEPT"
+
+$XT_MULTI iptables-restore --counters <<< "$DUMP"
+diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables-save --counters | grep foo)
diff --git a/iptables/tests/shell/testcases/ipt-restore/0009-table-name-comment_0 b/iptables/tests/shell/testcases/ipt-restore/0009-table-name-comment_0
new file mode 100755
index 0000000000000000000000000000000000000000..e96140758a99d21ad3b01143c65abc5b706da2b7
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-restore/0009-table-name-comment_0
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# when restoring a ruleset, *tables-restore prefixes each rule with
+# '-t <tablename>' so standard rule parsing routines may be used. This means
+# that it has to detect and reject rules which already contain a table option.
+
+families="ip ip6"
+[[ $(basename $XT_MULTI) == xtables-nft-multi ]] && families+=" eb"
+
+for fam in $families; do
+	$XT_MULTI ${fam}tables-restore <<EOF
+*filter
+-t nat -A FORWARD -j ACCEPT
+COMMIT
+EOF
+	[[ $? != 0 ]] || {
+		echo "${fam}tables-restore did not fail when it should have"
+		exit 1
+	}
+
+	$XT_MULTI ${fam}tables-restore <<EOF
+*filter
+-A FORWARD -j ACCEPT
+COMMIT
+EOF
+	[[ $? == 0 ]] || {
+		echo "${fam}tables-restore failed when it should not have"
+		exit 1
+	}
+done
diff --git a/iptables/tests/shell/testcases/ipt-save/0006iptables-xml_0 b/iptables/tests/shell/testcases/ipt-save/0006iptables-xml_0
new file mode 100755
index 0000000000000000000000000000000000000000..50c0cae88834165d578cb7e9b7a271f3bfa59903
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-save/0006iptables-xml_0
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+case "$(basename $XT_MULTI)" in
+	xtables-legacy-multi)
+		;;
+	*)
+		echo "skip $XT_MULTI"
+		exit 0
+		;;
+esac
+
+dump=$(dirname $0)/dumps/fedora27-iptables
+diff -u -Z <(cat ${dump}.xml) <($XT_MULTI iptables-xml <$dump)
diff --git a/iptables/tests/shell/testcases/ipt-save/dumps/fedora27-iptables.xml b/iptables/tests/shell/testcases/ipt-save/dumps/fedora27-iptables.xml
new file mode 100644
index 0000000000000000000000000000000000000000..400be032fbd200b1057fde7fa0b770686038ee86
--- /dev/null
+++ b/iptables/tests/shell/testcases/ipt-save/dumps/fedora27-iptables.xml
@@ -0,0 +1,925 @@
+<iptables-rules version="1.0">
+<!-- # Completed on Sat Feb 17 10:50:33 2018 -->
+<!-- # Generated by iptables*-save v1.6.1 on Sat Feb 17 10:50:33 2018 -->
+  <table name="mangle" >
+    <chain name="PREROUTING" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="1" byte-count="2" >
+       <actions>
+        <call >
+          <PREROUTING_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="3" byte-count="4" >
+       <actions>
+        <call >
+          <PREROUTING_ZONES_SOURCE />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PREROUTING_ZONES />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="INPUT" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <INPUT_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="OUTPUT" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <OUTPUT_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="POSTROUTING" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <o >virbr0</o>
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >68</dport>
+        </udp>
+       </conditions>
+       <actions>
+        <CHECKSUM >
+          <checksum-fill  />
+        </CHECKSUM>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <POSTROUTING_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="PREROUTING_ZONES" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >wlp58s0</i>
+        </match>
+       </conditions>
+       <actions>
+        <goto >
+          <PRE_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <goto >
+          <PRE_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="PRE_FedoraWorkstation" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_log />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_deny />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_allow />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD_direct" packet-count="0" byte-count="0" />
+    <chain name="INPUT_direct" packet-count="0" byte-count="0" />
+    <chain name="OUTPUT_direct" packet-count="0" byte-count="0" />
+    <chain name="POSTROUTING_direct" packet-count="0" byte-count="0" />
+    <chain name="PREROUTING_ZONES_SOURCE" packet-count="0" byte-count="0" />
+    <chain name="PREROUTING_direct" packet-count="0" byte-count="0" />
+    <chain name="PRE_FedoraWorkstation_allow" packet-count="0" byte-count="0" />
+    <chain name="PRE_FedoraWorkstation_deny" packet-count="0" byte-count="0" />
+    <chain name="PRE_FedoraWorkstation_log" packet-count="0" byte-count="0" />
+  </table>
+<!-- # Completed on Sat Feb 17 10:50:33 2018 -->
+<!-- # Generated by iptables*-save v1.6.1 on Sat Feb 17 10:50:33 2018 -->
+  <table name="raw" >
+    <chain name="PREROUTING" policy="ACCEPT" packet-count="1681" byte-count="2620433" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PREROUTING_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PREROUTING_ZONES_SOURCE />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PREROUTING_ZONES />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="OUTPUT" policy="ACCEPT" packet-count="1619" byte-count="171281" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <OUTPUT_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="PREROUTING_ZONES" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >wlp58s0</i>
+        </match>
+       </conditions>
+       <actions>
+        <goto >
+          <PRE_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <goto >
+          <PRE_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="PRE_FedoraWorkstation" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_log />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_deny />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <PRE_FedoraWorkstation_allow />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="PRE_FedoraWorkstation_allow" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >137</dport>
+        </udp>
+       </conditions>
+       <actions>
+        <CT >
+          <helper >netbios-ns</helper>
+        </CT>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="OUTPUT_direct" packet-count="0" byte-count="0" />
+    <chain name="PREROUTING_ZONES_SOURCE" packet-count="0" byte-count="0" />
+    <chain name="PREROUTING_direct" packet-count="0" byte-count="0" />
+    <chain name="PRE_FedoraWorkstation_deny" packet-count="0" byte-count="0" />
+    <chain name="PRE_FedoraWorkstation_log" packet-count="0" byte-count="0" />
+  </table>
+<!-- # Completed on Sat Feb 17 10:50:33 2018 -->
+<!-- # Generated by iptables*-save v1.6.1 on Sat Feb 17 10:50:33 2018 -->
+  <table name="filter" >
+    <chain name="INPUT" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="5" byte-count="6" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >53</dport>
+        </udp>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="123456789" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+          <p >tcp</p>
+        </match>
+        <tcp >
+          <dport >53</dport>
+        </tcp>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >67</dport>
+        </udp>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+          <p >tcp</p>
+        </match>
+        <tcp >
+          <dport >67</dport>
+        </tcp>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <conntrack >
+          <ctstate >RELATED,ESTABLISHED</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >lo</i>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <INPUT_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <INPUT_ZONES_SOURCE />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <INPUT_ZONES />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <conntrack >
+          <ctstate >INVALID</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <DROP  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <REJECT >
+          <reject-with >icmp-host-prohibited</reject-with>
+        </REJECT>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD" policy="ACCEPT" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <d >192.168.122.0/24</d>
+          <o >virbr0</o>
+        </match>
+        <conntrack >
+          <ctstate >RELATED,ESTABLISHED</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <s >192.168.122.0/24</s>
+          <i >virbr0</i>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+          <o >virbr0</o>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <o >virbr0</o>
+        </match>
+       </conditions>
+       <actions>
+        <REJECT >
+          <reject-with >icmp-port-unreachable</reject-with>
+        </REJECT>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >virbr0</i>
+        </match>
+       </conditions>
+       <actions>
+        <REJECT >
+          <reject-with >icmp-port-unreachable</reject-with>
+        </REJECT>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <conntrack >
+          <ctstate >RELATED,ESTABLISHED</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >lo</i>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_IN_ZONES_SOURCE />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_IN_ZONES />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_OUT_ZONES_SOURCE />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FORWARD_OUT_ZONES />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <conntrack >
+          <ctstate >INVALID</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <DROP  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <REJECT >
+          <reject-with >icmp-host-prohibited</reject-with>
+        </REJECT>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="OUTPUT" policy="ACCEPT" packet-count="1619" byte-count="171281" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <o >virbr0</o>
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >68</dport>
+        </udp>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <OUTPUT_direct />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD_IN_ZONES" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >wlp58s0</i>
+        </match>
+       </conditions>
+       <actions>
+        <goto >
+          <FWDI_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <goto >
+          <FWDI_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD_OUT_ZONES" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <o >wlp58s0</o>
+        </match>
+       </conditions>
+       <actions>
+        <goto >
+          <FWDO_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <goto >
+          <FWDO_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FWDI_FedoraWorkstation" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDI_FedoraWorkstation_log />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDI_FedoraWorkstation_deny />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDI_FedoraWorkstation_allow />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >icmp</p>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FWDO_FedoraWorkstation" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDO_FedoraWorkstation_log />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDO_FedoraWorkstation_deny />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <FWDO_FedoraWorkstation_allow />
+        </call>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="INPUT_ZONES" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <i >wlp58s0</i>
+        </match>
+       </conditions>
+       <actions>
+        <goto >
+          <IN_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <goto >
+          <IN_FedoraWorkstation />
+        </goto>
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="IN_FedoraWorkstation" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <IN_FedoraWorkstation_log />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <IN_FedoraWorkstation_deny />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <actions>
+        <call >
+          <IN_FedoraWorkstation_allow />
+        </call>
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >icmp</p>
+        </match>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="IN_FedoraWorkstation_allow" packet-count="0" byte-count="0" >
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >137</dport>
+        </udp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >138</dport>
+        </udp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >tcp</p>
+        </match>
+        <tcp >
+          <dport >22</dport>
+        </tcp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <d >224.0.0.251/32</d>
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >5353</dport>
+        </udp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="0" byte-count="0" >
+       <conditions>
+        <match >
+          <p >udp</p>
+        </match>
+        <udp >
+          <dport >1025:65535</dport>
+        </udp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+      <rule packet-count="7" byte-count="8" >
+       <conditions>
+        <match >
+          <p >tcp</p>
+        </match>
+        <tcp >
+          <dport >1025:65535</dport>
+        </tcp>
+        <conntrack >
+          <ctstate >NEW</ctstate>
+        </conntrack>
+       </conditions>
+       <actions>
+        <ACCEPT  />
+       </actions>
+
+      </rule>
+
+    </chain>
+    <chain name="FORWARD_IN_ZONES_SOURCE" packet-count="0" byte-count="0" />
+    <chain name="FORWARD_OUT_ZONES_SOURCE" packet-count="0" byte-count="0" />
+    <chain name="FORWARD_direct" packet-count="0" byte-count="0" />
+    <chain name="FWDI_FedoraWorkstation_allow" packet-count="0" byte-count="0" />
+    <chain name="FWDI_FedoraWorkstation_deny" packet-count="0" byte-count="0" />
+    <chain name="FWDI_FedoraWorkstation_log" packet-count="0" byte-count="0" />
+    <chain name="FWDO_FedoraWorkstation_allow" packet-count="0" byte-count="0" />
+    <chain name="FWDO_FedoraWorkstation_deny" packet-count="0" byte-count="0" />
+    <chain name="FWDO_FedoraWorkstation_log" packet-count="0" byte-count="0" />
+    <chain name="INPUT_ZONES_SOURCE" packet-count="0" byte-count="0" />
+    <chain name="INPUT_direct" packet-count="0" byte-count="0" />
+    <chain name="IN_FedoraWorkstation_deny" packet-count="0" byte-count="0" />
+    <chain name="IN_FedoraWorkstation_log" packet-count="0" byte-count="0" />
+    <chain name="OUTPUT_direct" packet-count="0" byte-count="0" />
+  </table>
+<!-- # Completed on Sat Feb 17 10:50:33 2018 -->
+</iptables-rules>
diff --git a/iptables/xshared.c b/iptables/xshared.c
index 36a2ec5f193d32a4b7ffee9db83f327330b80ba2..16c58914e59a5a3e4f14807d30e372c4d7096bcc 100644
--- a/iptables/xshared.c
+++ b/iptables/xshared.c
@@ -181,7 +181,6 @@ int command_default(struct iptables_command_state *cs,
 		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)
@@ -374,6 +373,43 @@ int parse_counters(const char *string, struct xt_counters *ctr)
 	return ret == 2;
 }
 
+/* Tokenize counters argument of typical iptables-restore format rule.
+ *
+ * If *bufferp contains counters, update *pcntp and *bcntp to point at them,
+ * change bytes after counters in *bufferp to nul-bytes, update *bufferp to
+ * point to after the counters and return true.
+ * If *bufferp does not contain counters, return false.
+ * If syntax is wrong in *bufferp, call xtables_error() and hence exit().
+ * */
+bool tokenize_rule_counters(char **bufferp, char **pcntp, char **bcntp, int line)
+{
+	char *ptr, *buffer = *bufferp, *pcnt, *bcnt;
+
+	if (buffer[0] != '[')
+		return false;
+
+	/* 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);
+
+	*pcntp = pcnt;
+	*bcntp = bcnt;
+	/* start command parsing after counter */
+	*bufferp = ptr + 1;
+
+	return true;
+}
+
 inline bool xs_has_arg(int argc, char *argv[])
 {
 	return optind < argc &&
@@ -381,56 +417,48 @@ inline bool xs_has_arg(int argc, char *argv[])
 	       argv[optind][0] != '!';
 }
 
-/* global new argv and argc */
-char *newargv[255];
-int newargc = 0;
-
-/* saved newargv and newargc from save_argv() */
-char *oldargv[255];
-int oldargc = 0;
-
-/* arg meta data, were they quoted, frinstance */
-int newargvattr[255];
-
-/* function adding one argument to newargv, updating newargc
- * returns true if argument added, false otherwise */
-int add_argv(const char *what, int quoted)
+/* function adding one argument to store, updating argc
+ * returns if argument added, does not return otherwise */
+void add_argv(struct argv_store *store, const char *what, int quoted)
 {
 	DEBUGP("add_argv: %s\n", what);
-	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
-		newargv[newargc] = strdup(what);
-		newargvattr[newargc] = quoted;
-		newargv[++newargc] = NULL;
-		return 1;
-	} else {
+
+	if (store->argc + 1 >= MAX_ARGC)
 		xtables_error(PARAMETER_PROBLEM,
 			      "Parser cannot handle more arguments\n");
-	}
+	if (!what)
+		xtables_error(PARAMETER_PROBLEM,
+			      "Trying to store NULL argument\n");
+
+	store->argv[store->argc] = strdup(what);
+	store->argvattr[store->argc] = quoted;
+	store->argv[++store->argc] = NULL;
 }
 
-void free_argv(void)
+void free_argv(struct argv_store *store)
 {
-	while (newargc)
-		free(newargv[--newargc]);
-	while (oldargc)
-		free(oldargv[--oldargc]);
+	while (store->argc) {
+		store->argc--;
+		free(store->argv[store->argc]);
+		store->argvattr[store->argc] = 0;
+	}
 }
 
 /* Save parsed rule for comparison with next rule to perform action aggregation
  * on duplicate conditions.
  */
-void save_argv(void)
+void save_argv(struct argv_store *dst, struct argv_store *src)
 {
-	unsigned int i;
-
-	while (oldargc)
-		free(oldargv[--oldargc]);
+	int i;
 
-	oldargc = newargc;
-	newargc = 0;
-	for (i = 0; i < oldargc; i++) {
-		oldargv[i] = newargv[i];
+	free_argv(dst);
+	for (i = 0; i < src->argc; i++) {
+		dst->argvattr[i] = src->argvattr[i];
+		dst->argv[i] = src->argv[i];
+		src->argv[i] = NULL;
 	}
+	dst->argc = src->argc;
+	src->argc = 0;
 }
 
 struct xt_param_buf {
@@ -446,9 +474,9 @@ static void add_param(struct xt_param_buf *param, const char *curchar)
 			      "Parameter too long!");
 }
 
-void add_param_to_argv(char *parsestart, int line)
+void add_param_to_argv(struct argv_store *store, char *parsestart, int line)
 {
-	int quote_open = 0, escaped = 0;
+	int quote_open = 0, escaped = 0, quoted = 0;
 	struct xt_param_buf param = {};
 	char *curchar;
 
@@ -475,6 +503,7 @@ void add_param_to_argv(char *parsestart, int line)
 		} else {
 			if (*curchar == '"') {
 				quote_open = 1;
+				quoted = 1;
 				continue;
 			}
 		}
@@ -497,23 +526,26 @@ void add_param_to_argv(char *parsestart, int line)
 		}
 
 		param.buffer[param.len] = '\0';
-
-		/* check if table name specified */
-		if ((param.buffer[0] == '-' &&
-		     param.buffer[1] != '-' &&
-		     strchr(param.buffer, 't')) ||
-		    (!strncmp(param.buffer, "--t", 3) &&
-		     !strncmp(param.buffer, "--table", strlen(param.buffer)))) {
-			xtables_error(PARAMETER_PROBLEM,
-				      "The -t option (seen in line %u) cannot be used in %s.\n",
-				      line, xt_params->program_name);
-		}
-
-		add_argv(param.buffer, 0);
+		add_argv(store, param.buffer, quoted);
 		param.len = 0;
+		quoted = 0;
+	}
+	if (param.len) {
+		param.buffer[param.len] = '\0';
+		add_argv(store, param.buffer, 0);
 	}
 }
 
+#ifdef DEBUG
+void debug_print_argv(struct argv_store *store)
+{
+	int i;
+
+	for (i = 0; i < store->argc; i++)
+		fprintf(stderr, "argv[%d]: %s\n", i, store->argv[i]);
+}
+#endif
+
 static const char *ipv4_addr_to_string(const struct in_addr *addr,
 				       const struct in_addr *mask,
 				       unsigned int format)
@@ -704,3 +736,42 @@ void command_jump(struct iptables_command_state *cs, const char *jumpto)
 		xtables_error(OTHER_PROBLEM, "can't alloc memory!");
 	xt_params->opts = opts;
 }
+
+char cmd2char(int option)
+{
+	/* cmdflags index corresponds with position of bit in CMD_* values */
+	static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+					 'N', 'X', 'P', 'E', 'S', 'Z', 'C' };
+	int i;
+
+	for (i = 0; option > 1; option >>= 1, i++)
+		;
+	if (i >= ARRAY_SIZE(cmdflags))
+		xtables_error(OTHER_PROBLEM,
+			      "cmd2char(): Invalid command number %u.\n",
+			      1 << i);
+	return cmdflags[i];
+}
+
+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;
+}
+
+/* Can't be zero. */
+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;
+}
diff --git a/iptables/xshared.h b/iptables/xshared.h
index fd1f96bad1b989a33324fc5ccaf95d312a44500c..490b19ade510679b185957f281912a7f616b48ca 100644
--- a/iptables/xshared.h
+++ b/iptables/xshared.h
@@ -11,7 +11,7 @@
 #include <linux/netfilter_ipv6/ip6_tables.h>
 
 #ifdef DEBUG
-#define DEBUGP(x, args...) fprintf(stdout, x, ## args)
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
 #else
 #define DEBUGP(x, args...)
 #endif
@@ -29,8 +29,35 @@ enum {
 	OPT_VIANAMEOUT  = 1 << 8,
 	OPT_LINENUMBERS = 1 << 9,
 	OPT_COUNTERS    = 1 << 10,
+	/* below are for arptables only */
+	OPT_S_MAC	= 1 << 11,
+	OPT_D_MAC	= 1 << 12,
+	OPT_H_LENGTH	= 1 << 13,
+	OPT_OPCODE	= 1 << 14,
+	OPT_H_TYPE	= 1 << 15,
+	OPT_P_TYPE	= 1 << 16,
 };
 
+enum {
+	CMD_NONE		= 0,
+	CMD_INSERT		= 1 << 0,
+	CMD_DELETE		= 1 << 1,
+	CMD_DELETE_NUM		= 1 << 2,
+	CMD_REPLACE		= 1 << 3,
+	CMD_APPEND		= 1 << 4,
+	CMD_LIST		= 1 << 5,
+	CMD_FLUSH		= 1 << 6,
+	CMD_ZERO		= 1 << 7,
+	CMD_NEW_CHAIN		= 1 << 8,
+	CMD_DELETE_CHAIN	= 1 << 9,
+	CMD_SET_POLICY		= 1 << 10,
+	CMD_RENAME_CHAIN	= 1 << 11,
+	CMD_LIST_RULES		= 1 << 12,
+	CMD_ZERO_NUM		= 1 << 13,
+	CMD_CHECK		= 1 << 14,
+};
+#define NUMBER_OF_CMD		16
+
 struct xtables_globals;
 struct xtables_rule_match;
 struct xtables_target;
@@ -151,22 +178,27 @@ extern int xtables_lock_or_exit(int wait, struct timeval *tv);
 int parse_wait_time(int argc, char *argv[]);
 void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval);
 int parse_counters(const char *string, struct xt_counters *ctr);
+bool tokenize_rule_counters(char **bufferp, char **pcnt, char **bcnt, int line);
 bool xs_has_arg(int argc, char *argv[]);
 
 extern const struct xtables_afinfo *afinfo;
 
-extern char *newargv[];
-extern int newargc;
-
-extern char *oldargv[];
-extern int oldargc;
-
-extern int newargvattr[];
+#define MAX_ARGC	255
+struct argv_store {
+	int argc;
+	char *argv[MAX_ARGC];
+	int argvattr[MAX_ARGC];
+};
 
-int add_argv(const char *what, int quoted);
-void free_argv(void);
-void save_argv(void);
-void add_param_to_argv(char *parsestart, int line);
+void add_argv(struct argv_store *store, const char *what, int quoted);
+void free_argv(struct argv_store *store);
+void save_argv(struct argv_store *dst, struct argv_store *src);
+void add_param_to_argv(struct argv_store *store, char *parsestart, int line);
+#ifdef DEBUG
+void debug_print_argv(struct argv_store *store);
+#else
+#  define debug_print_argv(...) /* nothing */
+#endif
 
 void print_ipv4_addresses(const struct ipt_entry *fw, unsigned int format);
 void print_ipv6_addresses(const struct ip6t_entry *fw6, unsigned int format);
@@ -178,4 +210,9 @@ void command_match(struct iptables_command_state *cs);
 const char *xt_parse_target(const char *targetname);
 void command_jump(struct iptables_command_state *cs, const char *jumpto);
 
+char cmd2char(int option);
+void add_command(unsigned int *cmd, const int newcmd,
+		 const int othercmds, int invert);
+int parse_rulenumber(const char *rule);
+
 #endif /* IPTABLES_XSHARED_H */
diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
index d3cb9df823febbc2561831260d9779c0bcafba96..9cfad76263d32eda56a1a87ca8aa3d05e06092ef 100644
--- a/iptables/xtables-arp.c
+++ b/iptables/xtables-arp.c
@@ -27,7 +27,7 @@
   This tool is not luser-proof: you can specify an Ethernet source address
   and set hardware length to something different than 6, f.e.
 */
-
+#include "config.h"
 #include <getopt.h>
 #include <string.h>
 #include <netdb.h>
@@ -53,56 +53,6 @@
 #include "nft-arp.h"
 #include <linux/netfilter_arp/arp_tables.h>
 
-typedef char arpt_chainlabel[32];
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-/* XXX: command defined by nft-shared.h do not overlap with these two */
-#undef CMD_CHECK
-#undef CMD_RENAME_CHAIN
-
-#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_CHECK		0x0800U
-#define CMD_RENAME_CHAIN	0x1000U
-#define NUMBER_OF_CMD	13
-static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
-				 'N', 'X', 'P', 'E' };
-
-#define OPTION_OFFSET 256
-
-#define OPT_NONE	0x00000U
-#define OPT_NUMERIC	0x00001U
-#define OPT_S_IP	0x00002U
-#define OPT_D_IP	0x00004U
-#define OPT_S_MAC	0x00008U
-#define OPT_D_MAC	0x00010U
-#define OPT_H_LENGTH	0x00020U
-#define OPT_P_LENGTH	0x00040U
-#define OPT_OPCODE	0x00080U
-#define OPT_H_TYPE	0x00100U
-#define OPT_P_TYPE	0x00200U
-#define OPT_JUMP	0x00400U
-#define OPT_VERBOSE	0x00800U
-#define OPT_VIANAMEIN	0x01000U
-#define OPT_VIANAMEOUT	0x02000U
-#define OPT_LINENUMBERS 0x04000U
-#define OPT_COUNTERS	0x08000U
 #define NUMBER_OF_OPT	16
 static const char optflags[NUMBER_OF_OPT]
 = { 'n', 's', 'd', 2, 3, 7, 8, 4, 5, 6, 'j', 'v', 'i', 'o', '0', 'c'};
@@ -148,85 +98,39 @@ static struct option original_opts[] = {
 	{ 0 }
 };
 
-int RUNTIME_NF_ARP_NUMHOOKS = 3;
-
 #define opts xt_params->opts
 
 extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
 struct xtables_globals arptables_globals = {
 	.option_offset		= 0,
-	.program_version	= IPTABLES_VERSION,
+	.program_version	= PACKAGE_VERSION,
 	.orig_opts		= original_opts,
 	.exit_err		= xtables_exit_error,
 	.compat_rev		= nft_compatible_revision,
 };
 
-/* 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 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  -f  --line */
-/*INSERT*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*DELETE*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*DELETE_NUM*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*REPLACE*/   {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*APPEND*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*LIST*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*FLUSH*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*ZERO*/      {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*NEW_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*DEL_CHAIN*/ {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*SET_POLICY*/{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*CHECK*/     {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
-/*RENAME*/    {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}
-};
-
-static int inverse_for_options[NUMBER_OF_OPT] =
+/* index relates to bit of each OPT_* value */
+static int inverse_for_options[] =
 {
 /* -n */ 0,
 /* -s */ ARPT_INV_SRCIP,
 /* -d */ ARPT_INV_TGTIP,
-/* 2 */ ARPT_INV_SRCDEVADDR,
-/* 3 */ ARPT_INV_TGTDEVADDR,
-/* -l */ ARPT_INV_ARPHLN,
-/* 8 */ 0,
-/* 4 */ ARPT_INV_ARPOP,
-/* 5 */ ARPT_INV_ARPHRD,
-/* 6 */ ARPT_INV_ARPPRO,
+/* -p */ 0,
 /* -j */ 0,
 /* -v */ 0,
+/* -x */ 0,
 /* -i */ ARPT_INV_VIA_IN,
 /* -o */ ARPT_INV_VIA_OUT,
 /*--line*/ 0,
 /* -c */ 0,
+/* 2 */ ARPT_INV_SRCDEVADDR,
+/* 3 */ ARPT_INV_TGTDEVADDR,
+/* -l */ ARPT_INV_ARPHLN,
+/* 4 */ ARPT_INV_ARPOP,
+/* 5 */ ARPT_INV_ARPHRD,
+/* 6 */ ARPT_INV_ARPPRO,
 };
 
-/* A few hardcoded protocols for 'all' and in case the user has no
-   /etc/protocols */
-struct pprot {
-	char *name;
-	u_int8_t num;
-};
-
-/* Primitive headers... */
-/* defined in netinet/in.h */
-#if 0
-#ifndef IPPROTO_ESP
-#define IPPROTO_ESP 50
-#endif
-#ifndef IPPROTO_AH
-#define IPPROTO_AH 51
-#endif
-#endif
-
 /***********************************************/
 /* ARPTABLES SPECIFIC NEW FUNCTIONS ADDED HERE */
 /***********************************************/
@@ -317,89 +221,10 @@ static int get16_and_mask(char *from, uint16_t *to, uint16_t *mask, int base)
 	return 0;
 }
 
-static int
-string_to_number(const char *s, unsigned int min, unsigned int max,
-		 unsigned int *ret)
-{
-	long number;
-	char *end;
-
-	/* Handle hex, octal, etc. */
-	errno = 0;
-	number = strtol(s, &end, 0);
-	if (*end == '\0' && end != s) {
-		/* we parsed a number, let's see if we want this */
-		if (errno != ERANGE && min <= number && number <= max) {
-			*ret = number;
-			return 0;
-		}
-	}
-	return -1;
-}
-
 /*********************************************/
 /* ARPTABLES SPECIFIC NEW FUNCTIONS END HERE */
 /*********************************************/
 
-static struct in_addr *
-dotted_to_addr(const char *dotted)
-{
-	static struct in_addr addr;
-	unsigned char *addrp;
-	char *p, *q;
-	unsigned int onebyte;
-	int i;
-	char buf[20];
-
-	/* copy dotted string, because we need to modify it */
-	strncpy(buf, dotted, sizeof(buf) - 1);
-	addrp = (unsigned char *) &(addr.s_addr);
-
-	p = buf;
-	for (i = 0; i < 3; i++) {
-		if ((q = strchr(p, '.')) == NULL)
-			return (struct in_addr *) NULL;
-
-		*q = '\0';
-		if (string_to_number(p, 0, 255, &onebyte) == -1)
-			return (struct in_addr *) NULL;
-
-		addrp[i] = (unsigned char) onebyte;
-		p = q + 1;
-	}
-
-	/* we've checked 3 bytes, now we check the last one */
-	if (string_to_number(p, 0, 255, &onebyte) == -1)
-		return (struct in_addr *) NULL;
-
-	addrp[3] = (unsigned char) onebyte;
-
-	return &addr;
-}
-
-static struct in_addr *
-network_to_addr(const char *name)
-{
-	struct netent *net;
-	static struct in_addr addr;
-
-	if ((net = getnetbyname(name)) != NULL) {
-		if (net->n_addrtype != AF_INET)
-			return (struct in_addr *) NULL;
-		addr.s_addr = htonl((unsigned long) net->n_net);
-		return &addr;
-	}
-
-	return (struct in_addr *) NULL;
-}
-
-static void
-inaddrcpy(struct in_addr *dst, struct in_addr *src)
-{
-	/* memcpy(dst, src, sizeof(struct in_addr)); */
-	dst->s_addr = src->s_addr;
-}
-
 static void
 exit_tryhelp(int status)
 {
@@ -503,42 +328,6 @@ exit_printhelp(void)
 	exit(0);
 }
 
-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)
 {
@@ -548,26 +337,6 @@ opt2char(int option)
 	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 unsigned int othercmds, int invert)
-{
-	if (invert)
-		xtables_error(PARAMETER_PROBLEM, "unexpected ! flag");
-	if (*cmd & (~othercmds))
-		xtables_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
-			      cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
-	*cmd |= newcmd;
-}
-
 static int
 check_inverse(const char option[], int *invert, int *optidx, int argc)
 {
@@ -575,7 +344,7 @@ check_inverse(const char option[], int *invert, int *optidx, int argc)
 		if (*invert)
 			xtables_error(PARAMETER_PROBLEM,
 				      "Multiple `!' flags not allowed");
-		*invert = TRUE;
+		*invert = true;
 		if (optidx) {
 			*optidx = *optidx+1;
 			if (argc && *optidx > argc)
@@ -583,181 +352,9 @@ check_inverse(const char option[], int *invert, int *optidx, int argc)
 					      "no argument following `!'");
 		}
 
-		return TRUE;
+		return true;
 	}
-	return FALSE;
-}
-
-static struct in_addr *
-host_to_addr(const char *name, unsigned int *naddr)
-{
-	struct in_addr *addr;
-	struct addrinfo hints = {
-		.ai_flags	= AI_CANONNAME,
-		.ai_family	= AF_INET,
-		.ai_socktype	= SOCK_RAW,
-	};;
-	struct addrinfo *res, *p;
-	int err;
-	unsigned int i;
-
-	*naddr = 0;
-	err = getaddrinfo(name, NULL, &hints, &res);
-	if (err != 0)
-		return NULL;
-	else {
-		for (p = res; p != NULL; p = p->ai_next)
-			(*naddr)++;
-		addr = xtables_calloc(*naddr, sizeof(struct in_addr));
-		for (i = 0, p = res; p != NULL; p = p->ai_next)
-			memcpy(&addr[i++],
-			       &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
-			       sizeof(struct in_addr));
-		freeaddrinfo(res);
-		return addr;
-	}
-
-	return (struct in_addr *) NULL;
-}
-
-/*
- *	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.
-*/
-
-static struct in_addr *
-parse_hostnetwork(const char *name, unsigned int *naddrs)
-{
-	struct in_addr *addrp, *addrptmp;
-
-	if ((addrptmp = dotted_to_addr(name)) != NULL ||
-	    (addrptmp = network_to_addr(name)) != NULL) {
-		addrp = xtables_malloc(sizeof(struct in_addr));
-		inaddrcpy(addrp, addrptmp);
-		*naddrs = 1;
-		return addrp;
-	}
-	if ((addrp = host_to_addr(name, naddrs)) != NULL)
-		return addrp;
-
-	xtables_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
-}
-
-static struct in_addr *
-parse_mask(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 = dotted_to_addr(mask)) != NULL)
-		/* dotted_to_addr already returns a network byte order addr */
-		return addrp;
-	if (string_to_number(mask, 0, 32, &bits) == -1)
-		xtables_error(PARAMETER_PROBLEM,
-			      "invalid mask `%s' specified", mask);
-	if (bits != 0) {
-		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
-		return &maskaddr;
-	}
-
-	maskaddr.s_addr = 0L;
-	return &maskaddr;
-}
-
-static void
-parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
-		      struct in_addr *maskp, unsigned int *naddrs)
-{
-	struct in_addr *addrp;
-	char buf[256];
-	char *p;
-	int i, j, k, n;
-
-	strncpy(buf, name, sizeof(buf) - 1);
-	if ((p = strrchr(buf, '/')) != NULL) {
-		*p = '\0';
-		addrp = parse_mask(p + 1);
-	} else
-		addrp = parse_mask(NULL);
-	inaddrcpy(maskp, addrp);
-
-	/* if a null mask is given, the name is ignored, like in "any/0" */
-	if (maskp->s_addr == 0L)
-		strcpy(buf, "0.0.0.0");
-
-	addrp = *addrpp = parse_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) {
-				(*naddrs)--;
-				j--;
-				break;
-			}
-		}
-	}
-}
-
-static void
-parse_interface(const char *arg, char *vianame, unsigned char *mask)
-{
-	int vialen = strlen(arg);
-	unsigned int i;
-
-	memset(mask, 0, IFNAMSIZ);
-	memset(vianame, 0, IFNAMSIZ);
-
-	if (vialen + 1 > IFNAMSIZ)
-		xtables_error(PARAMETER_PROBLEM,
-			      "interface name `%s' must be shorter than IFNAMSIZ"
-			      " (%i)", arg, IFNAMSIZ-1);
-
-	strcpy(vianame, arg);
-	if (vialen == 0)
-		memset(mask, 0, IFNAMSIZ);
-	else if (vianame[vialen - 1] == '+') {
-		memset(mask, 0xFF, vialen - 1);
-		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
-		/* Don't remove `+' here! -HW */
-	} else {
-		/* Include nul-terminator in match */
-		memset(mask, 0xFF, vialen + 1);
-		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
-		for (i = 0; vianame[i]; i++) {
-			if (!isalnum(vianame[i])
-			    && vianame[i] != '_'
-			    && vianame[i] != '.') {
-				printf("Warning: weird character in interface"
-				       " `%s' (No aliases, :, ! or *).\n",
-				       vianame);
-				break;
-			}
-		}
-	}
-}
-
-/* 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;
+	return false;
 }
 
 static void
@@ -814,8 +411,10 @@ append_entry(struct nft_handle *h,
 	     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[],
 	     bool verbose, bool append)
 {
 	unsigned int i, j;
@@ -823,8 +422,10 @@ append_entry(struct nft_handle *h,
 
 	for (i = 0; i < nsaddrs; i++) {
 		cs->arp.arp.src.s_addr = saddrs[i].s_addr;
+		cs->arp.arp.smsk.s_addr = smasks[i].s_addr;
 		for (j = 0; j < ndaddrs; j++) {
 			cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
+			cs->arp.arp.tmsk.s_addr = dmasks[j].s_addr;
 			if (append) {
 				ret = nft_rule_append(h, chain, table, cs, NULL,
 						      verbose);
@@ -844,11 +445,15 @@ replace_entry(const char *chain,
 	      struct iptables_command_state *cs,
 	      unsigned int rulenum,
 	      const struct in_addr *saddr,
+	      const struct in_addr *smask,
 	      const struct in_addr *daddr,
+	      const struct in_addr *dmask,
 	      bool verbose, struct nft_handle *h)
 {
 	cs->arp.arp.src.s_addr = saddr->s_addr;
 	cs->arp.arp.tgt.s_addr = daddr->s_addr;
+	cs->arp.arp.smsk.s_addr = smask->s_addr;
+	cs->arp.arp.tmsk.s_addr = dmask->s_addr;
 
 	return nft_rule_replace(h, chain, table, cs, rulenum, verbose);
 }
@@ -859,8 +464,10 @@ delete_entry(const char *chain,
 	     struct iptables_command_state *cs,
 	     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 nft_handle *h)
 {
 	unsigned int i, j;
@@ -868,8 +475,10 @@ delete_entry(const char *chain,
 
 	for (i = 0; i < nsaddrs; i++) {
 		cs->arp.arp.src.s_addr = saddrs[i].s_addr;
+		cs->arp.arp.smsk.s_addr = smasks[i].s_addr;
 		for (j = 0; j < ndaddrs; j++) {
 			cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
+			cs->arp.arp.tmsk.s_addr = dmasks[j].s_addr;
 			ret = nft_rule_delete(h, chain, table, cs, verbose);
 		}
 	}
@@ -919,7 +528,8 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 	};
 	int invert = 0;
 	unsigned int nsaddrs = 0, ndaddrs = 0;
-	struct in_addr *saddrs = NULL, *daddrs = NULL;
+	struct in_addr *saddrs = NULL, *smasks = NULL;
+	struct in_addr *daddrs = NULL, *dmasks = NULL;
 
 	int c, verbose = 0;
 	const char *chain = NULL;
@@ -1067,14 +677,14 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 			break;
 		case 's':
 			check_inverse(optarg, &invert, &optind, argc);
-			set_option(&options, OPT_S_IP, &cs.arp.arp.invflags,
+			set_option(&options, OPT_SOURCE, &cs.arp.arp.invflags,
 				   invert);
 			shostnetworkmask = argv[optind-1];
 			break;
 
 		case 'd':
 			check_inverse(optarg, &invert, &optind, argc);
-			set_option(&options, OPT_D_IP, &cs.arp.arp.invflags,
+			set_option(&options, OPT_DESTINATION, &cs.arp.arp.invflags,
 				   invert);
 			dhostnetworkmask = argv[optind-1];
 			break;
@@ -1168,18 +778,18 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 			check_inverse(optarg, &invert, &optind, argc);
 			set_option(&options, OPT_VIANAMEIN, &cs.arp.arp.invflags,
 				   invert);
-			parse_interface(argv[optind-1],
-					cs.arp.arp.iniface,
-					cs.arp.arp.iniface_mask);
+			xtables_parse_interface(argv[optind-1],
+						cs.arp.arp.iniface,
+						cs.arp.arp.iniface_mask);
 			break;
 
 		case 'o':
 			check_inverse(optarg, &invert, &optind, argc);
 			set_option(&options, OPT_VIANAMEOUT, &cs.arp.arp.invflags,
 				   invert);
-			parse_interface(argv[optind-1],
-					cs.arp.arp.outiface,
-					cs.arp.arp.outiface_mask);
+			xtables_parse_interface(argv[optind-1],
+						cs.arp.arp.outiface,
+						cs.arp.arp.outiface_mask);
 			break;
 
 		case 'v':
@@ -1255,7 +865,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 					xtables_error(PARAMETER_PROBLEM,
 						      "multiple consecutive ! not"
 						      " allowed");
-				invert = TRUE;
+				invert = true;
 				optarg[0] = '\0';
 				continue;
 			}
@@ -1269,7 +879,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 			}
 			break;
 		}
-		invert = FALSE;
+		invert = false;
 	}
 
 	if (cs.target)
@@ -1285,19 +895,19 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 			      "nothing appropriate following !");
 
 	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
-		if (!(options & OPT_D_IP))
+		if (!(options & OPT_DESTINATION))
 			dhostnetworkmask = "0.0.0.0/0";
-		if (!(options & OPT_S_IP))
+		if (!(options & OPT_SOURCE))
 			shostnetworkmask = "0.0.0.0/0";
 	}
 
 	if (shostnetworkmask)
-		parse_hostnetworkmask(shostnetworkmask, &saddrs,
-				      &(cs.arp.arp.smsk), &nsaddrs);
+		xtables_ipparse_multiple(shostnetworkmask, &saddrs,
+					 &smasks, &nsaddrs);
 
 	if (dhostnetworkmask)
-		parse_hostnetworkmask(dhostnetworkmask, &daddrs,
-				      &(cs.arp.arp.tmsk), &ndaddrs);
+		xtables_ipparse_multiple(dhostnetworkmask, &daddrs,
+					 &dmasks, &ndaddrs);
 
 	if ((nsaddrs > 1 || ndaddrs > 1) &&
 	    (cs.arp.arp.invflags & (ARPT_INV_SRCIP | ARPT_INV_TGTIP)))
@@ -1308,8 +918,6 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 		xtables_error(PARAMETER_PROBLEM, "Replacement rule does not "
 						 "specify a unique address");
 
-	generic_opt_check(command, options);
-
 	if (chain && strlen(chain) > ARPT_FUNCTION_MAXNAMELEN)
 		xtables_error(PARAMETER_PROBLEM,
 				"chain name `%s' too long (must be under %i chars)",
@@ -1343,12 +951,14 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 	switch (command) {
 	case CMD_APPEND:
 		ret = append_entry(h, chain, *table, &cs, 0,
-				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
 				   options&OPT_VERBOSE, true);
 		break;
 	case CMD_DELETE:
 		ret = delete_entry(chain, *table, &cs,
-				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
 				   options&OPT_VERBOSE, h);
 		break;
 	case CMD_DELETE_NUM:
@@ -1356,11 +966,13 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 		break;
 	case CMD_REPLACE:
 		ret = replace_entry(chain, *table, &cs, rulenum - 1,
-				    saddrs, daddrs, options&OPT_VERBOSE, h);
+				    saddrs, smasks, daddrs, dmasks,
+				    options&OPT_VERBOSE, h);
 		break;
 	case CMD_INSERT:
 		ret = append_entry(h, chain, *table, &cs, rulenum - 1,
-				   nsaddrs, saddrs, ndaddrs, daddrs,
+				   nsaddrs, saddrs, smasks,
+				   ndaddrs, daddrs, dmasks,
 				   options&OPT_VERBOSE, false);
 		break;
 	case CMD_LIST:
@@ -1409,10 +1021,10 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
 		exit_tryhelp(2);
 	}
 
-	if (nsaddrs)
-		free(saddrs);
-	if (ndaddrs)
-		free(daddrs);
+	free(saddrs);
+	free(smasks);
+	free(daddrs);
+	free(dmasks);
 
 	if (cs.target)
 		free(cs.target->t);
diff --git a/iptables/xtables-config-parser.y b/iptables/xtables-config-parser.y
deleted file mode 100644
index 89bfee739452652249ea7c518839a512bf7fb4ef..0000000000000000000000000000000000000000
--- a/iptables/xtables-config-parser.y
+++ /dev/null
@@ -1,248 +0,0 @@
-%{
-/*
- * (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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libiptc/linux_list.h>
-#include <libnftnl/table.h>
-#include <libnftnl/chain.h>
-
-#include <netinet/in.h>
-#include <linux/netfilter.h>
-
-extern char *yytext;
-extern int yylineno;
-
-static LIST_HEAD(xtables_stack);
-
-struct stack_elem {
-	struct list_head	head;
-	int			token;
-	size_t			size;
-	char			data[];
-};
-
-static void *stack_push(int token, size_t size)
-{
-	struct stack_elem *e;
-
-	e = calloc(1, sizeof(struct stack_elem) + size);
-
-	e->token = token;
-	e->size = size;
-
-	list_add(&e->head, &xtables_stack);
-
-	return e->data;
-}
-
-static struct stack_elem *stack_pop(void)
-{
-	struct stack_elem *e;
-
-	e = list_entry(xtables_stack.next, struct stack_elem, head);
-
-	if (&e->head == &xtables_stack)
-		return NULL;
-
-	list_del(&e->head);
-	return e;
-}
-
-static inline void stack_put_i32(void *data, int value)
-{
-	memcpy(data, &value, sizeof(int));
-}
-
-static inline void stack_put_str(void *data, const char *str)
-{
-	memcpy(data, str, strlen(str));
-}
-
-static void stack_free(struct stack_elem *e)
-{
-	free(e);
-}
-
-%}
-
-%union {
-	int	val;
-	char	*string;
-}
-
-%token T_FAMILY
-%token T_TABLE
-%token T_CHAIN
-%token T_HOOK
-%token T_PRIO
-
-%token <string> T_STRING
-%token <val>	T_INTEGER
-
-%%
-
-configfile	:
-		| lines
-		;
-
-lines		: line
-		| lines line
-		;
-
-line		: family
-		;
-
-family		: T_FAMILY T_STRING '{' tables '}'
-		{
-			void *data = stack_push(T_FAMILY, strlen($2)+1);
-			stack_put_str(data, $2);
-		}
-		;
-
-tables		: table
-		| tables table
-		;
-
-table		: T_TABLE T_STRING '{' chains '}'
-		{
-			/* added in reverse order to pop it in order */
-			void *data = stack_push(T_TABLE, strlen($2)+1);
-			stack_put_str(data, $2);
-		}
-		;
-
-chains		: chain
-		| chains chain
-		;
-
-chain		: T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER
-		{
-			/* added in reverse order to pop it in order */
-			void *data = stack_push(T_PRIO, sizeof(int32_t));
-			stack_put_i32(data, $6);
-			data = stack_push(T_HOOK, strlen($4)+1);
-			stack_put_str(data, $4);
-			data = stack_push(T_CHAIN, strlen($2)+1);
-			stack_put_str(data, $2);
-		}
-		;
-
-%%
-
-int __attribute__((noreturn))
-yyerror(char *msg)
-{
-	fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n",
-			 yylineno, yytext, msg);
-	exit(EXIT_FAILURE);
-}
-
-static int hooknametonum(const char *hookname)
-{
-	if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0)
-		return NF_INET_LOCAL_IN;
-	else if (strcmp(hookname, "NF_INET_FORWARD") == 0)
-		return NF_INET_FORWARD;
-	else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0)
-		return NF_INET_LOCAL_OUT;
-	else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0)
-		return NF_INET_PRE_ROUTING;
-	else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0)
-		return NF_INET_POST_ROUTING;
-
-	return -1;
-}
-
-static int32_t familytonumber(const char *family)
-{
-	if (strcmp(family, "ipv4") == 0)
-		return AF_INET;
-	else if (strcmp(family, "ipv6") == 0)
-		return AF_INET6;
-
-	return -1;
-}
-
-int xtables_config_parse(char *filename, struct nftnl_table_list *table_list,
-			 struct nftnl_chain_list *chain_list)
-{
-	FILE *fp;
-	struct stack_elem *e;
-	struct nftnl_table *table = NULL;
-	struct nftnl_chain *chain = NULL;
-	int prio = 0;
-	int32_t family = 0;
-
-	fp = fopen(filename, "r");
-	if (!fp)
-		return -1;
-
-	yyrestart(fp);
-	yyparse();
-	fclose(fp);
-
-	for (e = stack_pop(); e != NULL; e = stack_pop()) {
-		switch(e->token) {
-		case T_FAMILY:
-			family = familytonumber(e->data);
-			if (family == -1)
-				return -1;
-			break;
-		case T_TABLE:
-			table = nftnl_table_alloc();
-			if (table == NULL)
-				return -1;
-
-			nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
-			nftnl_table_set(table, NFTNL_TABLE_NAME, e->data);
-			/* This is intentionally prepending, instead of
-			 * appending, since the elements in the stack are in
-			 * the reverse order that chains appear in the
-			 * configuration file.
-			 */
-			nftnl_table_list_add(table, table_list);
-			break;
-		case T_PRIO:
-			memcpy(&prio, e->data, sizeof(int32_t));
-			break;
-		case T_CHAIN:
-			chain = nftnl_chain_alloc();
-			if (chain == NULL)
-				return -1;
-
-			nftnl_chain_set(chain, NFTNL_CHAIN_TABLE,
-				(char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
-			nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY,
-				nftnl_table_get_u32(table, NFTNL_TABLE_FAMILY));
-			nftnl_chain_set_s32(chain, NFTNL_CHAIN_PRIO, prio);
-			nftnl_chain_set(chain, NFTNL_CHAIN_NAME, e->data);
-			/* Intentionally prepending, instead of appending */
-			nftnl_chain_list_add(chain, chain_list);
-			break;
-		case T_HOOK:
-			nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM,
-						hooknametonum(e->data));
-			break;
-		default:
-			printf("unknown token type %d\n", e->token);
-			break;
-		}
-		stack_free(e);
-	}
-
-	return 0;
-}
diff --git a/iptables/xtables-config-syntax.l b/iptables/xtables-config-syntax.l
deleted file mode 100644
index a895c8bce8e3e8daae24f6dad59a09dd99a4ad79..0000000000000000000000000000000000000000
--- a/iptables/xtables-config-syntax.l
+++ /dev/null
@@ -1,54 +0,0 @@
-%{
-/*
- * (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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
- */
-
-#include <string.h>
-#include "xtables-config-parser.h"
-%}
-
-%option yylineno
-%option noinput
-%option nounput
-
-ws		[ \t]+
-comment         #.*$
-nl		[\n\r]
-
-is_on		[o|O][n|N]
-is_off		[o|O][f|F][f|F]
-integer		[\-\+]?[0-9]+
-string		[a-zA-Z][a-zA-Z0-9\.\-\_]*
-
-%%
-"family"		{ return T_FAMILY; }
-"table"			{ return T_TABLE; }
-"chain"			{ return T_CHAIN; }
-"hook"			{ return T_HOOK; }
-"prio"			{ return T_PRIO; }
-
-{integer}		{ yylval.val = atoi(yytext); return T_INTEGER; }
-{string}		{ yylval.string = strdup(yytext); return T_STRING; }
-
-{comment}	;
-{ws}		;
-{nl}		;
-
-<<EOF>>		{ yyterminate(); }
-
-.		{ return yytext[0]; }
-
-%%
-
-int
-yywrap()
-{
-	return 1;
-}
diff --git a/iptables/xtables-eb-standalone.c b/iptables/xtables-eb-standalone.c
index fb3daba0bd604008b56f04f61952eaa7eaa4005c..a9081c78c3bc36943e66424d9b4771ae7a392228 100644
--- a/iptables/xtables-eb-standalone.c
+++ b/iptables/xtables-eb-standalone.c
@@ -51,7 +51,7 @@ int xtables_eb_main(int argc, char *argv[])
 
 	ret = do_commandeb(&h, argc, argv, &table, false);
 	if (ret)
-		ret = nft_commit(&h);
+		ret = nft_bridge_commit(&h);
 
 	if (!ret)
 		fprintf(stderr, "ebtables: %s\n", nft_strerror(errno));
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
index bc71e12226fdc91fc2c4e143002a2559c3cb5a8d..15b971da3d425c293fe786e73b70cf70af7bcf27 100644
--- a/iptables/xtables-eb.c
+++ b/iptables/xtables-eb.c
@@ -20,7 +20,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
+#include "config.h"
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
@@ -197,7 +197,8 @@ int ebt_get_current_chain(const char *chain)
 	else if (strcmp(chain, "POSTROUTING") == 0)
 		return NF_BR_POST_ROUTING;
 
-	return -1;
+	/* placeholder for user defined chain */
+	return NF_BR_NUMHOOKS;
 }
 
 /*
@@ -273,7 +274,7 @@ struct option ebt_original_options[] =
 extern void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
 struct xtables_globals ebtables_globals = {
 	.option_offset 		= 0,
-	.program_version	= IPTABLES_VERSION,
+	.program_version	= PACKAGE_VERSION,
 	.orig_opts		= ebt_original_options,
 	.exit_err		= xtables_exit_error,
 	.compat_rev		= nft_compatible_revision,
@@ -410,7 +411,7 @@ static int list_rules(struct nft_handle *h, const char *chain, const char *table
 {
 	unsigned int format;
 
-	format = FMT_OPTIONS;
+	format = FMT_OPTIONS | FMT_C_COUNTS;
 	if (verbose)
 		format |= FMT_VIA;
 
@@ -593,6 +594,7 @@ void ebt_load_match_extensions(void)
 	ebt_load_match("pkttype");
 	ebt_load_match("vlan");
 	ebt_load_match("stp");
+	ebt_load_match("among");
 
 	ebt_load_watcher("log");
 	ebt_load_watcher("nflog");
@@ -779,6 +781,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
 	int selected_chain = -1;
 	struct xtables_rule_match *xtrm_i;
 	struct ebt_match *match;
+	bool table_set = false;
 
 	/* prevent getopt to spoil our error reporting */
 	optind = 0;
@@ -946,11 +949,16 @@ print_zero:
 			break;
 		case 't': /* Table */
 			ebt_check_option2(&flags, OPT_TABLE);
+			if (restore && table_set)
+				xtables_error(PARAMETER_PROBLEM,
+					      "The -t option (seen in line %u) cannot be used in %s.\n",
+					      line, xt_params->program_name);
 			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
 				xtables_error(PARAMETER_PROBLEM,
 					      "Table name length cannot exceed %d characters",
 					      EBT_TABLE_MAXNAMELEN - 1);
 			*table = optarg;
+			table_set = true;
 			break;
 		case 'i': /* Input interface */
 		case 2  : /* Logical input interface */
@@ -1180,7 +1188,7 @@ print_zero:
 			if (ebt_command_default(&cs))
 				xtables_error(PARAMETER_PROBLEM,
 					      "Unknown argument: '%s'",
-					      argv[optind - 1]);
+					      argv[optind]);
 
 			if (command != 'A' && command != 'I' &&
 			    command != 'D' && command != 'C')
@@ -1223,7 +1231,7 @@ print_zero:
 	cs.eb.ethproto = htons(cs.eb.ethproto);
 
 	if (command == 'P') {
-		if (selected_chain < 0) {
+		if (selected_chain >= NF_BR_NUMHOOKS) {
 			ret = ebt_set_user_chain_policy(h, *table, chain, policy);
 		} else {
 			if (strcmp(policy, "RETURN") == 0) {
diff --git a/iptables/xtables-monitor.c b/iptables/xtables-monitor.c
index f835c5e503e0f32e9d8df840f69371a66f9fbd72..a5245d1422af93ec8a808c78c64a24de7acd54b1 100644
--- a/iptables/xtables-monitor.c
+++ b/iptables/xtables-monitor.c
@@ -10,6 +10,8 @@
  */
 
 #define _GNU_SOURCE
+#include "config.h"
+#include <errno.h>
 #include <stdlib.h>
 #include <time.h>
 #include <string.h>
@@ -40,6 +42,7 @@
 struct cb_arg {
 	uint32_t nfproto;
 	bool is_event;
+	struct nft_handle *h;
 };
 
 static int table_cb(const struct nlmsghdr *nlh, void *data)
@@ -105,7 +108,7 @@ static int rule_cb(const struct nlmsghdr *nlh, void *data)
 	}
 
 	printf("-t %s ", nftnl_rule_get_str(r, NFTNL_RULE_TABLE));
-	nft_rule_print_save(r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
+	nft_rule_print_save(arg->h, r, type == NFT_MSG_NEWRULE ? NFT_RULE_APPEND :
 							   NFT_RULE_DEL,
 			    counters ? 0 : FMT_NOCOUNTS);
 err_free:
@@ -592,7 +595,10 @@ int xtables_monitor_main(int argc, char *argv[])
 	struct mnl_socket *nl;
 	char buf[MNL_SOCKET_BUFFER_SIZE];
 	uint32_t nfgroup = 0;
-	struct cb_arg cb_arg = {};
+	struct nft_handle h = {};
+	struct cb_arg cb_arg = {
+		.h = &h,
+	};
 	int ret, c;
 
 	xtables_globals.program_name = "xtables-monitor";
@@ -609,6 +615,14 @@ int xtables_monitor_main(int argc, char *argv[])
 	init_extensions4();
 #endif
 
+	if (nft_init(&h, xtables_ipv4)) {
+		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
+			xtables_globals.program_name,
+			xtables_globals.program_version,
+			strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
 	opterr = 0;
 	while ((c = getopt_long(argc, argv, "ceht46V", options, NULL)) != -1) {
 		switch (c) {
@@ -631,10 +645,10 @@ int xtables_monitor_main(int argc, char *argv[])
 			cb_arg.nfproto = NFPROTO_IPV6;
 			break;
 		case 'V':
-			printf("xtables-monitor %s\n", IPTABLES_VERSION);
+			printf("xtables-monitor %s\n", PACKAGE_VERSION);
 			exit(0);
 		default:
-			fprintf(stderr, "xtables-monitor %s: Bad argument.\n", IPTABLES_VERSION);
+			fprintf(stderr, "xtables-monitor %s: Bad argument.\n", PACKAGE_VERSION);
 			fprintf(stderr, "Try `xtables-monitor -h' for more information.\n");
 			exit(PARAMETER_PROBLEM);
 		}
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
index a6a331d398687959e3e65f2742da5861d826336f..2f0fe7d439d940dbd61045cf475bf0e314812bbc 100644
--- a/iptables/xtables-restore.c
+++ b/iptables/xtables-restore.c
@@ -4,9 +4,10 @@
  *
  * This code is distributed under the terms of GNU GPL v2
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <errno.h>
+#include <libgen.h>
 #include <stdbool.h>
 #include <string.h>
 #include <stdio.h>
@@ -17,6 +18,7 @@
 #include "xtables-multi.h"
 #include "nft.h"
 #include "nft-bridge.h"
+#include "nft-cache.h"
 #include <libnftnl/chain.h>
 
 static int counters, verbose;
@@ -43,7 +45,7 @@ static const struct option options[] = {
 
 static void print_usage(const char *name, const char *version)
 {
-	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6]\n"
+	fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6] [file]\n"
 			"	   [ --counters ]\n"
 			"	   [ --verbose ]\n"
 			"	   [ --version]\n"
@@ -51,25 +53,12 @@ static void print_usage(const char *name, const char *version)
 			"	   [ --help ]\n"
 			"	   [ --noflush ]\n"
 			"	   [ --table=<TABLE> ]\n"
-			"          [ --modprobe=<command> ]\n"
+			"	   [ --modprobe=<command> ]\n"
 			"	   [ --ipv4 ]\n"
 			"	   [ --ipv6 ]\n", name);
 }
 
-static struct nftnl_chain_list *get_chain_list(struct nft_handle *h,
-					       const char *table)
-{
-	struct nftnl_chain_list *chain_list;
-
-	chain_list = nft_chain_list_get(h, table);
-	if (chain_list == NULL)
-		xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
-
-	return chain_list;
-}
-
-struct nft_xt_restore_cb restore_cb = {
-	.chain_list	= get_chain_list,
+static const struct nft_xt_restore_cb restore_cb = {
 	.commit		= nft_commit,
 	.abort		= nft_abort,
 	.table_new	= nft_table_new,
@@ -79,241 +68,285 @@ struct nft_xt_restore_cb restore_cb = {
 	.chain_restore  = nft_chain_restore,
 };
 
-static const struct xtc_ops xtc_ops = {
-	.strerror	= nft_strerror,
+struct nft_xt_restore_state {
+	const struct builtin_table *curtable;
+	struct argv_store av_store;
+	bool in_table;
 };
 
-void xtables_restore_parse(struct nft_handle *h,
-			   struct nft_xt_restore_parse *p,
-			   struct nft_xt_restore_cb *cb,
-			   int argc, char *argv[])
+static void xtables_restore_parse_line(struct nft_handle *h,
+				       const struct nft_xt_restore_parse *p,
+				       struct nft_xt_restore_state *state,
+				       char *buffer)
 {
-	const struct builtin_table *curtable = NULL;
-	char buffer[10240];
-	int in_table = 0;
-	const struct xtc_ops *ops = &xtc_ops;
+	const struct nft_xt_restore_cb *cb = p->cb;
+	int ret = 0;
+
+	if (buffer[0] == '\n')
+		return;
+	else if (buffer[0] == '#') {
+		if (verbose)
+			fputs(buffer, stdout);
+		return;
+	} else if (state->in_table &&
+		   (strncmp(buffer, "COMMIT", 6) == 0) &&
+		   (buffer[6] == '\0' || buffer[6] == '\n')) {
+		if (!p->testing) {
+			/* Commit per table, although we support
+			 * global commit at once, stick by now to
+			 * the existing behaviour.
+			 */
+			DEBUGP("Calling commit\n");
+			if (cb->commit)
+				ret = cb->commit(h);
+		} else {
+			DEBUGP("Not calling commit, testing\n");
+			if (cb->abort)
+				ret = cb->abort(h);
+		}
+		state->in_table = false;
+
+	} else if ((buffer[0] == '*') && (!state->in_table || !p->commit)) {
+		/* 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);
+
+		state->curtable = nft_table_builtin_find(h, table);
+		if (!state->curtable)
+			xtables_error(PARAMETER_PROBLEM,
+				"%s: line %u table name '%s' invalid\n",
+				xt_params->program_name, line, table);
+
+		if (p->tablename && (strcmp(p->tablename, table) != 0))
+			return;
+
+		if (h->noflush == 0) {
+			DEBUGP("Cleaning all chains of table '%s'\n", table);
+			if (cb->table_flush)
+				cb->table_flush(h, table);
+		}
 
-	line = 0;
+		ret = 1;
+		state->in_table = true;
+
+		if (cb->table_new)
+			cb->table_new(h, table);
+
+	} else if ((buffer[0] == ':') && state->in_table) {
+		/* New chain. */
+		char *policy, *chain = NULL;
+		struct xt_counters count = {};
+
+		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);
+
+		if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+			xtables_error(PARAMETER_PROBLEM,
+				   "Invalid chain name `%s' (%u chars max)",
+				   chain, XT_EXTENSION_MAXNAMELEN - 1);
+
+		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);
+
+		if (nft_chain_builtin_find(state->curtable, chain)) {
+			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);
 
-	/* Grab standard input. */
-	while (fgets(buffer, sizeof(buffer), p->in)) {
-		int ret = 0;
-
-		line++;
-		h->error.lineno = 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 (!p->testing) {
-				/* Commit per table, although we support
-				 * global commit at once, stick by now to
-				 * the existing behaviour.
-				 */
-				DEBUGP("Calling commit\n");
-				if (cb->commit)
-					ret = cb->commit(h);
-			} else {
-				DEBUGP("Not calling commit, testing\n");
-				if (cb->abort)
-					ret = cb->abort(h);
 			}
-			in_table = 0;
-
-		} else if ((buffer[0] == '*') && (!in_table || !p->commit)) {
-			/* 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);
-			}
-			curtable = nft_table_builtin_find(h, table);
-			if (!curtable)
-				xtables_error(PARAMETER_PROBLEM,
-					"%s: line %u table name '%s' invalid\n",
-					xt_params->program_name, line, table);
-
-			if (p->tablename && (strcmp(p->tablename, table) != 0))
-				continue;
-
-			nft_build_cache(h);
-
-			if (h->noflush == 0) {
-				DEBUGP("Cleaning all chains of table '%s'\n",
-					table);
-				if (cb->table_flush)
-					cb->table_flush(h, table);
+			if (cb->chain_set &&
+			    cb->chain_set(h, state->curtable->name,
+					  chain, policy, &count) < 0) {
+				xtables_error(OTHER_PROBLEM,
+					      "Can't set policy `%s' on `%s' line %u: %s\n",
+					      policy, chain, line,
+					      strerror(errno));
 			}
+			DEBUGP("Setting policy of chain %s to %s\n",
+			       chain, policy);
+		} else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
+			   errno != EEXIST) {
+			xtables_error(PARAMETER_PROBLEM,
+				      "cannot create chain '%s' (%s)\n",
+				      chain, strerror(errno));
+		} else if (h->family == NFPROTO_BRIDGE &&
+			   !ebt_set_user_chain_policy(h, state->curtable->name,
+						      chain, policy)) {
+			xtables_error(OTHER_PROBLEM,
+				      "Can't set policy `%s' on `%s' line %u: %s\n",
+				      policy, chain, line,
+				      strerror(errno));
+		}
+		ret = 1;
+	} else if (state->in_table) {
+		char *pcnt = NULL;
+		char *bcnt = NULL;
+		char *parsestart = buffer;
+
+		add_argv(&state->av_store, xt_params->program_name, 0);
+		add_argv(&state->av_store, "-t", 0);
+		add_argv(&state->av_store, state->curtable->name, 0);
+
+		tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
+		if (counters && pcnt && bcnt) {
+			add_argv(&state->av_store, "--set-counters", 0);
+			add_argv(&state->av_store, pcnt, 0);
+			add_argv(&state->av_store, bcnt, 0);
+		}
 
-			ret = 1;
-			in_table = 1;
-
-			if (cb->table_new)
-				cb->table_new(h, table);
+		add_param_to_argv(&state->av_store, parsestart, line);
 
-		} else if ((buffer[0] == ':') && (in_table)) {
-			/* New chain. */
-			char *policy, *chain = NULL;
-			struct xt_counters count = {};
+		DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
+		       state->av_store.argc, state->curtable->name);
+		debug_print_argv(&state->av_store);
 
-			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);
-			}
+		ret = cb->do_command(h, state->av_store.argc,
+				     state->av_store.argv,
+				     &state->av_store.argv[2], true);
+		if (ret < 0) {
+			if (cb->abort)
+				ret = cb->abort(h);
+			else
+				ret = 0;
 
-			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
-				xtables_error(PARAMETER_PROBLEM,
-					   "Invalid chain name `%s' "
-					   "(%u chars max)",
-					   chain, XT_EXTENSION_MAXNAMELEN - 1);
-
-			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 (ret < 0) {
+				fprintf(stderr,
+					"failed to abort commit operation\n");
 			}
+			exit(1);
+		}
 
-			if (nft_chain_builtin_find(curtable, chain)) {
-				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);
-
-				}
-				if (cb->chain_set &&
-				    cb->chain_set(h, curtable->name,
-					          chain, policy, &count) < 0) {
-					xtables_error(OTHER_PROBLEM,
-						      "Can't set policy `%s'"
-						      " on `%s' line %u: %s\n",
-						      policy, chain, line,
-						      ops->strerror(errno));
-				}
-				DEBUGP("Setting policy of chain %s to %s\n",
-				       chain, policy);
-			} else if (cb->chain_restore(h, chain, curtable->name) < 0 &&
-				   errno != EEXIST) {
-				xtables_error(PARAMETER_PROBLEM,
-					      "cannot create chain "
-					      "'%s' (%s)\n", chain,
-					      strerror(errno));
-			} else if (h->family == NFPROTO_BRIDGE &&
-				   !ebt_set_user_chain_policy(h, curtable->name,
-							      chain, policy)) {
-				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 *pcnt = NULL;
-			char *bcnt = NULL;
-			char *parsestart;
+		free_argv(&state->av_store);
+		fflush(stdout);
+	}
+	if (p->tablename && state->curtable &&
+	    (strcmp(p->tablename, state->curtable->name) != 0))
+		return;
+	if (!ret) {
+		fprintf(stderr, "%s: line %u failed\n",
+				xt_params->program_name, line);
+		exit(1);
+	}
+}
 
-			/* reset the newargv */
-			newargc = 0;
+/* Return true if given iptables-restore line will require a full cache.
+ * Typically these are commands referring to an existing rule
+ * (either by number or content) or commands listing the ruleset. */
+static bool cmd_needs_full_cache(char *cmd)
+{
+	char c, chain[32];
+	int rulenum, mcount;
+
+	mcount = sscanf(cmd, "-%c %31s %d", &c, chain, &rulenum);
+
+	if (mcount == 3)
+		return true;
+	if (mcount < 1)
+		return false;
+
+	switch (c) {
+	case 'D':
+	case 'C':
+	case 'S':
+	case 'L':
+	case 'Z':
+		return true;
+	}
 
-			if (buffer[0] == '[') {
-				/* we have counters in our input */
-				char *ptr = strchr(buffer, ']');
+	return false;
+}
 
-				if (!ptr)
-					xtables_error(PARAMETER_PROBLEM,
-						   "Bad line %u: need ]\n",
-						   line);
+#define PREBUFSIZ	65536
 
-				pcnt = strtok(buffer+1, ":");
-				if (!pcnt)
-					xtables_error(PARAMETER_PROBLEM,
-						   "Bad line %u: need :\n",
-						   line);
+void xtables_restore_parse(struct nft_handle *h,
+			   const struct nft_xt_restore_parse *p)
+{
+	struct nft_xt_restore_state state = {};
+	char preload_buffer[PREBUFSIZ] = {}, buffer[10240], *ptr;
 
-				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;
+	if (!h->noflush) {
+		nft_fake_cache(h);
+	} else {
+		ssize_t pblen = sizeof(preload_buffer);
+		bool do_cache = false;
+
+		ptr = preload_buffer;
+		while (fgets(buffer, sizeof(buffer), p->in)) {
+			size_t blen = strlen(buffer);
+
+			/* drop trailing newline; xtables_restore_parse_line()
+			 * uses strtok() which replaces them by nul-characters,
+			 * causing unpredictable string delimiting in
+			 * preload_buffer */
+			if (buffer[blen - 1] == '\n')
+				buffer[blen - 1] = '\0';
+			else
+				blen++;
+
+			pblen -= blen;
+			if (pblen <= 0) {
+				/* buffer exhausted */
+				do_cache = true;
+				break;
 			}
 
-			add_argv(argv[0], 0);
-			add_argv("-t", 0);
-			add_argv(curtable->name, 0);
-
-			if (counters && pcnt && bcnt) {
-				add_argv("--set-counters", 0);
-				add_argv((char *) pcnt, 0);
-				add_argv((char *) bcnt, 0);
+			if (cmd_needs_full_cache(buffer)) {
+				do_cache = true;
+				break;
 			}
 
-			add_param_to_argv(parsestart, line);
-
-			DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
-				newargc, curtable->name);
-
-			for (a = 0; a < newargc; a++)
-				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+			/* copy string including terminating nul-char */
+			memcpy(ptr, buffer, blen);
+			ptr += blen;
+			buffer[0] = '\0';
+		}
 
-			ret = cb->do_command(h, newargc, newargv,
-					    &newargv[2], true);
-			if (ret < 0) {
-				if (cb->abort)
-					ret = cb->abort(h);
-				else
-					ret = 0;
-
-				if (ret < 0) {
-					fprintf(stderr, "failed to abort "
-							"commit operation\n");
-				}
-				exit(1);
-			}
+		if (do_cache)
+			nft_build_cache(h, NULL);
+	}
 
-			free_argv();
-			fflush(stdout);
-		}
-		if (p->tablename && curtable &&
-		    (strcmp(p->tablename, curtable->name) != 0))
-			continue;
-		if (!ret) {
-			fprintf(stderr, "%s: line %u failed\n",
-					xt_params->program_name, line);
-			exit(1);
-		}
+	line = 0;
+	ptr = preload_buffer;
+	while (*ptr) {
+		h->error.lineno = ++line;
+		DEBUGP("%s: buffered line %d: '%s'\n", __func__, line, ptr);
+		xtables_restore_parse_line(h, p, &state, ptr);
+		ptr += strlen(ptr) + 1;
+	}
+	if (*buffer) {
+		h->error.lineno = ++line;
+		DEBUGP("%s: overrun line %d: '%s'\n", __func__, line, buffer);
+		xtables_restore_parse_line(h, p, &state, buffer);
 	}
-	if (in_table && p->commit) {
+	while (fgets(buffer, sizeof(buffer), p->in)) {
+		h->error.lineno = ++line;
+		DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
+		xtables_restore_parse_line(h, p, &state, buffer);
+	}
+	if (state.in_table && p->commit) {
 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
 				xt_params->program_name, line + 1);
 		exit(1);
-	} else if (in_table && cb->commit && !cb->commit(h)) {
+	} else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
 		xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
 			      xt_params->program_name);
 	}
@@ -330,6 +363,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
 	int c;
 	struct nft_xt_restore_parse p = {
 		.commit = true,
+		.cb = &restore_cb,
 	};
 
 	line = 0;
@@ -361,8 +395,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
 				p.testing = 1;
 				break;
 			case 'h':
-				print_usage("xtables-restore",
-					    IPTABLES_VERSION);
+				print_usage(prog_name, PACKAGE_VERSION);
 				exit(0);
 			case 'n':
 				h.noflush = 1;
@@ -387,7 +420,8 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
 				break;
 			default:
 				fprintf(stderr,
-					"Try `xtables-restore -h' for more information.\n");
+					"Try `%s -h' for more information.\n",
+					prog_name);
 				exit(1);
 		}
 	}
@@ -434,7 +468,7 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
-	xtables_restore_parse(&h, &p, &restore_cb, argc, argv);
+	xtables_restore_parse(&h, &p);
 
 	nft_fini(&h);
 	fclose(p.in);
@@ -443,13 +477,13 @@ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
 
 int xtables_ip4_restore_main(int argc, char *argv[])
 {
-	return xtables_restore_main(NFPROTO_IPV4, "iptables-restore",
+	return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
 				    argc, argv);
 }
 
 int xtables_ip6_restore_main(int argc, char *argv[])
 {
-	return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore",
+	return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
 				    argc, argv);
 }
 
@@ -460,9 +494,8 @@ static int ebt_table_flush(struct nft_handle *h, const char *table)
 	return nft_table_flush(h, table);
 }
 
-struct nft_xt_restore_cb ebt_restore_cb = {
-	.chain_list	= get_chain_list,
-	.commit		= nft_commit,
+static const struct nft_xt_restore_cb ebt_restore_cb = {
+	.commit		= nft_bridge_commit,
 	.table_new	= nft_table_new,
 	.table_flush	= ebt_table_flush,
 	.do_command	= do_commandeb,
@@ -479,6 +512,7 @@ int xtables_eb_restore_main(int argc, char *argv[])
 {
 	struct nft_xt_restore_parse p = {
 		.in = stdin,
+		.cb = &ebt_restore_cb,
 	};
 	bool noflush = false;
 	struct nft_handle h;
@@ -500,14 +534,13 @@ int xtables_eb_restore_main(int argc, char *argv[])
 
 	nft_init_eb(&h, "ebtables-restore");
 	h.noflush = noflush;
-	xtables_restore_parse(&h, &p, &ebt_restore_cb, argc, argv);
+	xtables_restore_parse(&h, &p);
 	nft_fini(&h);
 
 	return 0;
 }
 
-struct nft_xt_restore_cb arp_restore_cb = {
-	.chain_list	= get_chain_list,
+static const struct nft_xt_restore_cb arp_restore_cb = {
 	.commit		= nft_commit,
 	.table_new	= nft_table_new,
 	.table_flush	= nft_table_flush,
@@ -520,11 +553,12 @@ int xtables_arp_restore_main(int argc, char *argv[])
 {
 	struct nft_xt_restore_parse p = {
 		.in = stdin,
+		.cb = &arp_restore_cb,
 	};
 	struct nft_handle h;
 
 	nft_init_arp(&h, "arptables-restore");
-	xtables_restore_parse(&h, &p, &arp_restore_cb, argc, argv);
+	xtables_restore_parse(&h, &p);
 	nft_fini(&h);
 
 	return 0;
diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
index 2cc5a7c7540beebb9e9d774b043b1b28f140ac45..3a52f8c3d820991383e905ee90855f07c0c5cd62 100644
--- a/iptables/xtables-save.c
+++ b/iptables/xtables-save.c
@@ -6,8 +6,10 @@
  * This code is distributed under the terms of GNU GPL v2
  *
  */
+#include "config.h"
 #include <getopt.h>
 #include <errno.h>
+#include <libgen.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -19,6 +21,7 @@
 #include "iptables.h"
 #include "xtables-multi.h"
 #include "nft.h"
+#include "nft-cache.h"
 
 #include <libnftnl/chain.h>
 
@@ -29,9 +32,8 @@
 #define prog_name xtables_globals.program_name
 #define prog_vers xtables_globals.program_version
 
-static bool show_counters = false;
-
-static const struct option options[] = {
+static const char *ipt_save_optstring = "bcdt:M:f:46V";
+static const struct option ipt_save_options[] = {
 	{.name = "counters", .has_arg = false, .val = 'c'},
 	{.name = "version",  .has_arg = false, .val = 'V'},
 	{.name = "dump",     .has_arg = false, .val = 'd'},
@@ -43,6 +45,7 @@ static const struct option options[] = {
 	{NULL},
 };
 
+static const char *arp_save_optstring = "cM:V";
 static const struct option arp_save_options[] = {
 	{.name = "counters", .has_arg = false, .val = 'c'},
 	{.name = "version",  .has_arg = false, .val = 'V'},
@@ -50,6 +53,7 @@ static const struct option arp_save_options[] = {
 	{NULL},
 };
 
+static const char *ebt_save_optstring = "ct:M:V";
 static const struct option ebt_save_options[] = {
 	{.name = "counters", .has_arg = false, .val = 'c'},
 	{.name = "version",  .has_arg = false, .val = 'V'},
@@ -58,49 +62,55 @@ static const struct option ebt_save_options[] = {
 	{NULL},
 };
 
-static bool ebt_legacy_counter_format;
+struct do_output_data {
+	unsigned int format;
+	bool commit;
+};
 
 static int
-__do_output(struct nft_handle *h, const char *tablename, bool counters)
+__do_output(struct nft_handle *h, const char *tablename, void *data)
 {
 	struct nftnl_chain_list *chain_list;
+	struct do_output_data *d = data;
+	time_t now;
 
+	if (!nft_table_builtin_find(h, tablename))
+		return 0;
 
-	if (!nft_is_table_compatible(h, tablename)) {
-		if (!nft_table_builtin_find(h, tablename))
-			printf("# Table `%s' is incompatible, use 'nft' tool.\n",
-			       tablename);
+	if (!nft_is_table_compatible(h, tablename, NULL)) {
+		printf("# Table `%s' is incompatible, use 'nft' tool.\n",
+		       tablename);
 		return 0;
 	}
 
-	chain_list = nft_chain_list_get(h, tablename);
+	chain_list = nft_chain_list_get(h, tablename, NULL);
 	if (!chain_list)
 		return 0;
 
-	time_t now = time(NULL);
+	now = time(NULL);
+	printf("# Generated by %s v%s on %s", prog_name,
+	       prog_vers, ctime(&now));
 
-	printf("# Generated by xtables-save v%s on %s",
-	       IPTABLES_VERSION, ctime(&now));
 	printf("*%s\n", tablename);
-
 	/* Dump out chain names first,
 	 * thereby preventing dependency conflicts */
 	nft_chain_save(h, chain_list);
-	nft_rule_save(h, tablename, counters ? 0 : FMT_NOCOUNTS);
+	nft_rule_save(h, tablename, d->format);
+	if (d->commit)
+		printf("COMMIT\n");
 
 	now = time(NULL);
-	printf("COMMIT\n");
 	printf("# Completed on %s", ctime(&now));
 	return 0;
 }
 
 static int
-do_output(struct nft_handle *h, const char *tablename, bool counters)
+do_output(struct nft_handle *h, const char *tablename, struct do_output_data *d)
 {
 	int ret;
 
 	if (!tablename) {
-		ret = nft_for_each_table(h, __do_output, counters);
+		ret = nft_for_each_table(h, __do_output, d);
 		nft_check_xt_legacy(h->family, true);
 		return !!ret;
 	}
@@ -111,7 +121,7 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
 		return 1;
 	}
 
-	ret = __do_output(h, tablename, counters);
+	ret = __do_output(h, tablename, d);
 	nft_check_xt_legacy(h->family, true);
 	return ret;
 }
@@ -121,10 +131,14 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
  * rule
  */
 static int
-xtables_save_main(int family, const char *progname, int argc, char *argv[])
+xtables_save_main(int family, int argc, char *argv[],
+		  const char *optstring, const struct option *longopts)
 {
 	const struct builtin_table *tables;
 	const char *tablename = NULL;
+	struct do_output_data d = {
+		.format = FMT_NOCOUNTS,
+	};
 	bool dump = false;
 	struct nft_handle h = {
 		.family	= family,
@@ -132,7 +146,7 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
 	FILE *file = NULL;
 	int ret, c;
 
-	xtables_globals.program_name = progname;
+	xtables_globals.program_name = basename(*argv);;
 	c = xtables_init_all(&xtables_globals, family);
 	if (c < 0) {
 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
@@ -141,13 +155,13 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
 		exit(1);
 	}
 
-	while ((c = getopt_long(argc, argv, "bcdt:M:f:46V", options, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) {
 		switch (c) {
 		case 'b':
 			fprintf(stderr, "-b/--binary option is not implemented\n");
 			break;
 		case 'c':
-			show_counters = true;
+			d.format &= ~FMT_NOCOUNTS;
 			break;
 
 		case 't':
@@ -206,13 +220,23 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
 		init_extensions4();
 #endif
 		tables = xtables_ipv4;
+		d.commit = true;
 		break;
 	case NFPROTO_ARP:
 		tables = xtables_arp;
 		break;
-	case NFPROTO_BRIDGE:
+	case NFPROTO_BRIDGE: {
+		const char *ctr = getenv("EBTABLES_SAVE_COUNTER");
+
+		if (!(d.format & FMT_NOCOUNTS)) {
+			d.format |= FMT_EBT_SAVE;
+		} else if (ctr && !strcmp(ctr, "yes")) {
+			d.format &= ~FMT_NOCOUNTS;
+			d.format |= FMT_C_COUNTS | FMT_EBT_SAVE;
+		}
 		tables = xtables_bridge;
 		break;
+	}
 	default:
 		fprintf(stderr, "Unknown family %d\n", family);
 		return 1;
@@ -225,8 +249,11 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
 				strerror(errno));
 		exit(EXIT_FAILURE);
 	}
+	h.ops = nft_family_ops_lookup(h.family);
+	if (!h.ops)
+		xtables_error(PARAMETER_PROBLEM, "Unknown family");
 
-	ret = do_output(&h, tablename, show_counters);
+	ret = do_output(&h, tablename, &d);
 	nft_fini(&h);
 	if (dump)
 		exit(0);
@@ -236,178 +263,24 @@ xtables_save_main(int family, const char *progname, int argc, char *argv[])
 
 int xtables_ip4_save_main(int argc, char *argv[])
 {
-	return xtables_save_main(NFPROTO_IPV4, "iptables-save", argc, argv);
+	return xtables_save_main(NFPROTO_IPV4, argc, argv,
+				 ipt_save_optstring, ipt_save_options);
 }
 
 int xtables_ip6_save_main(int argc, char *argv[])
 {
-	return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv);
-}
-
-static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters)
-{
-	struct nftnl_chain_list *chain_list;
-	unsigned int format = FMT_NOCOUNTS;
-	static bool first = true;
-	time_t now;
-
-	if (!nft_table_find(h, tablename)) {
-		printf("Table `%s' does not exist\n", tablename);
-		return 1;
-	}
-
-	if (!nft_is_table_compatible(h, tablename)) {
-		printf("# Table `%s' is incompatible, use 'nft' tool.\n", tablename);
-		return 0;
-	}
-
-	chain_list = nft_chain_list_get(h, tablename);
-
-	if (first) {
-		now = time(NULL);
-		printf("# Generated by ebtables-save v%s on %s",
-		       IPTABLES_VERSION, ctime(&now));
-		first = false;
-	}
-	printf("*%s\n", tablename);
-
-	if (counters)
-		format = ebt_legacy_counter_format ? FMT_EBT_SAVE : 0;
-
-	/* Dump out chain names first,
-	 * thereby preventing dependency conflicts */
-	nft_chain_save(h, chain_list);
-	nft_rule_save(h, tablename, format);
-	printf("\n");
-	return 0;
+	return xtables_save_main(NFPROTO_IPV6, argc, argv,
+				 ipt_save_optstring, ipt_save_options);
 }
 
-static int ebt_save(struct nft_handle *h, const char *tablename, bool counters)
+int xtables_eb_save_main(int argc, char *argv[])
 {
-	if (!tablename)
-		return nft_for_each_table(h, __ebt_save, counters);
-
-	return __ebt_save(h, tablename, counters);
+	return xtables_save_main(NFPROTO_BRIDGE, argc, argv,
+				 ebt_save_optstring, ebt_save_options);
 }
 
-int xtables_eb_save_main(int argc_, char *argv_[])
+int xtables_arp_save_main(int argc, char *argv[])
 {
-	const char *ctr = getenv("EBTABLES_SAVE_COUNTER");
-	const char *tablename = NULL;
-	struct nft_handle h = {
-		.family	= NFPROTO_BRIDGE,
-	};
-	int c;
-
-	if (ctr) {
-		if (strcmp(ctr, "yes") == 0) {
-			ebt_legacy_counter_format = true;
-			show_counters = true;
-		}
-	}
-
-	xtables_globals.program_name = "ebtables-save";
-	c = xtables_init_all(&xtables_globals, h.family);
-	if (c < 0) {
-		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
-				xtables_globals.program_name,
-				xtables_globals.program_version);
-		exit(1);
-	}
-
-	while ((c = getopt_long(argc_, argv_, "ct:M:V", ebt_save_options, NULL)) != -1) {
-		switch (c) {
-		case 'c':
-			unsetenv("EBTABLES_SAVE_COUNTER");
-			show_counters = true;
-			ebt_legacy_counter_format = false;
-			break;
-		case 't':
-			/* Select specific table. */
-			tablename = optarg;
-			break;
-		case 'M':
-			xtables_modprobe_program = optarg;
-			break;
-		case 'V':
-			printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
-			exit(0);
-		default:
-			fprintf(stderr,
-				"Look at manual page `%s.8' for more information.\n",
-				prog_name);
-			exit(1);
-		}
-	}
-
-	if (nft_init(&h, xtables_bridge) < 0) {
-		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
-				xtables_globals.program_name,
-				xtables_globals.program_version,
-				strerror(errno));
-		exit(EXIT_FAILURE);
-	}
-
-	ebt_save(&h, tablename, show_counters);
-	nft_fini(&h);
-	return 0;
-}
-
-int xtables_arp_save_main(int argc, char **argv)
-{
-	struct nft_handle h = {
-		.family	= NFPROTO_ARP,
-	};
-	int c;
-
-	xtables_globals.program_name = "arptables-save";
-	c = xtables_init_all(&xtables_globals, h.family);
-	if (c < 0) {
-		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
-				xtables_globals.program_name,
-				xtables_globals.program_version);
-		exit(1);
-	}
-
-	while ((c = getopt_long(argc, argv, "cM:V", arp_save_options, NULL)) != -1) {
-		switch (c) {
-		case 'c':
-			show_counters = true;
-			break;
-		case 'M':
-			xtables_modprobe_program = optarg;
-			break;
-		case 'V':
-			printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
-			exit(0);
-		default:
-			fprintf(stderr,
-				"Look at manual page `%s.8' for more information.\n",
-				prog_name);
-			exit(1);
-		}
-	}
-
-	if (nft_init(&h, xtables_arp) < 0) {
-		fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
-				xtables_globals.program_name,
-				xtables_globals.program_version,
-				strerror(errno));
-		exit(EXIT_FAILURE);
-	}
-
-	if (!nft_table_find(&h, "filter"))
-		return 0;
-
-	if (!nft_is_table_compatible(&h, "filter")) {
-		printf("# Table `filter' is incompatible, use 'nft' tool.\n");
-		return 0;
-	}
-
-	printf("*filter\n");
-	nft_chain_save(&h, nft_chain_list_get(&h, "filter"));
-	nft_rule_save(&h, "filter", show_counters ? 0 : FMT_NOCOUNTS);
-	printf("\n");
-	nft_fini(&h);
-	return 0;
+	return xtables_save_main(NFPROTO_ARP, argc, argv,
+				 arp_save_optstring, arp_save_options);
 }
diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
index eb35890afa782b98315a6a7d14fb30cac46937a2..a42c60a3b64c6309293a6f024f07860219b40536 100644
--- a/iptables/xtables-translate.c
+++ b/iptables/xtables-translate.c
@@ -6,7 +6,7 @@
  * by the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-
+#include "config.h"
 #include <time.h>
 #include "xtables-multi.h"
 #include "nft.h"
@@ -413,7 +413,7 @@ static int dummy_compat_rev(const char *name, uint8_t rev, int opt)
 	return 1;
 }
 
-static struct nft_xt_restore_cb cb_xlate = {
+static const struct nft_xt_restore_cb cb_xlate = {
 	.table_new	= xlate_table_new,
 	.chain_set	= xlate_chain_set,
 	.chain_restore	= xlate_chain_user_restore,
@@ -498,7 +498,9 @@ static int xtables_restore_xlate_main(int family, const char *progname,
 		.family = family,
 	};
 	const char *file = NULL;
-	struct nft_xt_restore_parse p = {};
+	struct nft_xt_restore_parse p = {
+		.cb = &cb_xlate,
+	};
 	time_t now = time(NULL);
 	int c;
 
@@ -510,20 +512,20 @@ static int xtables_restore_xlate_main(int family, const char *progname,
 	while ((c = getopt_long(argc, argv, "hf:V", options, NULL)) != -1) {
 		switch (c) {
 		case 'h':
-			print_usage(argv[0], IPTABLES_VERSION);
+			print_usage(argv[0], PACKAGE_VERSION);
 			exit(0);
 		case 'f':
 			file = optarg;
 			break;
 		case 'V':
-			printf("%s v%s\n", argv[0], IPTABLES_VERSION);
+			printf("%s v%s\n", argv[0], PACKAGE_VERSION);
 			exit(0);
 		}
 	}
 
 	if (file == NULL) {
 		fprintf(stderr, "ERROR: missing file name\n");
-		print_usage(argv[0], IPTABLES_VERSION);
+		print_usage(argv[0], PACKAGE_VERSION);
 		exit(0);
 	}
 
@@ -534,8 +536,8 @@ static int xtables_restore_xlate_main(int family, const char *progname,
 	}
 
 	printf("# Translated by %s v%s on %s",
-	       argv[0], IPTABLES_VERSION, ctime(&now));
-	xtables_restore_parse(&h, &p, &cb_xlate, argc, argv);
+	       argv[0], PACKAGE_VERSION, ctime(&now));
+	xtables_restore_parse(&h, &p);
 	printf("# Completed on %s", ctime(&now));
 
 	nft_fini(&h);
diff --git a/iptables/xtables.c b/iptables/xtables.c
index 44986a37aaf50df9cfbcf2f9948f6c7d38326697..8f9dc628d0029f26bc813fa2c611050bc73197ed 100644
--- a/iptables/xtables.c
+++ b/iptables/xtables.c
@@ -24,7 +24,7 @@
  *	along with this program; if not, write to the Free Software
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
+#include "config.h"
 #include <getopt.h>
 #include <string.h>
 #include <netdb.h>
@@ -43,17 +43,6 @@
 #include "nft-shared.h"
 #include "nft.h"
 
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#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[]
@@ -104,7 +93,7 @@ void xtables_exit_error(enum xtables_exittype status, const char *msg, ...) __at
 
 struct xtables_globals xtables_globals = {
 	.option_offset = 0,
-	.program_version = IPTABLES_VERSION,
+	.program_version = PACKAGE_VERSION,
 	.orig_opts = original_opts,
 	.exit_err = xtables_exit_error,
 	.compat_rev = nft_compatible_revision,
@@ -319,27 +308,6 @@ opt2char(int option)
 	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.
@@ -350,18 +318,6 @@ add_command(unsigned int *cmd, const int newcmd, const int othercmds,
 */
 
 /* 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
 set_option(unsigned int *options, unsigned int option, uint8_t *invflg,
@@ -590,6 +546,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 	bool wait_interval_set = false;
 	struct timeval wait_interval;
 	struct xtables_target *t;
+	bool table_set = false;
 	int wait = 0;
 
 	memset(cs, 0, sizeof(*cs));
@@ -879,11 +836,16 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 			if (cs->invert)
 				xtables_error(PARAMETER_PROBLEM,
 					   "unexpected ! flag before --table");
+			if (p->restore && table_set)
+				xtables_error(PARAMETER_PROBLEM,
+					      "The -t option (seen in line %u) cannot be used in %s.\n",
+					      line, xt_params->program_name);
 			if (!nft_table_builtin_find(h, optarg))
 				xtables_error(VERSION_PROBLEM,
 					      "table '%s' does not exist",
 					      optarg);
 			p->table = optarg;
+			table_set = true;
 			break;
 
 		case 'x':
@@ -955,6 +917,9 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 			break;
 
 		case '4':
+			if (p->restore && args->family == AF_INET6)
+				return;
+
 			if (args->family != AF_INET)
 				exit_tryhelp(2);
 
@@ -962,6 +927,9 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 			break;
 
 		case '6':
+			if (p->restore && args->family == AF_INET)
+				return;
+
 			args->family = AF_INET6;
 			xtables_set_nfproto(AF_INET6);
 
@@ -977,7 +945,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 					xtables_error(PARAMETER_PROBLEM,
 						   "multiple consecutive ! not"
 						   " allowed");
-				cs->invert = TRUE;
+				cs->invert = true;
 				optarg[0] = '\0';
 				continue;
 			}
@@ -990,7 +958,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
 				continue;
 			break;
 		}
-		cs->invert = FALSE;
+		cs->invert = false;
 	}
 
 	if (strcmp(p->table, "nat") == 0 &&
@@ -1174,6 +1142,9 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
 	case CMD_SET_POLICY:
 		ret = nft_chain_set(h, p.table, p.chain, p.policy, NULL);
 		break;
+	case CMD_NONE:
+	/* do_parse ignored the line (eg: -4 with ip6tables-restore) */
+		break;
 	default:
 		/* We should never reach this... */
 		exit_tryhelp(2);
diff --git a/libipq/Makefile.in b/libipq/Makefile.in
index 3a9f3db5fbe415700c33660e03f17563225263be..e930a92251244f0bbf1e0c1daad19ac3eb0cd742 100644
--- a/libipq/Makefile.in
+++ b/libipq/Makefile.in
@@ -92,10 +92,10 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = libipq
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
@@ -241,9 +241,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -277,8 +274,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -323,7 +318,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
diff --git a/libiptc/Makefile.am b/libiptc/Makefile.am
index 638295dbb737087f09e6eff2b2a23599ebc3761e..464a069628f0c0436b65612a9c1d9dea27b2d17b 100644
--- a/libiptc/Makefile.am
+++ b/libiptc/Makefile.am
@@ -5,11 +5,8 @@ AM_CPPFLAGS      = ${regular_CPPFLAGS} -I${top_builddir}/include -I${top_srcdir}
 
 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}
+lib_LTLIBRARIES     = libip4tc.la libip6tc.la
 libip4tc_la_SOURCES = libip4tc.c
 libip4tc_la_LDFLAGS = -version-info 2:0:0
 libip6tc_la_SOURCES = libip6tc.c
-libip6tc_la_LDFLAGS = -version-info 2:0:0 ${libiptc_LDFLAGS2}
+libip6tc_la_LDFLAGS = -version-info 2:0:0
diff --git a/libiptc/Makefile.in b/libiptc/Makefile.in
index eb68a250b1f4b92718e69b504e3bb8ceba7567f1..33f6bc0a1d577f435845b574a7a2c053b93e5131 100644
--- a/libiptc/Makefile.in
+++ b/libiptc/Makefile.in
@@ -92,10 +92,10 @@ build_triplet = @build@
 host_triplet = @host@
 subdir = libiptc
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
@@ -148,12 +148,6 @@ libip6tc_la_OBJECTS = $(am_libip6tc_la_OBJECTS)
 libip6tc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
 	$(libip6tc_la_LDFLAGS) $(LDFLAGS) -o $@
-libiptc_la_DEPENDENCIES = libip4tc.la libip6tc.la
-am_libiptc_la_OBJECTS =
-libiptc_la_OBJECTS = $(am_libiptc_la_OBJECTS)
-libiptc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(libiptc_la_LDFLAGS) $(LDFLAGS) -o $@
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -188,10 +182,8 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libip4tc_la_SOURCES) $(libip6tc_la_SOURCES) \
-	$(libiptc_la_SOURCES)
-DIST_SOURCES = $(libip4tc_la_SOURCES) $(libip6tc_la_SOURCES) \
-	$(libiptc_la_SOURCES)
+SOURCES = $(libip4tc_la_SOURCES) $(libip6tc_la_SOURCES)
+DIST_SOURCES = $(libip4tc_la_SOURCES) $(libip6tc_la_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -255,9 +247,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -291,8 +280,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -337,7 +324,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
@@ -375,14 +361,11 @@ xtlibdir = @xtlibdir@
 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}
+lib_LTLIBRARIES = libip4tc.la libip6tc.la
 libip4tc_la_SOURCES = libip4tc.c
 libip4tc_la_LDFLAGS = -version-info 2:0:0
 libip6tc_la_SOURCES = libip6tc.c
-libip6tc_la_LDFLAGS = -version-info 2:0:0 ${libiptc_LDFLAGS2}
+libip6tc_la_LDFLAGS = -version-info 2:0:0
 all: all-am
 
 .SUFFIXES:
@@ -464,9 +447,6 @@ libip4tc.la: $(libip4tc_la_OBJECTS) $(libip4tc_la_DEPENDENCIES) $(EXTRA_libip4tc
 libip6tc.la: $(libip6tc_la_OBJECTS) $(libip6tc_la_DEPENDENCIES) $(EXTRA_libip6tc_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(libip6tc_la_LINK) -rpath $(libdir) $(libip6tc_la_OBJECTS) $(libip6tc_la_LIBADD) $(LIBS)
 
-libiptc.la: $(libiptc_la_OBJECTS) $(libiptc_la_DEPENDENCIES) $(EXTRA_libiptc_la_DEPENDENCIES) 
-	$(AM_V_CCLD)$(libiptc_la_LINK) -rpath $(libdir) $(libiptc_la_OBJECTS) $(libiptc_la_LIBADD) $(LIBS)
-
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
 
diff --git a/libiptc/libip4tc.c b/libiptc/libip4tc.c
index dd599516cd7843a9db4df0b7b89da4435fed5f79..5554063863dbb99bac7a6f1f0c7df1905d2fd362 100644
--- a/libiptc/libip4tc.c
+++ b/libiptc/libip4tc.c
@@ -308,178 +308,4 @@ check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
 	(*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/libip6tc.c b/libiptc/libip6tc.c
index ca01bcb4a805710ec7fc90c6b620323f2d3e9e6a..b7dd1e3374bd29eda57e9eadabc281655f034f44 100644
--- a/libiptc/libip6tc.c
+++ b/libiptc/libip6tc.c
@@ -244,6 +244,7 @@ is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
 	return mptr;
 }
 
+#if 0
 /* All zeroes == unconditional rule. */
 static inline int
 unconditional(const struct ip6t_ip6 *ipv6)
@@ -256,181 +257,4 @@ unconditional(const struct ip6t_ip6 *ipv6)
 
 	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/libiptc.c b/libiptc/libiptc.c
index f4fb09fb509e54f71af35c1c30b3c1563766fc4d..58882015252132dd692b22b8859d421938efe3df 100644
--- a/libiptc/libiptc.c
+++ b/libiptc/libiptc.c
@@ -160,7 +160,7 @@ static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
 		return NULL;
 	memset(c, 0, sizeof(*c));
 
-	strncpy(c->name, name, TABLE_MAXNAMELEN);
+	strncpy(c->name, name, TABLE_MAXNAMELEN - 1);
 	c->hooknum = hooknum;
 	INIT_LIST_HEAD(&c->rules);
 
@@ -188,14 +188,6 @@ 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_*)
  **********************************************************************/
@@ -1370,7 +1362,6 @@ retry:
 	if (parse_table(h) < 0)
 		goto error;
 
-	CHECK(h);
 	return h;
 error:
 	TC_FREE(h);
@@ -1417,7 +1408,6 @@ 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);
@@ -2152,7 +2142,6 @@ TC_READ_COUNTER(const IPT_CHAINLABEL chain,
 	struct rule_head *r;
 
 	iptc_fn = TC_READ_COUNTER;
-	CHECK(*handle);
 
 	if (!(c = iptcc_find_label(chain, handle))) {
 		errno = ENOENT;
@@ -2176,7 +2165,6 @@ TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
 	struct rule_head *r;
 
 	iptc_fn = TC_ZERO_COUNTER;
-	CHECK(handle);
 
 	if (!(c = iptcc_find_label(chain, handle))) {
 		errno = ENOENT;
@@ -2207,7 +2195,6 @@ TC_SET_COUNTER(const IPT_CHAINLABEL chain,
 	STRUCT_ENTRY *e;
 
 	iptc_fn = TC_SET_COUNTER;
-	CHECK(handle);
 
 	if (!(c = iptcc_find_label(chain, handle))) {
 		errno = ENOENT;
@@ -2398,7 +2385,7 @@ int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
 	iptcc_chain_index_delete_chain(c, handle);
 
 	/* Change the name of the chain */
-	strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
+	strncpy(c->name, newname, sizeof(IPT_CHAINLABEL) - 1);
 
 	/* Insert sorted into to list again */
 	iptc_insert_chain(handle, c);
@@ -2532,7 +2519,6 @@ TC_COMMIT(struct xtc_handle *handle)
 	unsigned int new_size;
 
 	iptc_fn = TC_COMMIT;
-	CHECK(*handle);
 
 	/* Don't commit if nothing changed. */
 	if (!handle->changed)
diff --git a/libxtables/Makefile.in b/libxtables/Makefile.in
index ca52fa6c5c872abfb81065e5164da22c2ac3f664..0bbf518fe2560aae4439b861d3a276c78fa304f3 100644
--- a/libxtables/Makefile.in
+++ b/libxtables/Makefile.in
@@ -95,10 +95,10 @@ host_triplet = @host@
 @ENABLE_SHARED_TRUE@am__append_2 = -ldl
 subdir = libxtables
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
@@ -246,9 +246,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -282,8 +279,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -328,7 +323,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
diff --git a/m4/ax_check_linker_flags.m4 b/m4/ax_check_linker_flags.m4
deleted file mode 100644
index ba7bf3cfab36846432f469ec5145f946a543b7c3..0000000000000000000000000000000000000000
--- a/m4/ax_check_linker_flags.m4
+++ /dev/null
@@ -1,78 +0,0 @@
-#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/utils/Makefile.in b/utils/Makefile.in
index c85887313c7238bb134cc44bb096947c989fdef3..1dec6ed256ccea2f40d5606baa202e707722b4c5 100644
--- a/utils/Makefile.in
+++ b/utils/Makefile.in
@@ -99,10 +99,10 @@ sbin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3)
 @ENABLE_SYNCONF_TRUE@am__append_6 = nfsynproxy
 subdir = utils
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_linker_flags.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
@@ -258,9 +258,6 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
-LEX = @LEX@
-LEXLIB = @LEXLIB@
-LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -294,8 +291,6 @@ SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
 VERSION = @VERSION@
-YACC = @YACC@
-YFLAGS = @YFLAGS@
 abs_builddir = @abs_builddir@
 abs_srcdir = @abs_srcdir@
 abs_top_builddir = @abs_top_builddir@
@@ -340,7 +335,6 @@ kinclude_CPPFLAGS = @kinclude_CPPFLAGS@
 ksourcedir = @ksourcedir@
 libdir = @libdir@
 libexecdir = @libexecdir@
-libiptc_LDFLAGS2 = @libiptc_LDFLAGS2@
 libmnl_CFLAGS = @libmnl_CFLAGS@
 libmnl_LIBS = @libmnl_LIBS@
 libnetfilter_conntrack_CFLAGS = @libnetfilter_conntrack_CFLAGS@
diff --git a/utils/nfnl_osf.c b/utils/nfnl_osf.c
index 0ea33fce960fe89a6519752ef1d4b3c0cc7bf3ec..15d531975e11d073dad690f1e120e365ab6f1cc6 100644
--- a/utils/nfnl_osf.c
+++ b/utils/nfnl_osf.c
@@ -343,31 +343,34 @@ static int osf_load_line(char *buffer, int len, int del)
 	pend = xt_osf_strchr(pbeg, OSFPDEL);
 	if (pend) {
 		*pend = '\0';
-		snprintf(obuf, sizeof(obuf), "%s,", pbeg);
+		i = sizeof(obuf);
+		snprintf(obuf, i, "%.*s,", i - 2, pbeg);
 		pbeg = pend + 1;
 	}
 
 	pend = xt_osf_strchr(pbeg, OSFPDEL);
 	if (pend) {
 		*pend = '\0';
+		i = sizeof(f.genre);
 		if (pbeg[0] == '@' || pbeg[0] == '*')
-			snprintf(f.genre, sizeof(f.genre), "%s", pbeg + 1);
-		else
-			snprintf(f.genre, sizeof(f.genre), "%s", pbeg);
+			pbeg++;
+		snprintf(f.genre, i, "%.*s", i - 1, pbeg);
 		pbeg = pend + 1;
 	}
 
 	pend = xt_osf_strchr(pbeg, OSFPDEL);
 	if (pend) {
 		*pend = '\0';
-		snprintf(f.version, sizeof(f.version), "%s", pbeg);
+		i = sizeof(f.version);
+		snprintf(f.version, i, "%.*s", i - 1, pbeg);
 		pbeg = pend + 1;
 	}
 
 	pend = xt_osf_strchr(pbeg, OSFPDEL);
 	if (pend) {
 		*pend = '\0';
-		snprintf(f.subtype, sizeof(f.subtype), "%s", pbeg);
+		i = sizeof(f.subtype);
+		snprintf(f.subtype, i, "%.*s", i - 1, pbeg);
 	}
 
 	xt_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf));