diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4fbf74f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/iptables-1.8.2.tar.bz2
diff --git a/.iptables.metadata b/.iptables.metadata
new file mode 100644
index 0000000..d3ab801
--- /dev/null
+++ b/.iptables.metadata
@@ -0,0 +1 @@
+215c4ef4c6cd29ef0dd265b4fa5ec51a4f930c92 SOURCES/iptables-1.8.2.tar.bz2
diff --git a/SOURCES/0001-iptables-apply-Use-mktemp-instead-of-tempfile.patch b/SOURCES/0001-iptables-apply-Use-mktemp-instead-of-tempfile.patch
new file mode 100644
index 0000000..3eacce0
--- /dev/null
+++ b/SOURCES/0001-iptables-apply-Use-mktemp-instead-of-tempfile.patch
@@ -0,0 +1,35 @@
+From 4ca8d47a364ffa12a0b3ae9ab5096fbe29ae2f4c Mon Sep 17 00:00:00 2001
+From: Phil Sutter <psutter@redhat.com>
+Date: Mon, 17 Sep 2018 11:39:50 +0200
+Subject: [PATCH] iptables-apply: Use mktemp instead of tempfile
+
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/iptables-apply | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/iptables/iptables-apply b/iptables/iptables-apply
+index 819ca4a459c42..a685b6bbcd7dc 100755
+--- a/iptables/iptables-apply
++++ b/iptables/iptables-apply
+@@ -111,7 +111,7 @@ if [[ ! -r "$FILE" ]]; then
+ 	exit 2
+ fi
+ 
+-COMMANDS=(tempfile "$SAVE" "$RESTORE")
++COMMANDS=(mktemp "$SAVE" "$RESTORE")
+ 
+ for cmd in "${COMMANDS[@]}"; do
+ 	if ! command -v $cmd >/dev/null; then
+@@ -122,7 +122,7 @@ done
+ 
+ umask 0700
+ 
+-TMPFILE=$(tempfile -p iptap)
++TMPFILE=$(mktemp)
+ trap "rm -f $TMPFILE" EXIT HUP INT QUIT ILL TRAP ABRT BUS \
+ 		      FPE USR1 SEGV USR2 PIPE ALRM TERM
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0002-extensions-format-security-fixes-in-libip-6-t_icmp.patch b/SOURCES/0002-extensions-format-security-fixes-in-libip-6-t_icmp.patch
new file mode 100644
index 0000000..3ab5eda
--- /dev/null
+++ b/SOURCES/0002-extensions-format-security-fixes-in-libip-6-t_icmp.patch
@@ -0,0 +1,61 @@
+From ea1b67280baebb6b0583b5fe8217f7c37b78dad9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Adam=20Go=C5=82=C4=99biowski?= <adamg@pld-linux.org>
+Date: Wed, 14 Nov 2018 07:35:28 +0100
+Subject: [PATCH] extensions: format-security fixes in libip[6]t_icmp
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+commit 61d6c3834de3 ("xtables: add 'printf' attribute to xlate_add")
+introduced support for gcc feature to check format string against passed
+argument.  This commit adds missing bits to extenstions's libipt_icmp.c
+and libip6t_icmp6.c that were causing build to fail.
+
+Fixes: 61d6c3834de3 ("xtables: add 'printf' attribute to xlate_add")
+Signed-off-by: Adam Gołębiowski <adamg@pld-linux.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 907e429d7548157016cd51aba4adc5d0c7d9f816)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ extensions/libip6t_icmp6.c | 4 ++--
+ extensions/libipt_icmp.c   | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/extensions/libip6t_icmp6.c b/extensions/libip6t_icmp6.c
+index 45a71875722c4..cc7bfaeb72fd7 100644
+--- a/extensions/libip6t_icmp6.c
++++ b/extensions/libip6t_icmp6.c
+@@ -230,7 +230,7 @@ static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
+ 	type_name = icmp6_type_xlate(icmptype);
+ 
+ 	if (type_name) {
+-		xt_xlate_add(xl, type_name);
++		xt_xlate_add(xl, "%s", type_name);
+ 	} else {
+ 		for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i)
+ 			if (icmpv6_codes[i].type == icmptype &&
+@@ -239,7 +239,7 @@ static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
+ 				break;
+ 
+ 		if (i != ARRAY_SIZE(icmpv6_codes))
+-			xt_xlate_add(xl, icmpv6_codes[i].name);
++			xt_xlate_add(xl, "%s", icmpv6_codes[i].name);
+ 		else
+ 			return 0;
+ 	}
+diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
+index 5418997668d4c..e76257c54708c 100644
+--- a/extensions/libipt_icmp.c
++++ b/extensions/libipt_icmp.c
+@@ -236,7 +236,7 @@ static unsigned int type_xlate_print(struct xt_xlate *xl, unsigned int icmptype,
+ 			if (icmp_codes[i].type == icmptype &&
+ 			    icmp_codes[i].code_min == code_min &&
+ 			    icmp_codes[i].code_max == code_max) {
+-				xt_xlate_add(xl, icmp_codes[i].name);
++				xt_xlate_add(xl, "%s", icmp_codes[i].name);
+ 				return 1;
+ 			}
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0003-arptables-nft-use-generic-expression-parsing-functio.patch b/SOURCES/0003-arptables-nft-use-generic-expression-parsing-functio.patch
new file mode 100644
index 0000000..5cc487d
--- /dev/null
+++ b/SOURCES/0003-arptables-nft-use-generic-expression-parsing-functio.patch
@@ -0,0 +1,179 @@
+From 8e065f27f51cb46171b3ab1f9d0f4ad8bd8cdf86 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Sun, 18 Nov 2018 12:31:33 +0100
+Subject: [PATCH] arptables-nft: use generic expression parsing function
+
+since commit d9c6a5d0977a6d8bbe772dbc31a2c4f58eec1708
+("xtables: merge {ip,arp}tables_command_state structs") arptables
+uses the shared representation.
+
+With only minor changes (e.g., use generic counters in command_state),
+in print/save functions we can use the shared nftnl expression parser
+too.
+
+arptables-legacy prints (-L) the jump target first, i.e.:
+-j MARK -d 0.0.0.0/8 --h-length 6 ...
+
+... so keep that here too.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit aa5d3c5b16e94036ac0dc6d44194db7b009ced53)
+
+Conflicts:
+- Drop changes to extensions/libarpt_standard.t since these test
+  snippets are not included in tarball.
+
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-arp.c | 92 +++++-----------------------------------------
+ 1 file changed, 10 insertions(+), 82 deletions(-)
+
+diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
+index 1a98996f94bda..37850bd328b71 100644
+--- a/iptables/nft-arp.c
++++ b/iptables/nft-arp.c
+@@ -412,56 +412,6 @@ static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
+ 	}
+ }
+ 
+-static void nft_arp_rule_to_cs(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,
+-	};
+-
+-	iter = nftnl_expr_iter_create(r);
+-	if (iter == NULL)
+-		return;
+-
+-	ctx.iter = iter;
+-	expr = nftnl_expr_iter_next(iter);
+-	while (expr != NULL) {
+-		const char *name =
+-			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
+-
+-		if (strcmp(name, "counter") == 0)
+-			nft_parse_counter(expr, &ctx.cs->arp.counters);
+-		else if (strcmp(name, "payload") == 0)
+-			nft_parse_payload(&ctx, expr);
+-		else if (strcmp(name, "meta") == 0)
+-			nft_parse_meta(&ctx, expr);
+-		else if (strcmp(name, "bitwise") == 0)
+-			nft_parse_bitwise(&ctx, expr);
+-		else if (strcmp(name, "cmp") == 0)
+-			nft_parse_cmp(&ctx, expr);
+-		else if (strcmp(name, "immediate") == 0)
+-			nft_parse_immediate(&ctx, expr);
+-		else if (strcmp(name, "target") == 0)
+-			nft_parse_target(&ctx, expr);
+-
+-		expr = nftnl_expr_iter_next(iter);
+-	}
+-
+-	nftnl_expr_iter_destroy(iter);
+-
+-	if (cs->jumpto != NULL)
+-		return;
+-
+-	if (cs->target != NULL && cs->target->name != NULL)
+-		cs->target = xtables_find_target(cs->target->name, XTF_TRY_LOAD);
+-	else
+-		cs->jumpto = "";
+-}
+-
+ static void nft_arp_print_header(unsigned int format, const char *chain,
+ 				 const char *pol,
+ 				 const struct xt_counters *counters,
+@@ -627,14 +577,6 @@ after_devdst:
+ 	}
+ }
+ 
+-static void nft_arp_save_counters(const void *data)
+-{
+-	const struct iptables_command_state *cs = data;
+-
+-	printf("[%llu:%llu] ", (unsigned long long)cs->arp.counters.pcnt,
+-			       (unsigned long long)cs->arp.counters.bcnt);
+-}
+-
+ static void
+ nft_arp_save_rule(const void *data, unsigned int format)
+ {
+@@ -643,17 +585,7 @@ nft_arp_save_rule(const void *data, unsigned int format)
+ 	format |= FMT_NUMERIC;
+ 
+ 	nft_arp_print_rule_details(&cs->arp, format);
+-
+-	if (cs->jumpto != NULL && strcmp(cs->jumpto, "") != 0) {
+-		printf("-j %s", cs->jumpto);
+-	} else if (cs->target) {
+-		printf("-j %s", cs->target->name);
+-		if (cs->target->save != NULL)
+-			cs->target->save(&cs->arp, cs->target->t);
+-	}
+-
+-	if (!(format & FMT_NONEWLINE))
+-		fputc('\n', stdout);
++	save_matches_and_target(cs, false, &cs->arp, format);
+ }
+ 
+ static void
+@@ -664,22 +596,18 @@ nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 	if (format & FMT_LINENUMBERS)
+ 		printf("%u ", num);
+ 
+-	nft_arp_rule_to_cs(r, &cs);
++	nft_rule_to_iptables_command_state(r, &cs);
+ 
++	if (cs.jumpto)
++		printf("-j %s ", cs.jumpto);
+ 	nft_arp_print_rule_details(&cs.arp, format);
+-
+-	if (cs.jumpto != NULL && strcmp(cs.jumpto, "") != 0) {
+-		printf("-j %s", cs.jumpto);
+-	} else if (cs.target) {
+-		printf("-j %s", cs.target->name);
+-		cs.target->print(&cs.arp, cs.target->t, format & FMT_NUMERIC);
+-	}
++	print_matches_and_target(&cs, format);
+ 
+ 	if (!(format & FMT_NOCOUNTS)) {
+ 		printf(", pcnt=");
+-		xtables_print_num(cs.arp.counters.pcnt, format);
++		xtables_print_num(cs.counters.pcnt, format);
+ 		printf("-- bcnt=");
+-		xtables_print_num(cs.arp.counters.bcnt, format);
++		xtables_print_num(cs.counters.bcnt, format);
+ 	}
+ 
+ 	if (!(format & FMT_NONEWLINE))
+@@ -720,7 +648,7 @@ static bool nft_arp_rule_find(struct nft_family_ops *ops, struct nftnl_rule *r,
+ 	struct iptables_command_state this = {};
+ 
+ 	/* Delete by matching rule case */
+-	nft_arp_rule_to_cs(r, &this);
++	nft_rule_to_iptables_command_state(r, &this);
+ 
+ 	if (!nft_arp_is_same(&cs->arp, &this.arp))
+ 		return false;
+@@ -751,10 +679,10 @@ struct nft_family_ops nft_family_ops_arp = {
+ 	.print_header		= nft_arp_print_header,
+ 	.print_rule		= nft_arp_print_rule,
+ 	.save_rule		= nft_arp_save_rule,
+-	.save_counters		= nft_arp_save_counters,
++	.save_counters		= save_counters,
+ 	.save_chain		= nft_arp_save_chain,
+ 	.post_parse		= NULL,
+-	.rule_to_cs		= nft_arp_rule_to_cs,
++	.rule_to_cs		= nft_rule_to_iptables_command_state,
+ 	.clear_cs		= nft_clear_iptables_command_state,
+ 	.rule_find		= nft_arp_rule_find,
+ 	.parse_target		= nft_ipv46_parse_target,
+-- 
+2.20.1
+
diff --git a/SOURCES/0004-xtables-Don-t-use-native-nftables-comments.patch b/SOURCES/0004-xtables-Don-t-use-native-nftables-comments.patch
new file mode 100644
index 0000000..22f8f5e
--- /dev/null
+++ b/SOURCES/0004-xtables-Don-t-use-native-nftables-comments.patch
@@ -0,0 +1,134 @@
+From 33ec7bf3be4992f7db8049f2459afbcf8df67221 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 27 Nov 2018 20:07:11 +0100
+Subject: [PATCH] xtables: Don't use native nftables comments
+
+The problem with converting libxt_comment into nftables comment is that
+rules change when parsing from kernel due to comment match being moved
+to the end of the match list. And since match ordering matters, the rule
+may not be found anymore when checking or deleting. Apart from that,
+iptables-nft didn't support multiple comments per rule anymore. This is
+a compatibility issue without technical reason.
+
+Leave conversion from nftables comment to libxt_comment in place so we
+don't break running systems during an update.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit ccf154d7420c07b6e6febc1c3b8b31d2bd1adbe6)
+
+Conflicts:
+- Dropped changes to extensions/libxt_comment.t not present in release
+  tarball.
+
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-ipv4.c | 14 +++-----------
+ iptables/nft-ipv6.c | 14 +++-----------
+ iptables/nft.c      | 27 ---------------------------
+ iptables/nft.h      |  1 -
+ 4 files changed, 6 insertions(+), 50 deletions(-)
+
+diff --git a/iptables/nft-ipv4.c b/iptables/nft-ipv4.c
+index ffb439b4a1128..4497eb9b9347c 100644
+--- a/iptables/nft-ipv4.c
++++ b/iptables/nft-ipv4.c
+@@ -77,17 +77,9 @@ 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) {
+-		/* Use nft built-in comments support instead of comment match */
+-		if (strcmp(matchp->match->name, "comment") == 0) {
+-			ret = add_comment(r, (char *)matchp->match->m->data);
+-			if (ret < 0)
+-				goto try_match;
+-		} else {
+-try_match:
+-			ret = add_match(r, matchp->match->m);
+-			if (ret < 0)
+-				return ret;
+-		}
++		ret = add_match(r, matchp->match->m);
++		if (ret < 0)
++			return ret;
+ 	}
+ 
+ 	/* Counters need to me added before the target, otherwise they are
+diff --git a/iptables/nft-ipv6.c b/iptables/nft-ipv6.c
+index 7bacee4ab3a21..cacb1c9e141f2 100644
+--- a/iptables/nft-ipv6.c
++++ b/iptables/nft-ipv6.c
+@@ -66,17 +66,9 @@ 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) {
+-		/* Use nft built-in comments support instead of comment match */
+-		if (strcmp(matchp->match->name, "comment") == 0) {
+-			ret = add_comment(r, (char *)matchp->match->m->data);
+-			if (ret < 0)
+-				goto try_match;
+-		} else {
+-try_match:
+-			ret = add_match(r, matchp->match->m);
+-			if (ret < 0)
+-				return ret;
+-		}
++		ret = add_match(r, matchp->match->m);
++		if (ret < 0)
++			return ret;
+ 	}
+ 
+ 	/* Counters need to me added before the target, otherwise they are
+diff --git a/iptables/nft.c b/iptables/nft.c
+index e8538d38e0109..6863d851e44c2 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1107,33 +1107,6 @@ enum udata_type {
+ };
+ #define UDATA_TYPE_MAX (__UDATA_TYPE_MAX - 1)
+ 
+-int add_comment(struct nftnl_rule *r, const char *comment)
+-{
+-	struct nftnl_udata_buf *udata;
+-	uint32_t len;
+-
+-	if (nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len))
+-		return -EALREADY;
+-
+-	udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
+-	if (!udata)
+-		return -ENOMEM;
+-
+-	if (strnlen(comment, 255) == 255)
+-		return -ENOSPC;
+-
+-	if (!nftnl_udata_put_strz(udata, UDATA_TYPE_COMMENT, comment))
+-		return -ENOMEM;
+-
+-	nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
+-			    nftnl_udata_buf_data(udata),
+-			    nftnl_udata_buf_len(udata));
+-
+-	nftnl_udata_buf_free(udata);
+-
+-	return 0;
+-}
+-
+ static int parse_udata_cb(const struct nftnl_udata *attr, void *data)
+ {
+ 	unsigned char *value = nftnl_udata_get(attr);
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 9b4ba5f9a63eb..052105fc6f3cd 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -116,7 +116,6 @@ int add_match(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);
+-int add_comment(struct nftnl_rule *r, const char *comment);
+ char *get_comment(const void *data, uint32_t data_len);
+ 
+ enum nft_rule_print {
+-- 
+2.20.1
+
diff --git a/SOURCES/0005-xtables-Introduce-per-table-chain-caches.patch b/SOURCES/0005-xtables-Introduce-per-table-chain-caches.patch
new file mode 100644
index 0000000..96b5069
--- /dev/null
+++ b/SOURCES/0005-xtables-Introduce-per-table-chain-caches.patch
@@ -0,0 +1,592 @@
+From 6fb4660bde886f1f1ad5cfc55553b0a6fd98c2ed Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 15 Nov 2018 14:53:02 +0100
+Subject: [PATCH] xtables: Introduce per table chain caches
+
+Being able to omit the previously obligatory table name check when
+iterating over the chain cache might help restore performance with large
+rulesets in xtables-save and -restore.
+
+There is one subtle quirk in the code: flush_chain_cache() did free the
+global chain cache if not called with a table name but didn't if a table
+name was given even if it emptied the chain cache. In other places,
+chain_cache being non-NULL prevented a cache update from happening, so
+this patch establishes the same behaviour (for each individual chain
+cache) since otherwise unexpected cache updates lead to weird problems.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit c58ecf9f8bcb7619a27ef8ffaddf847a562475a5)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-shared.h      |   3 +-
+ iptables/nft.c             | 160 +++++++++++++++++--------------------
+ iptables/nft.h             |  10 ++-
+ iptables/xtables-restore.c |  16 ++--
+ iptables/xtables-save.c    |  12 +--
+ 5 files changed, 95 insertions(+), 106 deletions(-)
+
+diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
+index e3ecdb4d23df3..9a61d8d2863e3 100644
+--- a/iptables/nft-shared.h
++++ b/iptables/nft-shared.h
+@@ -251,7 +251,8 @@ 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);
++	struct nftnl_chain_list *(*chain_list)(struct nft_handle *h,
++					       const char *table);
+ 	void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
+ 			  const char *chain);
+ 	int (*chain_user_flush)(struct nft_handle *h,
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 6863d851e44c2..36529048a0ca6 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -673,15 +673,17 @@ nft_chain_builtin_find(struct builtin_table *t, const char *chain)
+ static void nft_chain_builtin_init(struct nft_handle *h,
+ 				   struct builtin_table *table)
+ {
+-	struct nftnl_chain_list *list = nft_chain_list_get(h);
++	struct nftnl_chain_list *list = nft_chain_list_get(h, table->name);
+ 	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++) {
+ 
+-		c = nft_chain_list_find(list, table->name,
+-					table->chains[i].name);
++		c = nft_chain_list_find(list, table->chains[i].name);
+ 		if (c != NULL)
+ 			continue;
+ 
+@@ -782,27 +784,33 @@ static void flush_rule_cache(struct nft_handle *h, const char *tablename)
+ 
+ static int __flush_chain_cache(struct nftnl_chain *c, void *data)
+ {
+-	const char *tablename = data;
+-
+-	if (!strcmp(nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), tablename)) {
+-		nftnl_chain_list_del(c);
+-		nftnl_chain_free(c);
+-	}
++	nftnl_chain_list_del(c);
++	nftnl_chain_free(c);
+ 
+ 	return 0;
+ }
+ 
+ static void flush_chain_cache(struct nft_handle *h, const char *tablename)
+ {
+-	if (!h->chain_cache)
+-		return;
++	int i;
+ 
+-	if (tablename) {
+-		nftnl_chain_list_foreach(h->chain_cache, __flush_chain_cache,
+-					 (void *)tablename);
+-	} else {
+-		nftnl_chain_list_free(h->chain_cache);
+-		h->chain_cache = NULL;
++	for (i = 0; i < NFT_TABLE_MAX; i++) {
++		if (h->tables[i].name == NULL)
++			continue;
++
++		if (tablename && strcmp(h->tables[i].name, tablename))
++			continue;
++
++		if (h->tables[i].chain_cache) {
++			if (tablename) {
++				nftnl_chain_list_foreach(h->tables[i].chain_cache,
++							 __flush_chain_cache, NULL);
++				break;
++			} else {
++				nftnl_chain_list_free(h->tables[i].chain_cache);
++				h->tables[i].chain_cache = NULL;
++			}
++		}
+ 	}
+ }
+ 
+@@ -1244,8 +1252,9 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
+ 
+ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+ {
++	struct nft_handle *h = data;
++	struct builtin_table *t;
+ 	struct nftnl_chain *c;
+-	struct nftnl_chain_list *list = data;
+ 
+ 	c = nftnl_chain_alloc();
+ 	if (c == NULL)
+@@ -1254,7 +1263,18 @@ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+ 	if (nftnl_chain_nlmsg_parse(nlh, c) < 0)
+ 		goto out;
+ 
+-	nftnl_chain_list_add_tail(c, list);
++	t = nft_table_builtin_find(h,
++			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE));
++	if (!t)
++		goto out;
++
++	if (!t->chain_cache) {
++		t->chain_cache = nftnl_chain_list_alloc();
++		if (!t->chain_cache)
++			goto out;
++	}
++
++	nftnl_chain_list_add_tail(c, t->chain_cache);
+ 
+ 	return MNL_CB_OK;
+ out:
+@@ -1263,35 +1283,34 @@ err:
+ 	return MNL_CB_OK;
+ }
+ 
+-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h)
++struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
++					    const char *table)
+ {
+ 	char buf[16536];
+ 	struct nlmsghdr *nlh;
+-	struct nftnl_chain_list *list;
++	struct builtin_table *t;
+ 	int ret;
+ 
+-	if (h->chain_cache)
+-		return h->chain_cache;
+-retry:
+-	list = nftnl_chain_list_alloc();
+-	if (list == NULL) {
+-		errno = ENOMEM;
++	t = nft_table_builtin_find(h, table);
++	if (!t)
+ 		return NULL;
+-	}
+ 
++	if (t->chain_cache)
++		return t->chain_cache;
++retry:
+ 	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, list);
++	ret = mnl_talk(h, nlh, nftnl_chain_list_cb, h);
+ 	if (ret < 0 && errno == EINTR) {
+ 		assert(nft_restart(h) >= 0);
+-		nftnl_chain_list_free(list);
+ 		goto retry;
+ 	}
+ 
+-	h->chain_cache = list;
++	if (!t->chain_cache)
++		t->chain_cache = nftnl_chain_list_alloc();
+ 
+-	return list;
++	return t->chain_cache;
+ }
+ 
+ static const char *policy_name[NF_ACCEPT+1] = {
+@@ -1299,8 +1318,7 @@ static const char *policy_name[NF_ACCEPT+1] = {
+ 	[NF_ACCEPT] = "ACCEPT",
+ };
+ 
+-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
+-		   const char *table)
++int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
+ {
+ 	struct nftnl_chain_list_iter *iter;
+ 	struct nft_family_ops *ops;
+@@ -1314,13 +1332,8 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *chain_table =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *policy = NULL;
+ 
+-		if (strcmp(table, chain_table) != 0)
+-			goto next;
+-
+ 		if (nft_chain_builtin(c)) {
+ 			uint32_t pol = NF_ACCEPT;
+ 
+@@ -1331,7 +1344,7 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list,
+ 
+ 		if (ops->save_chain)
+ 			ops->save_chain(c, policy);
+-next:
++
+ 		c = nftnl_chain_list_iter_next(iter);
+ 	}
+ 
+@@ -1502,7 +1515,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
+ 
+ 	nft_fn = nft_rule_flush;
+ 
+-	list = nft_chain_list_get(h);
++	list = nft_chain_list_get(h, table);
+ 	if (list == NULL) {
+ 		ret = 1;
+ 		goto err;
+@@ -1516,21 +1529,16 @@ 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 *table_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 
+-		if (strcmp(table, table_name) != 0)
+-			goto next;
+-
+ 		if (chain != NULL && strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+ 		if (verbose)
+ 			fprintf(stdout, "Flushing chain `%s'\n", chain_name);
+ 
+-		__nft_rule_flush(h, table_name, chain_name);
++		__nft_rule_flush(h, table, chain_name);
+ 
+ 		if (chain != NULL)
+ 			break;
+@@ -1546,6 +1554,7 @@ err:
+ 
+ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *table)
+ {
++	struct nftnl_chain_list *list;
+ 	struct nftnl_chain *c;
+ 	int ret;
+ 
+@@ -1564,9 +1573,9 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
+ 
+ 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
+ 
+-	nft_chain_list_get(h);
+-
+-	nftnl_chain_list_add(c, h->chain_cache);
++	list = nft_chain_list_get(h, table);
++	if (list)
++		nftnl_chain_list_add(c, list);
+ 
+ 	/* the core expects 1 for success and 0 for error */
+ 	return ret == 0 ? 1 : 0;
+@@ -1588,7 +1597,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);
++	list = nft_chain_list_get(h, table);
+ 	if (list == NULL)
+ 		goto err;
+ 
+@@ -1598,8 +1607,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *table_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 
+@@ -1607,9 +1614,6 @@ int nft_chain_user_del(struct nft_handle *h, const char *chain,
+ 		if (nft_chain_builtin(c))
+ 			goto next;
+ 
+-		if (strcmp(table, table_name) != 0)
+-			goto next;
+-
+ 		if (chain != NULL && strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+@@ -1644,8 +1648,7 @@ err:
+ }
+ 
+ struct nftnl_chain *
+-nft_chain_list_find(struct nftnl_chain_list *list,
+-		    const char *table, const char *chain)
++nft_chain_list_find(struct nftnl_chain_list *list, const char *chain)
+ {
+ 	struct nftnl_chain_list_iter *iter;
+ 	struct nftnl_chain *c;
+@@ -1656,14 +1659,9 @@ nft_chain_list_find(struct nftnl_chain_list *list,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *table_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 
+-		if (strcmp(table, table_name) != 0)
+-			goto next;
+-
+ 		if (strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+@@ -1681,11 +1679,11 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+ {
+ 	struct nftnl_chain_list *list;
+ 
+-	list = nft_chain_list_get(h);
++	list = nft_chain_list_get(h, table);
+ 	if (list == NULL)
+ 		return NULL;
+ 
+-	return nft_chain_list_find(list, table, chain);
++	return nft_chain_list_find(list, chain);
+ }
+ 
+ bool nft_chain_exists(struct nft_handle *h,
+@@ -2297,7 +2295,9 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 		return 1;
+ 	}
+ 
+-	list = nft_chain_list_get(h);
++	list = nft_chain_list_get(h, table);
++	if (!list)
++		goto err; /* XXX: return 0 instead? */
+ 
+ 	iter = nftnl_chain_list_iter_create(list);
+ 	if (iter == NULL)
+@@ -2308,8 +2308,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *chain_table =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 		uint32_t policy =
+@@ -2326,8 +2324,6 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 		if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
+ 			basechain = true;
+ 
+-		if (strcmp(table, chain_table) != 0)
+-			goto next;
+ 		if (chain) {
+ 			if (strcmp(chain, chain_name) != 0)
+ 				goto next;
+@@ -2442,7 +2438,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 		return 0;
+ 	}
+ 
+-	list = nft_chain_list_get(h);
++	list = nft_chain_list_get(h, table);
++	if (!list)
++		goto err; /* XXX: correct? */
+ 
+ 	/* Dump policies and custom chains first */
+ 	if (!rulenum)
+@@ -2460,13 +2458,9 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *chain_table =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 
+-		if (strcmp(table, chain_table) != 0)
+-			goto next;
+ 		if (chain && strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+@@ -3045,7 +3039,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);
++	list = nft_chain_list_get(h, table);
+ 	if (list == NULL)
+ 		goto err;
+ 
+@@ -3057,11 +3051,6 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
+ 	while (c != NULL) {
+ 		const char *chain_name =
+ 			nftnl_chain_get(c, NFTNL_CHAIN_NAME);
+-		const char *chain_table =
+-			nftnl_chain_get(c, NFTNL_CHAIN_TABLE);
+-
+-		if (strcmp(table, chain_table) != 0)
+-			goto next;
+ 
+ 		if (chain != NULL && strcmp(chain, chain_name) != 0)
+ 			goto next;
+@@ -3202,7 +3191,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
+ 	struct nftnl_chain *chain;
+ 	int ret = 0;
+ 
+-	list = nft_chain_list_get(h);
++	list = nft_chain_list_get(h, tablename);
+ 	if (list == NULL)
+ 		return -1;
+ 
+@@ -3212,12 +3201,7 @@ static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename
+ 
+ 	chain = nftnl_chain_list_iter_next(iter);
+ 	while (chain != NULL) {
+-		const char *chain_table;
+-
+-		chain_table = nftnl_chain_get_str(chain, NFTNL_CHAIN_TABLE);
+-
+-		if (strcmp(chain_table, tablename) ||
+-		    !nft_chain_builtin(chain))
++		if (!nft_chain_builtin(chain))
+ 			goto next;
+ 
+ 		ret = nft_is_chain_compatible(h, chain);
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 052105fc6f3cd..6229221bd51f7 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -25,6 +25,7 @@ struct builtin_table {
+ 	const char *name;
+ 	struct builtin_chain chains[NF_INET_NUMHOOKS];
+ 	bool initialized;
++	struct nftnl_chain_list *chain_cache;
+ };
+ 
+ struct nft_handle {
+@@ -38,7 +39,6 @@ struct nft_handle {
+ 	struct list_head	err_list;
+ 	struct nft_family_ops	*ops;
+ 	struct builtin_table	*tables;
+-	struct nftnl_chain_list	*chain_cache;
+ 	struct nftnl_rule_list	*rule_cache;
+ 	bool			restore;
+ 	int8_t			config_done;
+@@ -78,9 +78,11 @@ struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *t
+ 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);
+-struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list, const char *table, const char *chain);
+-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list, const char *table);
++struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
++					    const char *table);
++struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list,
++					const char *chain);
++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);
+ int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
+diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
+index f529774054215..a46a92955a01a 100644
+--- a/iptables/xtables-restore.c
++++ b/iptables/xtables-restore.c
+@@ -56,11 +56,12 @@ static void print_usage(const char *name, const char *version)
+ 			"	   [ --ipv6 ]\n", name);
+ }
+ 
+-static struct nftnl_chain_list *get_chain_list(struct nft_handle *h)
++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);
++	chain_list = nft_chain_list_get(h, table);
+ 	if (chain_list == NULL)
+ 		xtables_error(OTHER_PROBLEM, "cannot retrieve chain list\n");
+ 
+@@ -72,7 +73,7 @@ static void chain_delete(struct nftnl_chain_list *clist, const char *curtable,
+ {
+ 	struct nftnl_chain *chain_obj;
+ 
+-	chain_obj = nft_chain_list_find(clist, curtable, chain);
++	chain_obj = nft_chain_list_find(clist, chain);
+ 	/* This chain has been found, delete from list. Later
+ 	 * on, unvisited chains will be purged out.
+ 	 */
+@@ -112,9 +113,6 @@ void xtables_restore_parse(struct nft_handle *h,
+ 
+ 	line = 0;
+ 
+-	if (cb->chain_list)
+-		chain_list = cb->chain_list(h);
+-
+ 	/* Grab standard input. */
+ 	while (fgets(buffer, sizeof(buffer), p->in)) {
+ 		int ret = 0;
+@@ -165,6 +163,9 @@ void xtables_restore_parse(struct nft_handle *h,
+ 			if (p->tablename && (strcmp(p->tablename, table) != 0))
+ 				continue;
+ 
++			if (cb->chain_list)
++				chain_list = cb->chain_list(h, table);
++
+ 			if (noflush == 0) {
+ 				DEBUGP("Cleaning all chains of table '%s'\n",
+ 					table);
+@@ -197,8 +198,7 @@ void xtables_restore_parse(struct nft_handle *h,
+ 				if (cb->chain_del)
+ 					cb->chain_del(chain_list, curtable->name,
+ 						      chain);
+-			} else if (nft_chain_list_find(chain_list,
+-						       curtable->name, chain)) {
++			} else if (nft_chain_list_find(chain_list, chain)) {
+ 				chain_exists = true;
+ 				/* Apparently -n still flushes existing user
+ 				 * defined chains that are redefined. Otherwise,
+diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
+index bed3ee0318995..d121d50e180ff 100644
+--- a/iptables/xtables-save.c
++++ b/iptables/xtables-save.c
+@@ -73,7 +73,9 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
+ 		return 0;
+ 	}
+ 
+-	chain_list = nft_chain_list_get(h);
++	chain_list = nft_chain_list_get(h, tablename);
++	if (!chain_list)
++		return 0;
+ 
+ 	time_t now = time(NULL);
+ 
+@@ -83,7 +85,7 @@ __do_output(struct nft_handle *h, const char *tablename, bool counters)
+ 
+ 	/* Dump out chain names first,
+ 	 * thereby preventing dependency conflicts */
+-	nft_chain_save(h, chain_list, tablename);
++	nft_chain_save(h, chain_list);
+ 	nft_rule_save(h, tablename, counters ? 0 : FMT_NOCOUNTS);
+ 
+ 	now = time(NULL);
+@@ -257,7 +259,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
+ 		return 0;
+ 	}
+ 
+-	chain_list = nft_chain_list_get(h);
++	chain_list = nft_chain_list_get(h, tablename);
+ 
+ 	if (first) {
+ 		now = time(NULL);
+@@ -272,7 +274,7 @@ static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters
+ 
+ 	/* Dump out chain names first,
+ 	 * thereby preventing dependency conflicts */
+-	nft_chain_save(h, chain_list, tablename);
++	nft_chain_save(h, chain_list);
+ 	nft_rule_save(h, tablename, format);
+ 	printf("\n");
+ 	return 0;
+@@ -399,7 +401,7 @@ int xtables_arp_save_main(int argc, char **argv)
+ 	}
+ 
+ 	printf("*filter\n");
+-	nft_chain_save(&h, nft_chain_list_get(&h), "filter");
++	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);
+-- 
+2.20.1
+
diff --git a/SOURCES/0006-nft-add-type-field-to-builtin_table.patch b/SOURCES/0006-nft-add-type-field-to-builtin_table.patch
new file mode 100644
index 0000000..64d3852
--- /dev/null
+++ b/SOURCES/0006-nft-add-type-field-to-builtin_table.patch
@@ -0,0 +1,99 @@
+From c900978466fc50bee651f83eab8fd598b7f88183 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 17 Nov 2018 18:10:15 +0100
+Subject: [PATCH] nft: add type field to builtin_table
+
+Use enum nft_table_type to set the new type field in the structure that
+define tables.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 9847abe6fbb91621f6494df8243ff96f04efdc4a)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 8 ++++++++
+ iptables/nft.h | 1 +
+ 2 files changed, 9 insertions(+)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 36529048a0ca6..f0a60e1f568af 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -379,6 +379,7 @@ static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
+ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_RAW] = {
+ 		.name	= "raw",
++		.type	= NFT_TABLE_RAW,
+ 		.chains = {
+ 			{
+ 				.name	= "PREROUTING",
+@@ -396,6 +397,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	},
+ 	[NFT_TABLE_MANGLE] = {
+ 		.name	= "mangle",
++		.type	= NFT_TABLE_MANGLE,
+ 		.chains = {
+ 			{
+ 				.name	= "PREROUTING",
+@@ -431,6 +433,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	},
+ 	[NFT_TABLE_FILTER] = {
+ 		.name	= "filter",
++		.type	= NFT_TABLE_FILTER,
+ 		.chains = {
+ 			{
+ 				.name	= "INPUT",
+@@ -454,6 +457,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	},
+ 	[NFT_TABLE_SECURITY] = {
+ 		.name	= "security",
++		.type	= NFT_TABLE_SECURITY,
+ 		.chains = {
+ 			{
+ 				.name	= "INPUT",
+@@ -477,6 +481,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	},
+ 	[NFT_TABLE_NAT] = {
+ 		.name	= "nat",
++		.type	= NFT_TABLE_NAT,
+ 		.chains = {
+ 			{
+ 				.name	= "PREROUTING",
+@@ -511,6 +516,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_FILTER] = {
+ 	.name   = "filter",
++	.type	= NFT_TABLE_FILTER,
+ 	.chains = {
+ 			{
+ 				.name   = "INPUT",
+@@ -533,6 +539,7 @@ struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+ struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_FILTER] = {
+ 		.name = "filter",
++		.type	= NFT_TABLE_FILTER,
+ 		.chains = {
+ 			{
+ 				.name   = "INPUT",
+@@ -556,6 +563,7 @@ struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+ 	},
+ 	[NFT_TABLE_NAT] = {
+ 		.name = "nat",
++		.type	= NFT_TABLE_NAT,
+ 		.chains = {
+ 			{
+ 				.name   = "PREROUTING",
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 6229221bd51f7..85c894e80e02e 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -23,6 +23,7 @@ struct builtin_chain {
+ 
+ struct builtin_table {
+ 	const char *name;
++	enum nft_table_type type;
+ 	struct builtin_chain chains[NF_INET_NUMHOOKS];
+ 	bool initialized;
+ 	struct nftnl_chain_list *chain_cache;
+-- 
+2.20.1
+
diff --git a/SOURCES/0007-nft-move-chain_cache-back-to-struct-nft_handle.patch b/SOURCES/0007-nft-move-chain_cache-back-to-struct-nft_handle.patch
new file mode 100644
index 0000000..58e96a1
--- /dev/null
+++ b/SOURCES/0007-nft-move-chain_cache-back-to-struct-nft_handle.patch
@@ -0,0 +1,106 @@
+From df7d696834080e595f29934f8225c12cecb3f819 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 17 Nov 2018 18:16:45 +0100
+Subject: [PATCH] nft: move chain_cache back to struct nft_handle
+
+Place this back into the structure that stores the state information.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 1847d9db753825b0bd1cd450b549f4e39f7bcc31)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 26 +++++++++++++-------------
+ iptables/nft.h |  4 +++-
+ 2 files changed, 16 insertions(+), 14 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index f0a60e1f568af..fdb4ead55a873 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -809,14 +809,14 @@ static void flush_chain_cache(struct nft_handle *h, const char *tablename)
+ 		if (tablename && strcmp(h->tables[i].name, tablename))
+ 			continue;
+ 
+-		if (h->tables[i].chain_cache) {
++		if (h->table[i].chain_cache) {
+ 			if (tablename) {
+-				nftnl_chain_list_foreach(h->tables[i].chain_cache,
++				nftnl_chain_list_foreach(h->table[i].chain_cache,
+ 							 __flush_chain_cache, NULL);
+ 				break;
+ 			} else {
+-				nftnl_chain_list_free(h->tables[i].chain_cache);
+-				h->tables[i].chain_cache = NULL;
++				nftnl_chain_list_free(h->table[i].chain_cache);
++				h->table[i].chain_cache = NULL;
+ 			}
+ 		}
+ 	}
+@@ -1276,13 +1276,13 @@ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+ 	if (!t)
+ 		goto out;
+ 
+-	if (!t->chain_cache) {
+-		t->chain_cache = nftnl_chain_list_alloc();
+-		if (!t->chain_cache)
++	if (!h->table[t->type].chain_cache) {
++		h->table[t->type].chain_cache = nftnl_chain_list_alloc();
++		if (!h->table[t->type].chain_cache)
+ 			goto out;
+ 	}
+ 
+-	nftnl_chain_list_add_tail(c, t->chain_cache);
++	nftnl_chain_list_add_tail(c, h->table[t->type].chain_cache);
+ 
+ 	return MNL_CB_OK;
+ out:
+@@ -1303,8 +1303,8 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ 	if (!t)
+ 		return NULL;
+ 
+-	if (t->chain_cache)
+-		return t->chain_cache;
++	if (h->table[t->type].chain_cache)
++		return h->table[t->type].chain_cache;
+ retry:
+ 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+ 					NLM_F_DUMP, h->seq);
+@@ -1315,10 +1315,10 @@ retry:
+ 		goto retry;
+ 	}
+ 
+-	if (!t->chain_cache)
+-		t->chain_cache = nftnl_chain_list_alloc();
++	if (!h->table[t->type].chain_cache)
++		h->table[t->type].chain_cache = nftnl_chain_list_alloc();
+ 
+-	return t->chain_cache;
++	return h->table[t->type].chain_cache;
+ }
+ 
+ static const char *policy_name[NF_ACCEPT+1] = {
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 85c894e80e02e..1c028206221c4 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -26,7 +26,6 @@ struct builtin_table {
+ 	enum nft_table_type type;
+ 	struct builtin_chain chains[NF_INET_NUMHOOKS];
+ 	bool initialized;
+-	struct nftnl_chain_list *chain_cache;
+ };
+ 
+ struct nft_handle {
+@@ -40,6 +39,9 @@ struct nft_handle {
+ 	struct list_head	err_list;
+ 	struct nft_family_ops	*ops;
+ 	struct builtin_table	*tables;
++	struct {
++		struct nftnl_chain_list *chain_cache;
++	} table[NFT_TABLE_MAX];
+ 	struct nftnl_rule_list	*rule_cache;
+ 	bool			restore;
+ 	int8_t			config_done;
+-- 
+2.20.1
+
diff --git a/SOURCES/0008-nft-move-initialize-to-struct-nft_handle.patch b/SOURCES/0008-nft-move-initialize-to-struct-nft_handle.patch
new file mode 100644
index 0000000..a943173
--- /dev/null
+++ b/SOURCES/0008-nft-move-initialize-to-struct-nft_handle.patch
@@ -0,0 +1,91 @@
+From 993eea7a78dd3690cb864b58c7056d5851550f5f Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 17 Nov 2018 18:38:30 +0100
+Subject: [PATCH] nft: move initialize to struct nft_handle
+
+Move this to the structure that stores, stateful information. Introduce
+nft_table_initialized() and use it.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 5016d1eb84f951d84f5a0c18f994f40677ad0643)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 14 ++++++++++----
+ iptables/nft.h |  2 +-
+ 2 files changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index fdb4ead55a873..9b479307a2fbc 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -587,13 +587,19 @@ struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+ 	},
+ };
+ 
++static bool nft_table_initialized(const struct nft_handle *h,
++				  enum nft_table_type type)
++{
++	return h->table[type].initialized;
++}
++
+ static int nft_table_builtin_add(struct nft_handle *h,
+ 				 struct builtin_table *_t)
+ {
+ 	struct nftnl_table *t;
+ 	int ret;
+ 
+-	if (_t->initialized)
++	if (nft_table_initialized(h, _t->type))
+ 		return 0;
+ 
+ 	t = nftnl_table_alloc();
+@@ -707,7 +713,7 @@ static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
+ 	if (t == NULL)
+ 		return -1;
+ 
+-	if (t->initialized)
++	if (nft_table_initialized(h, t->type))
+ 		return 0;
+ 
+ 	if (nft_table_builtin_add(h, t) < 0)
+@@ -715,7 +721,7 @@ static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
+ 
+ 	nft_chain_builtin_init(h, t);
+ 
+-	t->initialized = true;
++	h->table[t->type].initialized = true;
+ 
+ 	return 0;
+ }
+@@ -1875,7 +1881,7 @@ static int __nft_table_flush(struct nft_handle *h, const char *table)
+ 
+ 	_t = nft_table_builtin_find(h, table);
+ 	assert(_t);
+-	_t->initialized = false;
++	h->table[_t->type].initialized = false;
+ 
+ 	flush_chain_cache(h, table);
+ 	flush_rule_cache(h, table);
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 1c028206221c4..b9ba66b110042 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -25,7 +25,6 @@ struct builtin_table {
+ 	const char *name;
+ 	enum nft_table_type type;
+ 	struct builtin_chain chains[NF_INET_NUMHOOKS];
+-	bool initialized;
+ };
+ 
+ struct nft_handle {
+@@ -41,6 +40,7 @@ struct nft_handle {
+ 	struct builtin_table	*tables;
+ 	struct {
+ 		struct nftnl_chain_list *chain_cache;
++		bool			initialized;
+ 	} table[NFT_TABLE_MAX];
+ 	struct nftnl_rule_list	*rule_cache;
+ 	bool			restore;
+-- 
+2.20.1
+
diff --git a/SOURCES/0009-xtables-constify-struct-builtin_table-and-struct-bui.patch b/SOURCES/0009-xtables-constify-struct-builtin_table-and-struct-bui.patch
new file mode 100644
index 0000000..5f49552
--- /dev/null
+++ b/SOURCES/0009-xtables-constify-struct-builtin_table-and-struct-bui.patch
@@ -0,0 +1,287 @@
+From 8ef9a56ea980170e146353bc8d7b91765b801344 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 17 Nov 2018 18:25:58 +0100
+Subject: [PATCH] xtables: constify struct builtin_table and struct
+ builtin_chain
+
+These definitions should be const, propagate this to all existing users.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 3c7ea26c85b95a4c62355c359030d6bbdf2f8df0)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c               | 42 ++++++++++++++++++------------------
+ iptables/nft.h               | 14 ++++++------
+ iptables/xtables-restore.c   |  4 ++--
+ iptables/xtables-save.c      |  2 +-
+ iptables/xtables-translate.c |  2 +-
+ 5 files changed, 32 insertions(+), 32 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 9b479307a2fbc..7b6fb2b10686d 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -376,7 +376,7 @@ static int batch_rule_add(struct nft_handle *h, enum obj_update_type type,
+ 	return batch_add(h, type, r);
+ }
+ 
+-struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
++const struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_RAW] = {
+ 		.name	= "raw",
+ 		.type	= NFT_TABLE_RAW,
+@@ -513,7 +513,7 @@ struct builtin_table xtables_ipv4[NFT_TABLE_MAX] = {
+ 
+ #include <linux/netfilter_arp.h>
+ 
+-struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
++const struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_FILTER] = {
+ 	.name   = "filter",
+ 	.type	= NFT_TABLE_FILTER,
+@@ -536,7 +536,7 @@ struct builtin_table xtables_arp[NFT_TABLE_MAX] = {
+ 
+ #include <linux/netfilter_bridge.h>
+ 
+-struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
++const struct builtin_table xtables_bridge[NFT_TABLE_MAX] = {
+ 	[NFT_TABLE_FILTER] = {
+ 		.name = "filter",
+ 		.type	= NFT_TABLE_FILTER,
+@@ -594,7 +594,7 @@ static bool nft_table_initialized(const struct nft_handle *h,
+ }
+ 
+ static int nft_table_builtin_add(struct nft_handle *h,
+-				 struct builtin_table *_t)
++				 const struct builtin_table *_t)
+ {
+ 	struct nftnl_table *t;
+ 	int ret;
+@@ -614,8 +614,8 @@ static int nft_table_builtin_add(struct nft_handle *h,
+ }
+ 
+ static struct nftnl_chain *
+-nft_chain_builtin_alloc(struct builtin_table *table,
+-			struct builtin_chain *chain, int policy)
++nft_chain_builtin_alloc(const struct builtin_table *table,
++			const struct builtin_chain *chain, int policy)
+ {
+ 	struct nftnl_chain *c;
+ 
+@@ -634,8 +634,8 @@ nft_chain_builtin_alloc(struct builtin_table *table,
+ }
+ 
+ static void nft_chain_builtin_add(struct nft_handle *h,
+-				  struct builtin_table *table,
+-				  struct builtin_chain *chain)
++				  const struct builtin_table *table,
++				  const struct builtin_chain *chain)
+ {
+ 	struct nftnl_chain *c;
+ 
+@@ -647,7 +647,7 @@ static void nft_chain_builtin_add(struct nft_handle *h,
+ }
+ 
+ /* find if built-in table already exists */
+-struct builtin_table *
++const struct builtin_table *
+ nft_table_builtin_find(struct nft_handle *h, const char *table)
+ {
+ 	int i;
+@@ -668,8 +668,8 @@ nft_table_builtin_find(struct nft_handle *h, const char *table)
+ }
+ 
+ /* find if built-in chain already exists */
+-struct builtin_chain *
+-nft_chain_builtin_find(struct builtin_table *t, const char *chain)
++const struct builtin_chain *
++nft_chain_builtin_find(const struct builtin_table *t, const char *chain)
+ {
+ 	int i;
+ 	bool found = false;
+@@ -685,7 +685,7 @@ nft_chain_builtin_find(struct builtin_table *t, const char *chain)
+ }
+ 
+ static void nft_chain_builtin_init(struct nft_handle *h,
+-				   struct builtin_table *table)
++				   const struct builtin_table *table)
+ {
+ 	struct nftnl_chain_list *list = nft_chain_list_get(h, table->name);
+ 	struct nftnl_chain *c;
+@@ -707,7 +707,7 @@ static void nft_chain_builtin_init(struct nft_handle *h,
+ 
+ static int nft_xt_builtin_init(struct nft_handle *h, const char *table)
+ {
+-	struct builtin_table *t;
++	const struct builtin_table *t;
+ 
+ 	t = nft_table_builtin_find(h, table);
+ 	if (t == NULL)
+@@ -750,7 +750,7 @@ static int nft_restart(struct nft_handle *h)
+ 	return 0;
+ }
+ 
+-int nft_init(struct nft_handle *h, struct builtin_table *t)
++int nft_init(struct nft_handle *h, const struct builtin_table *t)
+ {
+ 	h->nl = mnl_socket_open(NETLINK_NETFILTER);
+ 	if (h->nl == NULL)
+@@ -852,8 +852,8 @@ static struct nftnl_chain *nft_chain_new(struct nft_handle *h,
+ 				       const struct xt_counters *counters)
+ {
+ 	struct nftnl_chain *c;
+-	struct builtin_table *_t;
+-	struct builtin_chain *_c;
++	const struct builtin_table *_t;
++	const struct builtin_chain *_c;
+ 
+ 	_t = nft_table_builtin_find(h, table);
+ 	if (!_t) {
+@@ -1267,7 +1267,7 @@ nft_rule_print_save(const struct nftnl_rule *r, enum nft_rule_print type,
+ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+ {
+ 	struct nft_handle *h = data;
+-	struct builtin_table *t;
++	const struct builtin_table *t;
+ 	struct nftnl_chain *c;
+ 
+ 	c = nftnl_chain_alloc();
+@@ -1302,7 +1302,7 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ {
+ 	char buf[16536];
+ 	struct nlmsghdr *nlh;
+-	struct builtin_table *t;
++	const struct builtin_table *t;
+ 	int ret;
+ 
+ 	t = nft_table_builtin_find(h, table);
+@@ -1703,7 +1703,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+ bool nft_chain_exists(struct nft_handle *h,
+ 		      const char *table, const char *chain)
+ {
+-	struct builtin_table *t = nft_table_builtin_find(h, table);
++	const struct builtin_table *t = nft_table_builtin_find(h, table);
+ 
+ 	/* xtables does not support custom tables */
+ 	if (!t)
+@@ -1868,7 +1868,7 @@ int nft_for_each_table(struct nft_handle *h,
+ 
+ static int __nft_table_flush(struct nft_handle *h, const char *table)
+ {
+-	struct builtin_table *_t;
++	const struct builtin_table *_t;
+ 	struct nftnl_table *t;
+ 
+ 	t = nftnl_table_alloc();
+@@ -3166,7 +3166,7 @@ static int nft_is_chain_compatible(const struct nft_handle *h,
+ 				   const struct nftnl_chain *chain)
+ {
+ 	const char *table, *name, *type, *cur_table;
+-	struct builtin_chain *chains;
++	const struct builtin_chain *chains;
+ 	int i, j, prio;
+ 	enum nf_inet_hooks hook;
+ 
+diff --git a/iptables/nft.h b/iptables/nft.h
+index b9ba66b110042..bf60ab3943659 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -37,7 +37,7 @@ struct nft_handle {
+ 	struct nftnl_batch	*batch;
+ 	struct list_head	err_list;
+ 	struct nft_family_ops	*ops;
+-	struct builtin_table	*tables;
++	const struct builtin_table *tables;
+ 	struct {
+ 		struct nftnl_chain_list *chain_cache;
+ 		bool			initialized;
+@@ -52,14 +52,14 @@ struct nft_handle {
+ 	} error;
+ };
+ 
+-extern struct builtin_table xtables_ipv4[NFT_TABLE_MAX];
+-extern struct builtin_table xtables_arp[NFT_TABLE_MAX];
+-extern struct builtin_table xtables_bridge[NFT_TABLE_MAX];
++extern const struct builtin_table xtables_ipv4[NFT_TABLE_MAX];
++extern const struct builtin_table xtables_arp[NFT_TABLE_MAX];
++extern const struct builtin_table xtables_bridge[NFT_TABLE_MAX];
+ 
+ int mnl_talk(struct nft_handle *h, struct nlmsghdr *nlh,
+ 	     int (*cb)(const struct nlmsghdr *nlh, void *data),
+ 	     void *data);
+-int nft_init(struct nft_handle *h, struct builtin_table *t);
++int nft_init(struct nft_handle *h, const struct builtin_table *t);
+ void nft_fini(struct nft_handle *h);
+ 
+ /*
+@@ -73,7 +73,7 @@ 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);
+ void nft_table_new(struct nft_handle *h, const char *table);
+-struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *table);
++const struct builtin_table *nft_table_builtin_find(struct nft_handle *h, const char *table);
+ 
+ /*
+  * Operations with chains.
+@@ -92,7 +92,7 @@ int nft_chain_user_flush(struct nft_handle *h, struct nftnl_chain_list *list,
+ 			 const char *chain, const char *table);
+ int nft_chain_user_rename(struct nft_handle *h, const char *chain, const char *table, const char *newname);
+ int nft_chain_zero_counters(struct nft_handle *h, const char *chain, const char *table, bool verbose);
+-struct builtin_chain *nft_chain_builtin_find(struct builtin_table *t, const char *chain);
++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);
+ 
+ /*
+diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
+index a46a92955a01a..642876d6c70ac 100644
+--- a/iptables/xtables-restore.c
++++ b/iptables/xtables-restore.c
+@@ -105,9 +105,9 @@ void xtables_restore_parse(struct nft_handle *h,
+ 			   struct nft_xt_restore_cb *cb,
+ 			   int argc, char *argv[])
+ {
++	const struct builtin_table *curtable = NULL;
+ 	char buffer[10240];
+ 	int in_table = 0;
+-	struct builtin_table *curtable = NULL;
+ 	const struct xtc_ops *ops = &xtc_ops;
+ 	struct nftnl_chain_list *chain_list = NULL;
+ 
+@@ -359,7 +359,7 @@ void xtables_restore_parse(struct nft_handle *h,
+ static int
+ xtables_restore_main(int family, const char *progname, int argc, char *argv[])
+ {
+-	struct builtin_table *tables;
++	const struct builtin_table *tables;
+ 	struct nft_handle h = {
+ 		.family = family,
+ 		.restore = true,
+diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c
+index d121d50e180ff..414a864b6196b 100644
+--- a/iptables/xtables-save.c
++++ b/iptables/xtables-save.c
+@@ -122,7 +122,7 @@ do_output(struct nft_handle *h, const char *tablename, bool counters)
+ static int
+ xtables_save_main(int family, const char *progname, int argc, char *argv[])
+ {
+-	struct builtin_table *tables;
++	const struct builtin_table *tables;
+ 	const char *tablename = NULL;
+ 	bool dump = false;
+ 	struct nft_handle h = {
+diff --git a/iptables/xtables-translate.c b/iptables/xtables-translate.c
+index 849c53f30e155..e1d2a7d6cce88 100644
+--- a/iptables/xtables-translate.c
++++ b/iptables/xtables-translate.c
+@@ -426,7 +426,7 @@ static int xtables_xlate_main_common(struct nft_handle *h,
+ 				     int family,
+ 				     const char *progname)
+ {
+-	struct builtin_table *tables;
++	const struct builtin_table *tables;
+ 	int ret;
+ 
+ 	xtables_globals.program_name = progname;
+-- 
+2.20.1
+
diff --git a/SOURCES/0010-nft-Simplify-nftnl_rule_list_chain_save.patch b/SOURCES/0010-nft-Simplify-nftnl_rule_list_chain_save.patch
new file mode 100644
index 0000000..4e43cc0
--- /dev/null
+++ b/SOURCES/0010-nft-Simplify-nftnl_rule_list_chain_save.patch
@@ -0,0 +1,60 @@
+From b54cb3132045f5cac8c09b24e564ef05aff29288 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:02 +0100
+Subject: [PATCH] nft: Simplify nftnl_rule_list_chain_save()
+
+Since there are per table chain caches, The chain list passed to that
+function is comprised of chains belonging to the right table only.
+Therefore the table name check can safely be skipped.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 71f9e474d467dd59582d7a3920eded9613b99000)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 10 +++-------
+ 1 file changed, 3 insertions(+), 7 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 7b6fb2b10686d..9d20eb0eac2db 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2381,8 +2381,7 @@ list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 
+ static int
+ nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
+-			 const char *table, struct nftnl_chain_list *list,
+-			 int counters)
++			   struct nftnl_chain_list *list, int counters)
+ {
+ 	struct nftnl_chain_list_iter *iter;
+ 	struct nftnl_chain *c;
+@@ -2393,15 +2392,12 @@ nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *chain_table =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 		uint32_t policy =
+ 			nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+ 
+-		if (strcmp(table, chain_table) != 0 ||
+-		    (chain && strcmp(chain, chain_name) != 0))
++		if (chain && strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+ 		/* this is a base chain */
+@@ -2458,7 +2454,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 
+ 	/* Dump policies and custom chains first */
+ 	if (!rulenum)
+-		nftnl_rule_list_chain_save(h, chain, table, list, counters);
++		nftnl_rule_list_chain_save(h, chain, list, counters);
+ 
+ 	/* Now dump out rules in this table */
+ 	iter = nftnl_chain_list_iter_create(list);
+-- 
+2.20.1
+
diff --git a/SOURCES/0011-nft-Review-unclear-return-points.patch b/SOURCES/0011-nft-Review-unclear-return-points.patch
new file mode 100644
index 0000000..b415d20
--- /dev/null
+++ b/SOURCES/0011-nft-Review-unclear-return-points.patch
@@ -0,0 +1,43 @@
+From f5043360f8a340ea4b924edfe5c3779099671061 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:03 +0100
+Subject: [PATCH] nft: Review unclear return points
+
+When converting to per table chain caches, these two error returns were
+marked for review but apparently forgotten. Make sure error condition is
+propagated when returning at those points.
+
+Fixes: c58ecf9f8bcb7 ("xtables: Introduce per table chain caches")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 7e09582a57440f13796bdd5bd70466ef0913345b)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 9d20eb0eac2db..1fca1f17147f6 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2311,7 +2311,7 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 
+ 	list = nft_chain_list_get(h, table);
+ 	if (!list)
+-		goto err; /* XXX: return 0 instead? */
++		return 0;
+ 
+ 	iter = nftnl_chain_list_iter_create(list);
+ 	if (iter == NULL)
+@@ -2450,7 +2450,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 
+ 	list = nft_chain_list_get(h, table);
+ 	if (!list)
+-		goto err; /* XXX: correct? */
++		goto err;
+ 
+ 	/* Dump policies and custom chains first */
+ 	if (!rulenum)
+-- 
+2.20.1
+
diff --git a/SOURCES/0012-xtables-restore-Review-chain-handling.patch b/SOURCES/0012-xtables-restore-Review-chain-handling.patch
new file mode 100644
index 0000000..dab20f9
--- /dev/null
+++ b/SOURCES/0012-xtables-restore-Review-chain-handling.patch
@@ -0,0 +1,172 @@
+From 6e1e7be69aa29784a3f891fe7ffbd9b5c534c901 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:04 +0100
+Subject: [PATCH] xtables-restore: Review chain handling
+
+There is no need to "delete" (actually, remove from cache) a chain if
+noflush wasn't given: While handling the corresponding table line,
+'table_flush' callback has already taken care of that.
+
+This .chain_del indirection is not required since d1eb4d587297
+("iptables-compat: chains are purge out already from table flush").
+
+Streamlining the code further, move syntax checks to the top. If these
+concede, there are three cases to distinguish:
+
+A) Given chain name matches a builtin one in current table, so assume it
+   exists already and just set policy and counters.
+
+B) Noflush was given and the (custom) chain exists already, flush it.
+
+C) Custom chain was either flushed (noflush not given) or didn't exist
+   before, create it.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 9523b2e9dee3d9b4439214092c496542ce9f434e)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-shared.h      |  2 --
+ iptables/xtables-restore.c | 68 +++++++++++---------------------------
+ 2 files changed, 19 insertions(+), 51 deletions(-)
+
+diff --git a/iptables/nft-shared.h b/iptables/nft-shared.h
+index 9a61d8d2863e3..17fff984ba312 100644
+--- a/iptables/nft-shared.h
++++ b/iptables/nft-shared.h
+@@ -253,8 +253,6 @@ 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);
+-	void (*chain_del)(struct nftnl_chain_list *clist, const char *curtable,
+-			  const char *chain);
+ 	int (*chain_user_flush)(struct nft_handle *h,
+ 				struct nftnl_chain_list *clist,
+ 				const char *table, const char *chain);
+diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
+index 642876d6c70ac..4e00ed86be06d 100644
+--- a/iptables/xtables-restore.c
++++ b/iptables/xtables-restore.c
+@@ -68,21 +68,6 @@ static struct nftnl_chain_list *get_chain_list(struct nft_handle *h,
+ 	return chain_list;
+ }
+ 
+-static void chain_delete(struct nftnl_chain_list *clist, const char *curtable,
+-			 const char *chain)
+-{
+-	struct nftnl_chain *chain_obj;
+-
+-	chain_obj = nft_chain_list_find(clist, chain);
+-	/* This chain has been found, delete from list. Later
+-	 * on, unvisited chains will be purged out.
+-	 */
+-	if (chain_obj != NULL) {
+-		nftnl_chain_list_del(chain_obj);
+-		nftnl_chain_free(chain_obj);
+-	}
+-}
+-
+ struct nft_xt_restore_cb restore_cb = {
+ 	.chain_list	= get_chain_list,
+ 	.commit		= nft_commit,
+@@ -90,7 +75,6 @@ struct nft_xt_restore_cb restore_cb = {
+ 	.table_new	= nft_table_new,
+ 	.table_flush	= nft_table_flush,
+ 	.chain_user_flush = nft_chain_user_flush,
+-	.chain_del	= chain_delete,
+ 	.do_command	= do_commandx,
+ 	.chain_set	= nft_chain_set,
+ 	.chain_user_add	= nft_chain_user_add,
+@@ -183,7 +167,6 @@ void xtables_restore_parse(struct nft_handle *h,
+ 			/* New chain. */
+ 			char *policy, *chain = NULL;
+ 			struct xt_counters count = {};
+-			bool chain_exists = false;
+ 
+ 			chain = strtok(buffer+1, " \t\n");
+ 			DEBUGP("line %u, chain '%s'\n", line, chain);
+@@ -194,21 +177,6 @@ void xtables_restore_parse(struct nft_handle *h,
+ 				exit(1);
+ 			}
+ 
+-			if (noflush == 0) {
+-				if (cb->chain_del)
+-					cb->chain_del(chain_list, curtable->name,
+-						      chain);
+-			} else if (nft_chain_list_find(chain_list, chain)) {
+-				chain_exists = true;
+-				/* Apparently -n still flushes existing user
+-				 * defined chains that are redefined. Otherwise,
+-				 * leave them as is.
+-				 */
+-				if (cb->chain_user_flush)
+-					cb->chain_user_flush(h, chain_list,
+-							     curtable->name, chain);
+-			}
+-
+ 			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
+ 				xtables_error(PARAMETER_PROBLEM,
+ 					   "Invalid chain name `%s' "
+@@ -246,24 +214,28 @@ void xtables_restore_parse(struct nft_handle *h,
+ 				}
+ 				DEBUGP("Setting policy of chain %s to %s\n",
+ 				       chain, policy);
+-				ret = 1;
+ 
+-			} else {
+-				if (!chain_exists &&
+-				    cb->chain_user_add &&
+-				    cb->chain_user_add(h, chain,
+-						       curtable->name) < 0) {
+-					if (errno == EEXIST)
+-						continue;
++			} else if (noflush &&
++				   nftnl_chain_list_lookup_byname(chain_list, chain)) {
++				/* Apparently -n still flushes existing user
++				 * defined chains that are redefined. Otherwise,
++				 * leave them as is.
++				 */
++				if (cb->chain_user_flush)
++					cb->chain_user_flush(h, chain_list,
++							     curtable->name, chain);
++			} else if (cb->chain_user_add &&
++				   cb->chain_user_add(h, chain,
++						      curtable->name) < 0) {
++				if (errno == EEXIST)
++					continue;
+ 
+-					xtables_error(PARAMETER_PROBLEM,
+-						      "cannot create chain "
+-						      "'%s' (%s)\n", chain,
+-						      strerror(errno));
+-				}
+-				continue;
++				xtables_error(PARAMETER_PROBLEM,
++					      "cannot create chain "
++					      "'%s' (%s)\n", chain,
++					      strerror(errno));
+ 			}
+-
++			ret = 1;
+ 		} else if (in_table) {
+ 			int a;
+ 			char *pcnt = NULL;
+@@ -496,7 +468,6 @@ struct nft_xt_restore_cb ebt_restore_cb = {
+ 	.table_new	= nft_table_new,
+ 	.table_flush	= nft_table_flush,
+ 	.chain_user_flush = nft_chain_user_flush,
+-	.chain_del	= chain_delete,
+ 	.do_command	= do_commandeb,
+ 	.chain_set	= nft_chain_set,
+ 	.chain_user_add	= nft_chain_user_add,
+@@ -542,7 +513,6 @@ struct nft_xt_restore_cb arp_restore_cb = {
+ 	.table_new	= nft_table_new,
+ 	.table_flush	= nft_table_flush,
+ 	.chain_user_flush = nft_chain_user_flush,
+-	.chain_del	= chain_delete,
+ 	.do_command	= do_commandarp,
+ 	.chain_set	= nft_chain_set,
+ 	.chain_user_add	= nft_chain_user_add,
+-- 
+2.20.1
+
diff --git a/SOURCES/0013-nft-Review-is_-_compatible-routines.patch b/SOURCES/0013-nft-Review-is_-_compatible-routines.patch
new file mode 100644
index 0000000..db210cb
--- /dev/null
+++ b/SOURCES/0013-nft-Review-is_-_compatible-routines.patch
@@ -0,0 +1,233 @@
+From 0318e0d123ebecbc98305284832774647d953859 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:05 +0100
+Subject: [PATCH] nft: Review is_*_compatible() routines
+
+- Call to nft_table_builtin_find() in nft_is_table_compatible() is not
+  needed, as it is repeated in the latter call to nft_chain_list_get()
+  by nft_are_chains_compatible().
+
+- Turn nft_is_chain_compatible(), nft_is_rule_compatible() and
+  nft_is_expr_compatible() into callbacks for use with respective
+  foreach functions.
+
+- nft_are_chains_compatible() is not needed anymore due to foreach
+  function use.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit e774b15299c271a4c7570899591cf1b7960477ea)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 153 ++++++++++++++++++-------------------------------
+ 1 file changed, 55 insertions(+), 98 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 1fca1f17147f6..5032c718b33a9 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -3115,7 +3115,7 @@ static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = {
+ };
+ 
+ 
+-static int nft_is_expr_compatible(const struct nftnl_expr *expr)
++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;
+@@ -3130,138 +3130,95 @@ static int nft_is_expr_compatible(const struct nftnl_expr *expr)
+ 	    nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0)
+ 		return 0;
+ 
+-	return 1;
++	return -1;
+ }
+ 
+-static bool nft_is_rule_compatible(struct nftnl_rule *rule)
+-{
+-	struct nftnl_expr_iter *iter;
+-	struct nftnl_expr *expr;
+-	bool compatible = false;
+-
+-	iter = nftnl_expr_iter_create(rule);
+-	if (iter == NULL)
+-		return false;
++struct nft_is_rule_compatible_data {
++	const char *tablename;
++};
+ 
+-	expr = nftnl_expr_iter_next(iter);
+-	while (expr != NULL) {
+-		if (nft_is_expr_compatible(expr) == 0) {
+-			expr = nftnl_expr_iter_next(iter);
+-			continue;
+-		}
++static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
++{
++	const char *table = nftnl_rule_get_str(rule, NFTNL_RULE_TABLE);
++	struct nft_is_rule_compatible_data *d = data;
+ 
+-		compatible = true;
+-		break;
+-	}
++	/* ignore rules belonging to a different table */
++	if (strcmp(table, d->tablename))
++		return 0;
+ 
+-	nftnl_expr_iter_destroy(iter);
+-	return compatible;
++	return nftnl_expr_foreach(rule, nft_is_expr_compatible, NULL);
+ }
+ 
+-static int nft_is_chain_compatible(const struct nft_handle *h,
+-				   const struct nftnl_chain *chain)
++static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+ {
+-	const char *table, *name, *type, *cur_table;
+-	const struct builtin_chain *chains;
+-	int i, j, prio;
++	const struct builtin_chain *chains = NULL, *chain = NULL;
++	const char *table, *name, *type;
++	struct nft_handle *h = data;
+ 	enum nf_inet_hooks hook;
++	int i, prio;
+ 
+-	table = nftnl_chain_get(chain, NFTNL_CHAIN_TABLE);
+-	name = nftnl_chain_get(chain, NFTNL_CHAIN_NAME);
+-	type = nftnl_chain_get(chain, NFTNL_CHAIN_TYPE);
+-	prio = nftnl_chain_get_u32(chain, NFTNL_CHAIN_PRIO);
+-	hook = nftnl_chain_get_u32(chain, NFTNL_CHAIN_HOOKNUM);
++	if (!nft_chain_builtin(c))
++		return 0;
+ 
++	/* find chain's table in builtin tables */
++	table = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+ 	for (i = 0; i < NFT_TABLE_MAX; i++) {
+-		cur_table = h->tables[i].name;
+-		chains = h->tables[i].chains;
++		const char *cur_table = h->tables[i].name;
+ 
+-		if (!cur_table || strcmp(table, cur_table) != 0)
++		if (!cur_table || strcmp(cur_table, table))
+ 			continue;
+ 
+-		for (j = 0; j < NF_INET_NUMHOOKS && chains[j].name; j++) {
+-			if (strcmp(name, chains[j].name) != 0)
+-				continue;
+-
+-			if (strcmp(type, chains[j].type) == 0 &&
+-			    prio == chains[j].prio &&
+-			    hook == chains[j].hook)
+-				return 0;
+-			break;
+-		}
++		chains = h->tables[i].chains;
++		break;
+ 	}
+-
+-	return 1;
+-}
+-
+-static int nft_are_chains_compatible(struct nft_handle *h, const char *tablename)
+-{
+-	struct nftnl_chain_list *list;
+-	struct nftnl_chain_list_iter *iter;
+-	struct nftnl_chain *chain;
+-	int ret = 0;
+-
+-	list = nft_chain_list_get(h, tablename);
+-	if (list == NULL)
+-		return -1;
+-
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
++	if (!chains)
+ 		return -1;
+ 
+-	chain = nftnl_chain_list_iter_next(iter);
+-	while (chain != NULL) {
+-		if (!nft_chain_builtin(chain))
+-			goto next;
++	/* find chain in builtin chain list */
++	name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
++	for (i = 0; i < NF_INET_NUMHOOKS && chains[i].name; i++) {
++		if (strcmp(name, chains[i].name))
++			continue;
+ 
+-		ret = nft_is_chain_compatible(h, chain);
+-		if (ret != 0)
+-			break;
+-next:
+-		chain = nftnl_chain_list_iter_next(iter);
++		chain = &chains[i];
++		break;
+ 	}
++	if (!chain)
++		return -1;
+ 
+-	nftnl_chain_list_iter_destroy(iter);
++	/* compare properties */
++	type = nftnl_chain_get_str(c, NFTNL_CHAIN_TYPE);
++	prio = nftnl_chain_get_u32(c, NFTNL_CHAIN_PRIO);
++	hook = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM);
++	if (strcmp(type, chain->type) ||
++	    prio != chain->prio ||
++	    hook != chain->hook)
++		return -1;
+ 
+-	return ret;
++	return 0;
+ }
+ 
+ bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
+ {
++	struct nft_is_rule_compatible_data rcd = {
++		.tablename = tablename
++	};
++	struct nftnl_chain_list *clist;
+ 	struct nftnl_rule_list *list;
+-	struct nftnl_rule_list_iter *iter;
+-	struct nftnl_rule *rule;
+-	int ret = 0;
+ 
+-	if (!nft_table_builtin_find(h, tablename))
++	clist = nft_chain_list_get(h, tablename);
++	if (clist == NULL)
+ 		return false;
+ 
+-	ret = nft_are_chains_compatible(h, tablename);
+-	if (ret != 0)
++	if (nftnl_chain_list_foreach(clist, nft_is_chain_compatible, h))
+ 		return false;
+ 
+ 	list = nft_rule_list_get(h);
+ 	if (list == NULL)
+ 		return true;
+ 
+-	iter = nftnl_rule_list_iter_create(list);
+-	if (iter == NULL)
+-		return true;
+-
+-	rule = nftnl_rule_list_iter_next(iter);
+-	while (rule != NULL) {
+-		const char *table = nftnl_rule_get_str(rule, NFTNL_RULE_TABLE);
+-
+-		if (strcmp(table, tablename))
+-			goto next_rule;
+-
+-		ret = nft_is_rule_compatible(rule);
+-		if (ret != 0)
+-			break;
+-next_rule:
+-		rule = nftnl_rule_list_iter_next(iter);
+-	}
++	if (nftnl_rule_list_foreach(list, nft_is_rule_compatible, &rcd))
++		return false;
+ 
+-	nftnl_rule_list_iter_destroy(iter);
+-	return ret == 0;
++	return true;
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/0014-nft-Reduce-__nft_rule_del-signature.patch b/SOURCES/0014-nft-Reduce-__nft_rule_del-signature.patch
new file mode 100644
index 0000000..6bf043a
--- /dev/null
+++ b/SOURCES/0014-nft-Reduce-__nft_rule_del-signature.patch
@@ -0,0 +1,51 @@
+From 882a0b623fe352f1b25a8fc7652dfc9e2083549c Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:06 +0100
+Subject: [PATCH] nft: Reduce __nft_rule_del() signature
+
+The function does not use passed struct nftnl_rule_list, so remove it
+from its parameters.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 9975b6da9f926994bcea8ae788e47aab4b5b235e)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 5032c718b33a9..befd9f4dd9026 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1946,8 +1946,7 @@ void nft_table_new(struct nft_handle *h, const char *table)
+ 		nft_xt_builtin_init(h, table);
+ }
+ 
+-static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule_list *list,
+-			  struct nftnl_rule *r)
++static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
+ {
+ 	int ret;
+ 
+@@ -2046,7 +2045,7 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
+ 
+ 	r = nft_rule_find(h, list, chain, table, data, -1);
+ 	if (r != NULL) {
+-		ret =__nft_rule_del(h, list, r);
++		ret =__nft_rule_del(h, r);
+ 		if (ret < 0)
+ 			errno = ENOMEM;
+ 		if (verbose)
+@@ -2151,7 +2150,7 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
+ 	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
+ 	if (r != NULL) {
+ 		DEBUGP("deleting rule by number %d\n", rulenum);
+-		ret = __nft_rule_del(h, list, r);
++		ret = __nft_rule_del(h, r);
+ 		if (ret < 0)
+ 			errno = ENOMEM;
+ 	} else
+-- 
+2.20.1
+
diff --git a/SOURCES/0015-nft-Reduce-indenting-level-in-flush_chain_cache.patch b/SOURCES/0015-nft-Reduce-indenting-level-in-flush_chain_cache.patch
new file mode 100644
index 0000000..3ee9d68
--- /dev/null
+++ b/SOURCES/0015-nft-Reduce-indenting-level-in-flush_chain_cache.patch
@@ -0,0 +1,60 @@
+From f9932edff18a74dc373c708f38fe95b2f8d9a8a5 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:07 +0100
+Subject: [PATCH] nft: Reduce indenting level in flush_chain_cache()
+
+Instead of doing all in one go, make two separate decisions:
+
+1) If table has no chain cache, either continue or return depending on
+   whether we're flushing for a specific table.
+
+2) With chain cache present, flushing strategy once more depends on
+   whether we're flushing for a specific table: If given, just remove
+   all rules and return. If not, free the cache and set to NULL (so that
+   it will be repopulated later), then continue the loop.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit d4b0d248cc057e39608c7c1c1203dd3f1ea96645)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 22 +++++++++++++---------
+ 1 file changed, 13 insertions(+), 9 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index befd9f4dd9026..997d7bc58fd00 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -815,16 +815,20 @@ static void flush_chain_cache(struct nft_handle *h, const char *tablename)
+ 		if (tablename && strcmp(h->tables[i].name, tablename))
+ 			continue;
+ 
+-		if (h->table[i].chain_cache) {
+-			if (tablename) {
+-				nftnl_chain_list_foreach(h->table[i].chain_cache,
+-							 __flush_chain_cache, NULL);
+-				break;
+-			} else {
+-				nftnl_chain_list_free(h->table[i].chain_cache);
+-				h->table[i].chain_cache = NULL;
+-			}
++		if (!h->table[i].chain_cache) {
++			if (tablename)
++				return;
++			continue;
+ 		}
++
++		if (tablename) {
++			nftnl_chain_list_foreach(h->table[i].chain_cache,
++						 __flush_chain_cache, NULL);
++			return;
++		}
++
++		nftnl_chain_list_free(h->table[i].chain_cache);
++		h->table[i].chain_cache = NULL;
+ 	}
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0016-nft-Simplify-per-table-chain-cache-update.patch b/SOURCES/0016-nft-Simplify-per-table-chain-cache-update.patch
new file mode 100644
index 0000000..e9f0165
--- /dev/null
+++ b/SOURCES/0016-nft-Simplify-per-table-chain-cache-update.patch
@@ -0,0 +1,86 @@
+From 64c11057e8002896c74d10b21c64ab5796def01a Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:08 +0100
+Subject: [PATCH] nft: Simplify per table chain cache update
+
+Previously, each table's chain cache was potentially unallocated until
+nftnl_chain_list_cb() saw a chain for it. This means such callback had to
+check the chain_cache pointer for each chain belonging to that table.
+
+In addition to the above, nft_chain_list_get() had to cover for the
+possibility that a given table didn't have any chains at all in kernel,
+so check requested table's chain cache once more and allocate it if
+NULL.
+
+Instead, simply iterate over all tables and preallocate their chain
+caches prior to requesting the chain list from kernel. The only caveat
+is to flush the chain cache completely before retrying in case of EINTR.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 402dac2190e6011d4f4ad81c2992b7126b3d79d9)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 997d7bc58fd00..7d08a0884adde 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1286,12 +1286,6 @@ static int nftnl_chain_list_cb(const struct nlmsghdr *nlh, void *data)
+ 	if (!t)
+ 		goto out;
+ 
+-	if (!h->table[t->type].chain_cache) {
+-		h->table[t->type].chain_cache = nftnl_chain_list_alloc();
+-		if (!h->table[t->type].chain_cache)
+-			goto out;
+-	}
+-
+ 	nftnl_chain_list_add_tail(c, h->table[t->type].chain_cache);
+ 
+ 	return MNL_CB_OK;
+@@ -1307,7 +1301,7 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ 	char buf[16536];
+ 	struct nlmsghdr *nlh;
+ 	const struct builtin_table *t;
+-	int ret;
++	int i, ret;
+ 
+ 	t = nft_table_builtin_find(h, table);
+ 	if (!t)
+@@ -1316,18 +1310,27 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ 	if (h->table[t->type].chain_cache)
+ 		return h->table[t->type].chain_cache;
+ retry:
++	for (i = 0; i < NFT_TABLE_MAX; i++) {
++		enum nft_table_type type = h->tables[i].type;
++
++		if (!h->tables[i].name)
++			continue;
++
++		h->table[type].chain_cache = nftnl_chain_list_alloc();
++		if (!h->table[type].chain_cache)
++			return NULL;
++	}
++
+ 	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);
++		flush_chain_cache(h, NULL);
+ 		goto retry;
+ 	}
+ 
+-	if (!h->table[t->type].chain_cache)
+-		h->table[t->type].chain_cache = nftnl_chain_list_alloc();
+-
+ 	return h->table[t->type].chain_cache;
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0017-nft-Simplify-nft_rule_insert-a-bit.patch b/SOURCES/0017-nft-Simplify-nft_rule_insert-a-bit.patch
new file mode 100644
index 0000000..acc307c
--- /dev/null
+++ b/SOURCES/0017-nft-Simplify-nft_rule_insert-a-bit.patch
@@ -0,0 +1,49 @@
+From c10f5977915b1b7ddfb6557f9f6b18cd4140c26b Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:09 +0100
+Subject: [PATCH] nft: Simplify nft_rule_insert() a bit
+
+Fetch rule list right on top instead of in each branch separately.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 88bd4f28878bc7d41daa23098d68bf1bf6f5cea2)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 7d08a0884adde..469448f42cd6d 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2091,8 +2091,8 @@ nft_rule_add(struct nft_handle *h, const char *chain,
+ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 		    const char *table, void *data, int rulenum, bool verbose)
+ {
++	struct nftnl_rule_list *list = nft_rule_list_get(h);
+ 	struct nftnl_rule *r, *new_rule;
+-	struct nftnl_rule_list *list;
+ 	uint64_t handle = 0;
+ 
+ 	/* If built-in chains don't exist for this table, create them */
+@@ -2102,7 +2102,6 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 	nft_fn = nft_rule_insert;
+ 
+ 	if (rulenum > 0) {
+-		list = nft_rule_list_get(h);
+ 		if (list == NULL)
+ 			goto err;
+ 
+@@ -2123,8 +2122,6 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 
+ 		handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
+ 		DEBUGP("adding after rule handle %"PRIu64"\n", handle);
+-	} else {
+-		nft_rule_list_get(h);
+ 	}
+ 
+ 	new_rule = nft_rule_add(h, chain, table, data, handle, verbose);
+-- 
+2.20.1
+
diff --git a/SOURCES/0018-nft-Introduce-fetch_chain_cache.patch b/SOURCES/0018-nft-Introduce-fetch_chain_cache.patch
new file mode 100644
index 0000000..eac4624
--- /dev/null
+++ b/SOURCES/0018-nft-Introduce-fetch_chain_cache.patch
@@ -0,0 +1,76 @@
+From 8fc923734c2a393a377c898b3f4c6db776745838 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:10 +0100
+Subject: [PATCH] nft: Introduce fetch_chain_cache()
+
+Move chain cache population from nft_chain_list_get() into a dedicated
+function.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 8bae620abf9ac81794acca43d305ca74f15a13ff)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 27 +++++++++++++++++----------
+ 1 file changed, 17 insertions(+), 10 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 469448f42cd6d..b425577798679 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1295,20 +1295,12 @@ err:
+ 	return MNL_CB_OK;
+ }
+ 
+-struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+-					    const char *table)
++static int fetch_chain_cache(struct nft_handle *h)
+ {
+ 	char buf[16536];
+ 	struct nlmsghdr *nlh;
+-	const struct builtin_table *t;
+ 	int i, ret;
+ 
+-	t = nft_table_builtin_find(h, table);
+-	if (!t)
+-		return NULL;
+-
+-	if (h->table[t->type].chain_cache)
+-		return h->table[t->type].chain_cache;
+ retry:
+ 	for (i = 0; i < NFT_TABLE_MAX; i++) {
+ 		enum nft_table_type type = h->tables[i].type;
+@@ -1318,7 +1310,7 @@ retry:
+ 
+ 		h->table[type].chain_cache = nftnl_chain_list_alloc();
+ 		if (!h->table[type].chain_cache)
+-			return NULL;
++			return -1;
+ 	}
+ 
+ 	nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, h->family,
+@@ -1331,6 +1323,21 @@ retry:
+ 		goto retry;
+ 	}
+ 
++	return ret;
++}
++
++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;
++
++	if (!h->table[t->type].chain_cache)
++		fetch_chain_cache(h);
++
+ 	return h->table[t->type].chain_cache;
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0019-nft-Move-nft_rule_list_get-above-nft_chain_list_get.patch b/SOURCES/0019-nft-Move-nft_rule_list_get-above-nft_chain_list_get.patch
new file mode 100644
index 0000000..e34fa1c
--- /dev/null
+++ b/SOURCES/0019-nft-Move-nft_rule_list_get-above-nft_chain_list_get.patch
@@ -0,0 +1,149 @@
+From 510fef3a3fe67feb3da2fb237784299c7f070d70 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:11 +0100
+Subject: [PATCH] nft: Move nft_rule_list_get() above nft_chain_list_get()
+
+Later when introducing per chain rule caches, nft_rule_list_get() will
+be removed. But nftnl_rule_list_cb() which it uses will be reused to
+update each chain's rule cache from inside nftnl_chain_list_get(), so
+move both into position.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit eb13831f1c41c0efa626ab85d4448fb8ce4c87a2)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 110 ++++++++++++++++++++++++-------------------------
+ 1 file changed, 55 insertions(+), 55 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index b425577798679..1840561f2e531 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1326,61 +1326,6 @@ retry:
+ 	return ret;
+ }
+ 
+-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;
+-
+-	if (!h->table[t->type].chain_cache)
+-		fetch_chain_cache(h);
+-
+-	return h->table[t->type].chain_cache;
+-}
+-
+-static const char *policy_name[NF_ACCEPT+1] = {
+-	[NF_DROP] = "DROP",
+-	[NF_ACCEPT] = "ACCEPT",
+-};
+-
+-int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
+-{
+-	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;
+-
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *policy = NULL;
+-
+-		if (nft_chain_builtin(c)) {
+-			uint32_t pol = NF_ACCEPT;
+-
+-			if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
+-				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+-			policy = policy_name[pol];
+-		}
+-
+-		if (ops->save_chain)
+-			ops->save_chain(c, policy);
+-
+-		c = nftnl_chain_list_iter_next(iter);
+-	}
+-
+-	nftnl_chain_list_iter_destroy(iter);
+-
+-	return 1;
+-}
+-
+ static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+ {
+ 	struct nftnl_rule *r;
+@@ -1437,6 +1382,61 @@ retry:
+ 	return list;
+ }
+ 
++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;
++
++	if (!h->table[t->type].chain_cache)
++		fetch_chain_cache(h);
++
++	return h->table[t->type].chain_cache;
++}
++
++static const char *policy_name[NF_ACCEPT+1] = {
++	[NF_DROP] = "DROP",
++	[NF_ACCEPT] = "ACCEPT",
++};
++
++int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
++{
++	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;
++
++	c = nftnl_chain_list_iter_next(iter);
++	while (c != NULL) {
++		const char *policy = NULL;
++
++		if (nft_chain_builtin(c)) {
++			uint32_t pol = NF_ACCEPT;
++
++			if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
++				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
++			policy = policy_name[pol];
++		}
++
++		if (ops->save_chain)
++			ops->save_chain(c, policy);
++
++		c = nftnl_chain_list_iter_next(iter);
++	}
++
++	nftnl_chain_list_iter_destroy(iter);
++
++	return 1;
++}
++
+ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
+ {
+ 	struct nftnl_rule_list *list;
+-- 
+2.20.1
+
diff --git a/SOURCES/0020-xtables-Implement-per-chain-rule-cache.patch b/SOURCES/0020-xtables-Implement-per-chain-rule-cache.patch
new file mode 100644
index 0000000..499b4bd
--- /dev/null
+++ b/SOURCES/0020-xtables-Implement-per-chain-rule-cache.patch
@@ -0,0 +1,802 @@
+From 4c6357a568d3e079fa7246cc2aa1c46739a6a14c Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:12 +0100
+Subject: [PATCH] xtables: Implement per chain rule cache
+
+Use recently introduced support for rules inside chains in libnftnl to
+introduce a rule cache per chain instead of a global one.
+
+A tricky bit is to decide if cache should be updated or not. Previously,
+the global rule cache was populated just once and then reused unless
+being flushed completely (via call to flush_rule_cache() with
+NULL-pointer table argument). Resemble this behaviour by introducing a
+boolean indicating cache status and fetch rules for all chains when
+updating the chain cache in nft_chain_list_get().
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 947c51c95edbbf08d6b3c105177ac5cfa238aade)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 386 +++++++++++++++++++++----------------------------
+ iptables/nft.h |   2 +-
+ 2 files changed, 166 insertions(+), 222 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 1840561f2e531..842ed2b805bee 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -772,28 +772,15 @@ int nft_init(struct nft_handle *h, const struct builtin_table *t)
+ 
+ static int __flush_rule_cache(struct nftnl_rule *r, void *data)
+ {
+-	const char *tablename = data;
+-
+-	if (!strcmp(nftnl_rule_get_str(r, NFTNL_RULE_TABLE), tablename)) {
+-		nftnl_rule_list_del(r);
+-		nftnl_rule_free(r);
+-	}
++	nftnl_rule_list_del(r);
++	nftnl_rule_free(r);
+ 
+ 	return 0;
+ }
+ 
+-static void flush_rule_cache(struct nft_handle *h, const char *tablename)
++static void flush_rule_cache(struct nftnl_chain *c)
+ {
+-	if (!h->rule_cache)
+-		return;
+-
+-	if (tablename) {
+-		nftnl_rule_list_foreach(h->rule_cache, __flush_rule_cache,
+-					(void *)tablename);
+-	} else {
+-		nftnl_rule_list_free(h->rule_cache);
+-		h->rule_cache = NULL;
+-	}
++	nftnl_rule_foreach(c, __flush_rule_cache, NULL);
+ }
+ 
+ static int __flush_chain_cache(struct nftnl_chain *c, void *data)
+@@ -830,12 +817,12 @@ static void flush_chain_cache(struct nft_handle *h, const char *tablename)
+ 		nftnl_chain_list_free(h->table[i].chain_cache);
+ 		h->table[i].chain_cache = NULL;
+ 	}
++	h->have_cache = false;
+ }
+ 
+ void nft_fini(struct nft_handle *h)
+ {
+ 	flush_chain_cache(h, NULL);
+-	flush_rule_cache(h, NULL);
+ 	mnl_socket_close(h->nl);
+ }
+ 
+@@ -1195,12 +1182,14 @@ err:
+ 	return NULL;
+ }
+ 
+-static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h);
++static struct nftnl_chain *
++nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
+ 
+ int
+ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ 		void *data, uint64_t handle, bool verbose)
+ {
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+ 	int type;
+ 
+@@ -1228,10 +1217,9 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ 	if (verbose)
+ 		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ 
+-	if (!nft_rule_list_get(h))
+-		return 0;
+-
+-	nftnl_rule_list_add_tail(r, h->rule_cache);
++	c = nft_chain_find(h, table, chain);
++	if (c)
++		nftnl_chain_rule_add_tail(r, c);
+ 
+ 	return 1;
+ }
+@@ -1328,58 +1316,75 @@ retry:
+ 
+ static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+ {
++	struct nftnl_chain *c = data;
+ 	struct nftnl_rule *r;
+-	struct nftnl_rule_list *list = data;
+ 
+ 	r = nftnl_rule_alloc();
+ 	if (r == NULL)
+-		goto err;
+-
+-	if (nftnl_rule_nlmsg_parse(nlh, r) < 0)
+-		goto out;
++		return MNL_CB_OK;
+ 
+-	nftnl_rule_list_add_tail(r, list);
++	if (nftnl_rule_nlmsg_parse(nlh, r) < 0) {
++		nftnl_rule_free(r);
++		return MNL_CB_OK;
++	}
+ 
+-	return MNL_CB_OK;
+-out:
+-	nftnl_rule_free(r);
+-	nftnl_rule_list_free(list);
+-err:
++	nftnl_chain_rule_add_tail(r, c);
+ 	return MNL_CB_OK;
+ }
+ 
+-static struct nftnl_rule_list *nft_rule_list_get(struct nft_handle *h)
++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_list *list;
++	struct nftnl_rule *rule;
+ 	int ret;
+ 
+-	if (h->rule_cache)
+-		return h->rule_cache;
++	rule = nftnl_rule_alloc();
++	if (!rule)
++		return -1;
+ 
+-retry:
+-	list = nftnl_rule_list_alloc();
+-	if (list == NULL)
+-		return 0;
++	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));
+ 
++retry:
+ 	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, list);
++	ret = mnl_talk(h, nlh, nftnl_rule_list_cb, c);
+ 	if (ret < 0) {
++		flush_rule_cache(c);
++
+ 		if (errno == EINTR) {
+ 			assert(nft_restart(h) >= 0);
+-			nftnl_rule_list_free(list);
+ 			goto retry;
+ 		}
+-
+-		nftnl_rule_list_free(list);
+-		return NULL;
++		nftnl_rule_free(rule);
++		return -1;
+ 	}
+ 
+-	h->rule_cache = list;
+-	return list;
++	nftnl_rule_free(rule);
++	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->table[type].chain_cache,
++					     nft_rule_list_update, h))
++			return -1;
++	}
++	return 0;
+ }
+ 
+ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+@@ -1391,8 +1396,11 @@ struct nftnl_chain_list *nft_chain_list_get(struct nft_handle *h,
+ 	if (!t)
+ 		return NULL;
+ 
+-	if (!h->table[t->type].chain_cache)
++	if (!h->have_cache) {
+ 		fetch_chain_cache(h);
++		fetch_rule_cache(h);
++		h->have_cache = true;
++	}
+ 
+ 	return h->table[t->type].chain_cache;
+ }
+@@ -1437,38 +1445,54 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
+ 	return 1;
+ }
+ 
+-int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
++static int nft_chain_save_rules(struct nft_handle *h,
++				struct nftnl_chain *c, unsigned int format)
+ {
+-	struct nftnl_rule_list *list;
+-	struct nftnl_rule_list_iter *iter;
++	struct nftnl_rule_iter *iter;
+ 	struct nftnl_rule *r;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
+-		return 0;
+-
+-	iter = nftnl_rule_list_iter_create(list);
++	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+-		return 0;
++		return 1;
+ 
+-	r = nftnl_rule_list_iter_next(iter);
++	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+-		const char *rule_table =
+-			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
++		nft_rule_print_save(r, NFT_RULE_APPEND, format);
++		r = nftnl_rule_iter_next(iter);
++	}
+ 
+-		if (strcmp(table, rule_table) != 0)
+-			goto next;
++	nftnl_rule_iter_destroy(iter);
++	return 0;
++}
+ 
+-		nft_rule_print_save(r, NFT_RULE_APPEND, format);
++int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
++{
++	struct nftnl_chain_list_iter *iter;
++	struct nftnl_chain_list *list;
++	struct nftnl_chain *c;
++	int ret = 0;
+ 
+-next:
+-		r = nftnl_rule_list_iter_next(iter);
++	list = nft_chain_list_get(h, table);
++	if (!list)
++		return 0;
++
++	iter = nftnl_chain_list_iter_create(list);
++	if (!iter)
++		return 0;
++
++	c = nftnl_chain_list_iter_next(iter);
++	while (c) {
++		ret = nft_chain_save_rules(h, c, format);
++		if (ret != 0)
++			break;
++
++		c = nftnl_chain_list_iter_next(iter);
+ 	}
+ 
+-	nftnl_rule_list_iter_destroy(iter);
++	nftnl_chain_list_iter_destroy(iter);
+ 
+ 	/* the core expects 1 for success and 0 for error */
+-	return 1;
++	return ret == 0 ? 1 : 0;
+ }
+ 
+ static void
+@@ -1567,6 +1591,7 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
+ 			fprintf(stdout, "Flushing chain `%s'\n", chain_name);
+ 
+ 		__nft_rule_flush(h, table, chain_name);
++		flush_rule_cache(c);
+ 
+ 		if (chain != NULL)
+ 			break;
+@@ -1574,7 +1599,6 @@ next:
+ 		c = nftnl_chain_list_iter_next(iter);
+ 	}
+ 	nftnl_chain_list_iter_destroy(iter);
+-	flush_rule_cache(h, table);
+ err:
+ 	/* the core expects 1 for success and 0 for error */
+ 	return ret == 0 ? 1 : 0;
+@@ -1898,7 +1922,6 @@ static int __nft_table_flush(struct nft_handle *h, const char *table)
+ 	h->table[_t->type].initialized = false;
+ 
+ 	flush_chain_cache(h, table);
+-	flush_rule_cache(h, table);
+ 
+ 	return 0;
+ }
+@@ -1939,12 +1962,6 @@ next:
+ 		t = nftnl_table_list_iter_next(iter);
+ 	}
+ 
+-	if (!h->rule_cache) {
+-		h->rule_cache = nftnl_rule_list_alloc();
+-		if (h->rule_cache == NULL)
+-			return -1;
+-	}
+-
+ err_table_iter:
+ 	nftnl_table_list_iter_destroy(iter);
+ err_table_list:
+@@ -1975,31 +1992,19 @@ static int __nft_rule_del(struct nft_handle *h, struct nftnl_rule *r)
+ }
+ 
+ static struct nftnl_rule *
+-nft_rule_find(struct nft_handle *h, struct nftnl_rule_list *list,
+-	      const char *chain, const char *table, void *data, int rulenum)
++nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulenum)
+ {
+ 	struct nftnl_rule *r;
+-	struct nftnl_rule_list_iter *iter;
++	struct nftnl_rule_iter *iter;
+ 	int rule_ctr = 0;
+ 	bool found = false;
+ 
+-	iter = nftnl_rule_list_iter_create(list);
++	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return 0;
+ 
+-	r = nftnl_rule_list_iter_next(iter);
++	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+-		const char *rule_table =
+-			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+-		const char *rule_chain =
+-			nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+-
+-		if (strcmp(table, rule_table) != 0 ||
+-		    strcmp(chain, rule_chain) != 0) {
+-			DEBUGP("different chain / table\n");
+-			goto next;
+-		}
+-
+ 		if (rulenum >= 0) {
+ 			/* Delete by rule number case */
+ 			if (rule_ctr == rulenum) {
+@@ -2012,11 +2017,10 @@ nft_rule_find(struct nft_handle *h, struct nftnl_rule_list *list,
+ 				break;
+ 		}
+ 		rule_ctr++;
+-next:
+-		r = nftnl_rule_list_iter_next(iter);
++		r = nftnl_rule_iter_next(iter);
+ 	}
+ 
+-	nftnl_rule_list_iter_destroy(iter);
++	nftnl_rule_iter_destroy(iter);
+ 
+ 	return found ? r : NULL;
+ }
+@@ -2024,16 +2028,16 @@ next:
+ int nft_rule_check(struct nft_handle *h, const char *chain,
+ 		   const char *table, void *data, bool verbose)
+ {
+-	struct nftnl_rule_list *list;
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+ 
+ 	nft_fn = nft_rule_check;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
++	c = nft_chain_find(h, table, chain);
++	if (!c)
+ 		return 0;
+ 
+-	r = nft_rule_find(h, list, chain, table, data, -1);
++	r = nft_rule_find(h, c, data, -1);
+ 	if (r == NULL) {
+ 		errno = ENOENT;
+ 		return 0;
+@@ -2048,16 +2052,18 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
+ 		    const char *table, void *data, bool verbose)
+ {
+ 	int ret = 0;
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+-	struct nftnl_rule_list *list;
+ 
+ 	nft_fn = nft_rule_delete;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
++	c = nft_chain_find(h, table, chain);
++	if (!c) {
++		errno = ENOENT;
+ 		return 0;
++	}
+ 
+-	r = nft_rule_find(h, list, chain, table, data, -1);
++	r = nft_rule_find(h, c, data, -1);
+ 	if (r != NULL) {
+ 		ret =__nft_rule_del(h, r);
+ 		if (ret < 0)
+@@ -2098,8 +2104,8 @@ nft_rule_add(struct nft_handle *h, const char *chain,
+ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 		    const char *table, void *data, int rulenum, bool verbose)
+ {
+-	struct nftnl_rule_list *list = nft_rule_list_get(h);
+ 	struct nftnl_rule *r, *new_rule;
++	struct nftnl_chain *c;
+ 	uint64_t handle = 0;
+ 
+ 	/* If built-in chains don't exist for this table, create them */
+@@ -2108,17 +2114,19 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 
+ 	nft_fn = nft_rule_insert;
+ 
+-	if (rulenum > 0) {
+-		if (list == NULL)
+-			goto err;
++	c = nft_chain_find(h, table, chain);
++	if (!c) {
++		errno = ENOENT;
++		goto err;
++	}
+ 
+-		r = nft_rule_find(h, list, chain, table, data, rulenum);
++	if (rulenum > 0) {
++		r = nft_rule_find(h, c, data, rulenum);
+ 		if (r == NULL) {
+ 			/* special case: iptables allows to insert into
+ 			 * rule_count + 1 position.
+ 			 */
+-			r = nft_rule_find(h, list, chain, table, data,
+-					  rulenum - 1);
++			r = nft_rule_find(h, c, data, rulenum - 1);
+ 			if (r != NULL)
+ 				return nft_rule_append(h, chain, table, data,
+ 						       0, verbose);
+@@ -2136,9 +2144,9 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 		goto err;
+ 
+ 	if (handle)
+-		nftnl_rule_list_insert_at(new_rule, r);
++		nftnl_chain_rule_insert_at(new_rule, r);
+ 	else
+-		nftnl_rule_list_add(new_rule, h->rule_cache);
++		nftnl_chain_rule_add(new_rule, c);
+ 
+ 	return 1;
+ err:
+@@ -2149,16 +2157,18 @@ int nft_rule_delete_num(struct nft_handle *h, const char *chain,
+ 			const char *table, int rulenum, bool verbose)
+ {
+ 	int ret = 0;
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+-	struct nftnl_rule_list *list;
+ 
+ 	nft_fn = nft_rule_delete_num;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
++	c = nft_chain_find(h, table, chain);
++	if (!c) {
++		errno = ENOENT;
+ 		return 0;
++	}
+ 
+-	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
++	r = nft_rule_find(h, c, NULL, rulenum);
+ 	if (r != NULL) {
+ 		DEBUGP("deleting rule by number %d\n", rulenum);
+ 		ret = __nft_rule_del(h, r);
+@@ -2174,16 +2184,18 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
+ 		     const char *table, void *data, int rulenum, bool verbose)
+ {
+ 	int ret = 0;
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+-	struct nftnl_rule_list *list;
+ 
+ 	nft_fn = nft_rule_replace;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
++	c = nft_chain_find(h, table, chain);
++	if (!c) {
++		errno = ENOENT;
+ 		return 0;
++	}
+ 
+-	r = nft_rule_find(h, list, chain, table, data, rulenum);
++	r = nft_rule_find(h, c, data, rulenum);
+ 	if (r != NULL) {
+ 		DEBUGP("replacing rule with handle=%llu\n",
+ 			(unsigned long long)
+@@ -2201,35 +2213,21 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
+ }
+ 
+ static int
+-__nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
++__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))
+ {
+-	struct nftnl_rule_list *list;
+-	struct nftnl_rule_list_iter *iter;
++	struct nftnl_rule_iter *iter;
+ 	struct nftnl_rule *r;
+ 	int rule_ctr = 0;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
+-		return 0;
+-
+-	iter = nftnl_rule_list_iter_create(list);
++	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return 0;
+ 
+-	r = nftnl_rule_list_iter_next(iter);
++	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+-		const char *rule_table =
+-			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+-		const char *rule_chain =
+-			nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+-
+-		if (strcmp(table, rule_table) != 0 ||
+-		    strcmp(chain, rule_chain) != 0)
+-			goto next;
+-
+ 		rule_ctr++;
+ 
+ 		if (rulenum > 0 && rule_ctr != rulenum) {
+@@ -2242,46 +2240,30 @@ __nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 			break;
+ 
+ next:
+-		r = nftnl_rule_list_iter_next(iter);
++		r = nftnl_rule_iter_next(iter);
+ 	}
+ 
+-	nftnl_rule_list_iter_destroy(iter);
++	nftnl_rule_iter_destroy(iter);
+ 	return 1;
+ }
+ 
+-static int nft_rule_count(struct nft_handle *h,
+-			  const char *chain, const char *table)
++static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
+ {
+-	struct nftnl_rule_list_iter *iter;
+-	struct nftnl_rule_list *list;
++	struct nftnl_rule_iter *iter;
+ 	struct nftnl_rule *r;
+ 	int rule_ctr = 0;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
+-		return 0;
+-
+-	iter = nftnl_rule_list_iter_create(list);
++	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return 0;
+ 
+-	r = nftnl_rule_list_iter_next(iter);
++	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+-		const char *rule_table =
+-			nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+-		const char *rule_chain =
+-			nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+-
+-		if (strcmp(table, rule_table) != 0 ||
+-		    strcmp(chain, rule_chain) != 0)
+-			goto next;
+-
+ 		rule_ctr++;
+-next:
+-		r = nftnl_rule_list_iter_next(iter);
++		r = nftnl_rule_iter_next(iter);
+ 	}
+ 
+-	nftnl_rule_list_iter_destroy(iter);
++	nftnl_rule_iter_destroy(iter);
+ 	return rule_ctr;
+ }
+ 
+@@ -2314,8 +2296,11 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 	}
+ 
+ 	if (chain && rulenum) {
+-		__nft_rule_list(h, chain, table,
+-				rulenum, format, ops->print_rule);
++		c = nft_chain_find(h, table, chain);
++		if (!c)
++			return 0;
++
++		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
+ 		return 1;
+ 	}
+ 
+@@ -2358,12 +2343,11 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 		if (found)
+ 			printf("\n");
+ 
+-		entries = nft_rule_count(h, chain_name, table);
++		entries = nft_rule_count(h, c);
+ 		ops->print_header(format, chain_name, policy_name[policy],
+ 				  &ctrs, basechain, refs - entries, entries);
+ 
+-		__nft_rule_list(h, chain_name, table,
+-				rulenum, format, ops->print_rule);
++		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
+ 
+ 		found = true;
+ 
+@@ -2484,8 +2468,7 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 		if (chain && strcmp(chain, chain_name) != 0)
+ 			goto next;
+ 
+-		ret = __nft_rule_list(h, chain_name, table, rulenum,
+-				      format, list_save);
++		ret = __nft_rule_list(h, c, rulenum, format, list_save);
+ 
+ 		/* we printed the chain we wanted, stop processing. */
+ 		if (chain)
+@@ -2503,17 +2486,17 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
+ 			   const char *table, int rulenum)
+ {
+ 	struct iptables_command_state cs = {};
+-	struct nftnl_rule_list *list;
++	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+ 	int ret = 0;
+ 
+ 	nft_fn = nft_rule_delete;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
++	c = nft_chain_find(h, table, chain);
++	if (!c)
+ 		return 0;
+ 
+-	r = nft_rule_find(h, list, chain, table, NULL, rulenum);
++	r = nft_rule_find(h, c, NULL, rulenum);
+ 	if (r == NULL) {
+ 		errno = ENOENT;
+ 		ret = 1;
+@@ -2982,38 +2965,19 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename,
+ static void nft_chain_zero_rule_counters(struct nft_handle *h,
+ 					 struct nftnl_chain *c)
+ {
+-	struct nftnl_rule_list_iter *iter;
+-	struct nftnl_rule_list *list;
+-	const char *table_name;
+-	const char *chain_name;
++	struct nftnl_rule_iter *iter;
+ 	struct nftnl_rule *r;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
+-		return;
+-	iter = nftnl_rule_list_iter_create(list);
++	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return;
+ 
+-	table_name = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+-	chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-
+-	r = nftnl_rule_list_iter_next(iter);
++	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+ 		struct nftnl_expr_iter *ei;
+-		const char *table_chain;
+-		const char *rule_chain;
+ 		struct nftnl_expr *e;
+ 		bool zero_needed;
+ 
+-		table_chain = nftnl_rule_get_str(r, NFTNL_RULE_TABLE);
+-		if (strcmp(table_chain, table_name))
+-			goto next;
+-
+-		rule_chain = nftnl_rule_get_str(r, NFTNL_RULE_CHAIN);
+-		if (strcmp(rule_chain, chain_name))
+-			goto next;
+-
+ 		ei = nftnl_expr_iter_create(r);
+ 		if (!ei)
+ 			break;
+@@ -3044,11 +3008,10 @@ static void nft_chain_zero_rule_counters(struct nft_handle *h,
+ 			nftnl_rule_unset(r, NFTNL_RULE_POSITION);
+ 			batch_rule_add(h, NFT_COMPAT_RULE_REPLACE, r);
+ 		}
+-next:
+-		r = nftnl_rule_list_iter_next(iter);
++		r = nftnl_rule_iter_next(iter);
+ 	}
+ 
+-	nftnl_rule_list_iter_destroy(iter);
++	nftnl_rule_iter_destroy(iter);
+ }
+ 
+ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
+@@ -3143,19 +3106,8 @@ static int nft_is_expr_compatible(struct nftnl_expr *expr, void *data)
+ 	return -1;
+ }
+ 
+-struct nft_is_rule_compatible_data {
+-	const char *tablename;
+-};
+-
+ static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
+ {
+-	const char *table = nftnl_rule_get_str(rule, NFTNL_RULE_TABLE);
+-	struct nft_is_rule_compatible_data *d = data;
+-
+-	/* ignore rules belonging to a different table */
+-	if (strcmp(table, d->tablename))
+-		return 0;
+-
+ 	return nftnl_expr_foreach(rule, nft_is_expr_compatible, NULL);
+ }
+ 
+@@ -3167,6 +3119,9 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+ 	enum nf_inet_hooks hook;
+ 	int i, prio;
+ 
++	if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
++		return -1;
++
+ 	if (!nft_chain_builtin(c))
+ 		return 0;
+ 
+@@ -3210,11 +3165,7 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+ 
+ bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
+ {
+-	struct nft_is_rule_compatible_data rcd = {
+-		.tablename = tablename
+-	};
+ 	struct nftnl_chain_list *clist;
+-	struct nftnl_rule_list *list;
+ 
+ 	clist = nft_chain_list_get(h, tablename);
+ 	if (clist == NULL)
+@@ -3223,12 +3174,5 @@ bool nft_is_table_compatible(struct nft_handle *h, const char *tablename)
+ 	if (nftnl_chain_list_foreach(clist, nft_is_chain_compatible, h))
+ 		return false;
+ 
+-	list = nft_rule_list_get(h);
+-	if (list == NULL)
+-		return true;
+-
+-	if (nftnl_rule_list_foreach(list, nft_is_rule_compatible, &rcd))
+-		return false;
+-
+ 	return true;
+ }
+diff --git a/iptables/nft.h b/iptables/nft.h
+index bf60ab3943659..6568257feddc7 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -42,7 +42,7 @@ struct nft_handle {
+ 		struct nftnl_chain_list *chain_cache;
+ 		bool			initialized;
+ 	} table[NFT_TABLE_MAX];
+-	struct nftnl_rule_list	*rule_cache;
++	bool			have_cache;
+ 	bool			restore;
+ 	int8_t			config_done;
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0021-nft-Drop-nft_chain_list_find.patch b/SOURCES/0021-nft-Drop-nft_chain_list_find.patch
new file mode 100644
index 0000000..534482b
--- /dev/null
+++ b/SOURCES/0021-nft-Drop-nft_chain_list_find.patch
@@ -0,0 +1,89 @@
+From 6e0de300158a6f3e8151903952b2a95f4487bedc Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:13 +0100
+Subject: [PATCH] nft: Drop nft_chain_list_find()
+
+Replace the function by nftnl_chain_list_lookup_byname() as provided by
+libnftnl.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 63dc7a0d86a1b86b10c5e04dd910497b9d8fcfaf)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 31 ++-----------------------------
+ iptables/nft.h |  2 --
+ 2 files changed, 2 insertions(+), 31 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 842ed2b805bee..883fb3db2c671 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -697,7 +697,7 @@ static void nft_chain_builtin_init(struct nft_handle *h,
+ 	/* Initialize built-in chains if they don't exist yet */
+ 	for (i=0; i < NF_INET_NUMHOOKS && table->chains[i].name != NULL; i++) {
+ 
+-		c = nft_chain_list_find(list, table->chains[i].name);
++		c = nftnl_chain_list_lookup_byname(list, table->chains[i].name);
+ 		if (c != NULL)
+ 			continue;
+ 
+@@ -1699,33 +1699,6 @@ err:
+ 	return ret == 0 ? 1 : 0;
+ }
+ 
+-struct nftnl_chain *
+-nft_chain_list_find(struct nftnl_chain_list *list, const char *chain)
+-{
+-	struct nftnl_chain_list_iter *iter;
+-	struct nftnl_chain *c;
+-
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
+-		return NULL;
+-
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-
+-		if (strcmp(chain, chain_name) != 0)
+-			goto next;
+-
+-		nftnl_chain_list_iter_destroy(iter);
+-		return c;
+-next:
+-		c = nftnl_chain_list_iter_next(iter);
+-	}
+-	nftnl_chain_list_iter_destroy(iter);
+-	return NULL;
+-}
+-
+ static struct nftnl_chain *
+ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+ {
+@@ -1735,7 +1708,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain)
+ 	if (list == NULL)
+ 		return NULL;
+ 
+-	return nft_chain_list_find(list, chain);
++	return nftnl_chain_list_lookup_byname(list, chain);
+ }
+ 
+ bool nft_chain_exists(struct nft_handle *h,
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 6568257feddc7..dfdffd69342db 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -83,8 +83,6 @@ 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);
+-struct nftnl_chain *nft_chain_list_find(struct nftnl_chain_list *list,
+-					const char *chain);
+ 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);
+-- 
+2.20.1
+
diff --git a/SOURCES/0022-xtables-Optimize-flushing-a-specific-chain.patch b/SOURCES/0022-xtables-Optimize-flushing-a-specific-chain.patch
new file mode 100644
index 0000000..c8cac98
--- /dev/null
+++ b/SOURCES/0022-xtables-Optimize-flushing-a-specific-chain.patch
@@ -0,0 +1,85 @@
+From 96b7b6eb3c963dc835a3f132f037050d032aaa77 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:14 +0100
+Subject: [PATCH] xtables: Optimize flushing a specific chain
+
+If a chain name is given to nft_rule_flush(), make use of
+nftnl_chain_list_lookup_byname().
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 196841c9597eff536b59655b60df088ee1929904)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 30 +++++++++++++++++-------------
+ 1 file changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 883fb3db2c671..a23acbcc9b100 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1496,10 +1496,14 @@ int nft_rule_save(struct nft_handle *h, const char *table, unsigned int format)
+ }
+ 
+ static void
+-__nft_rule_flush(struct nft_handle *h, const char *table, const char *chain)
++__nft_rule_flush(struct nft_handle *h, const char *table,
++		 const char *chain, bool verbose)
+ {
+ 	struct nftnl_rule *r;
+ 
++	if (verbose)
++		fprintf(stdout, "Flushing chain `%s'\n", chain);
++
+ 	r = nftnl_rule_alloc();
+ 	if (r == NULL)
+ 		return;
+@@ -1533,7 +1537,7 @@ static int __nft_chain_user_flush(struct nftnl_chain *c, void *data)
+ 		return 0;
+ 
+ 	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM))
+-		__nft_rule_flush(h, table, chain);
++		__nft_rule_flush(h, table, chain, false);
+ 
+ 	return 0;
+ }
+@@ -1573,6 +1577,16 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
+ 		goto err;
+ 	}
+ 
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
++		if (!c)
++			return 0;
++
++		__nft_rule_flush(h, table, chain, verbose);
++		flush_rule_cache(c);
++		return 1;
++	}
++
+ 	iter = nftnl_chain_list_iter_create(list);
+ 	if (iter == NULL) {
+ 		ret = 1;
+@@ -1584,18 +1598,8 @@ int nft_rule_flush(struct nft_handle *h, const char *chain, const char *table,
+ 		const char *chain_name =
+ 			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+ 
+-		if (chain != NULL && strcmp(chain, chain_name) != 0)
+-			goto next;
+-
+-		if (verbose)
+-			fprintf(stdout, "Flushing chain `%s'\n", chain_name);
+-
+-		__nft_rule_flush(h, table, chain_name);
++		__nft_rule_flush(h, table, chain_name, verbose);
+ 		flush_rule_cache(c);
+-
+-		if (chain != NULL)
+-			break;
+-next:
+ 		c = nftnl_chain_list_iter_next(iter);
+ 	}
+ 	nftnl_chain_list_iter_destroy(iter);
+-- 
+2.20.1
+
diff --git a/SOURCES/0023-xtables-Optimize-nft_chain_zero_counters.patch b/SOURCES/0023-xtables-Optimize-nft_chain_zero_counters.patch
new file mode 100644
index 0000000..ea36fd0
--- /dev/null
+++ b/SOURCES/0023-xtables-Optimize-nft_chain_zero_counters.patch
@@ -0,0 +1,136 @@
+From ecd80647d4b862324943ba8ecb40ae988a7e6376 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:15 +0100
+Subject: [PATCH] xtables: Optimize nft_chain_zero_counters()
+
+If a chain name was given, make use of nftnl_chain_list_lookup_byname().
+Streamline nft_chain_zero_rule_counters() to be suitable for calling
+from nftnl_chain_list_foreach().
+
+There is an unrelated optimization in here, too: Add batch job
+NFT_COMPAT_CHAIN_ZERO only if it is a base chain. Since user-defined
+chains don't have counters, there is no need to do anything for them.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit a6ce0c65d3a390bfff16e834c18650beedecf40c)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 72 +++++++++++++++++++++++++-------------------------
+ 1 file changed, 36 insertions(+), 36 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index a23acbcc9b100..9951bf3212197 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2939,15 +2939,36 @@ int nft_xtables_config_load(struct nft_handle *h, const char *filename,
+ 	return h->config_done;
+ }
+ 
+-static void nft_chain_zero_rule_counters(struct nft_handle *h,
+-					 struct nftnl_chain *c)
++struct chain_zero_data {
++	struct nft_handle	*handle;
++	bool			verbose;
++};
++
++static int __nft_chain_zero_counters(struct nftnl_chain *c, void *data)
+ {
++	struct chain_zero_data *d = data;
++	struct nft_handle *h = d->handle;
+ 	struct nftnl_rule_iter *iter;
+ 	struct nftnl_rule *r;
++	int ret = 0;
++
++	if (d->verbose)
++		fprintf(stdout, "Zeroing chain `%s'\n",
++			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
++
++	if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
++		/* zero base chain counters. */
++		nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
++		nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
++		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
++		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c);
++		if (ret)
++			return -1;
++	}
+ 
+ 	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+-		return;
++		return -1;
+ 
+ 	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+@@ -2989,13 +3010,17 @@ static void nft_chain_zero_rule_counters(struct nft_handle *h,
+ 	}
+ 
+ 	nftnl_rule_iter_destroy(iter);
++	return 0;
+ }
+ 
+ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
+ 			    const char *table, bool verbose)
+ {
+ 	struct nftnl_chain_list *list;
+-	struct nftnl_chain_list_iter *iter;
++	struct chain_zero_data d = {
++		.handle = h,
++		.verbose = verbose,
++	};
+ 	struct nftnl_chain *c;
+ 	int ret = 0;
+ 
+@@ -3003,41 +3028,16 @@ int nft_chain_zero_counters(struct nft_handle *h, const char *chain,
+ 	if (list == NULL)
+ 		goto err;
+ 
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
+-		goto err;
+-
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get(c, NFTNL_CHAIN_NAME);
+-
+-		if (chain != NULL && strcmp(chain, chain_name) != 0)
+-			goto next;
+-
+-		if (verbose)
+-			fprintf(stdout, "Zeroing chain `%s'\n", chain_name);
+-
+-		if (nftnl_chain_is_set(c, NFTNL_CHAIN_HOOKNUM)) {
+-			/* zero base chain counters. */
+-			nftnl_chain_set_u64(c, NFTNL_CHAIN_PACKETS, 0);
+-			nftnl_chain_set_u64(c, NFTNL_CHAIN_BYTES, 0);
+-		}
+-
+-		nft_chain_zero_rule_counters(h, c);
+-
+-		nftnl_chain_unset(c, NFTNL_CHAIN_HANDLE);
+-
+-		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_ZERO, c);
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
++		if (!c)
++			return 0;
+ 
+-		if (chain != NULL)
+-			break;
+-next:
+-		c = nftnl_chain_list_iter_next(iter);
++		ret = __nft_chain_zero_counters(c, &d);
++		goto err;
+ 	}
+ 
+-	nftnl_chain_list_iter_destroy(iter);
+-
++	ret = nftnl_chain_list_foreach(list, __nft_chain_zero_counters, &d);
+ err:
+ 	/* the core expects 1 for success and 0 for error */
+ 	return ret == 0 ? 1 : 0;
+-- 
+2.20.1
+
diff --git a/SOURCES/0024-tests-Extend-verbose-output-and-return-code-tests.patch b/SOURCES/0024-tests-Extend-verbose-output-and-return-code-tests.patch
new file mode 100644
index 0000000..700a0d9
--- /dev/null
+++ b/SOURCES/0024-tests-Extend-verbose-output-and-return-code-tests.patch
@@ -0,0 +1,79 @@
+From cb418353998513b2d1b95fbd3dbcf205c38ec4a0 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:16 +0100
+Subject: [PATCH] tests: Extend verbose output and return code tests
+
+Recent changes to chain flush and zero routines incorporate proper error
+propagation so trying to flush or zero a non-existent chain results in
+an error. This is consistent with iptables-legacy, extend tests to make
+sure it stays this way.
+
+Also extend verbose output test to make these recent changes didn't mess
+it up.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit e80f7fe56e4c64e05da426418bc2fae7ca221c49)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ .../shell/testcases/iptables/0002-verbose-output_0  | 13 +++++++++----
+ .../shell/testcases/iptables/0004-return-codes_0    |  6 ++++++
+ 2 files changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/iptables/tests/shell/testcases/iptables/0002-verbose-output_0 b/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
+index 2e8059536ea7b..b1ef91f61f481 100755
+--- a/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
++++ b/iptables/tests/shell/testcases/iptables/0002-verbose-output_0
+@@ -29,23 +29,28 @@ Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
+ 
+ diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI iptables -v -n -L)
+ 
++[[ -z $($XT_MULTI iptables -v -N foobar) ]] || exit 1
++
+ diff -u -Z <(echo -e "$VOUT1") <($XT_MULTI iptables -v -D FORWARD $RULE1)
+ diff -u -Z <(echo -e "$VOUT2") <($XT_MULTI iptables -v -D FORWARD $RULE2)
+ 
+ EXPECT="Flushing chain \`INPUT'
+ Flushing chain \`FORWARD'
+-Flushing chain \`OUTPUT'"
++Flushing chain \`OUTPUT'
++Flushing chain \`foobar'"
+ 
+ diff -u <(echo -e "$EXPECT") <($XT_MULTI iptables -v -F)
+ 
+ EXPECT="Zeroing chain \`INPUT'
+ Zeroing chain \`FORWARD'
+-Zeroing chain \`OUTPUT'"
++Zeroing chain \`OUTPUT'
++Zeroing chain \`foobar'"
+ 
+ diff -u <(echo -e "$EXPECT") <($XT_MULTI iptables -v -Z)
+ 
+ diff -u <(echo "Flushing chain \`OUTPUT'") <($XT_MULTI iptables -v -F OUTPUT)
+ diff -u <(echo "Zeroing chain \`OUTPUT'") <($XT_MULTI iptables -v -Z OUTPUT)
++diff -u <(echo "Flushing chain \`foobar'") <($XT_MULTI iptables -v -F foobar)
++diff -u <(echo "Zeroing chain \`foobar'") <($XT_MULTI iptables -v -Z foobar)
+ 
+-$XT_MULTI iptables -N foo
+-diff -u <(echo "Deleting chain \`foo'") <($XT_MULTI iptables -v -X foo)
++diff -u <(echo "Deleting chain \`foobar'") <($XT_MULTI iptables -v -X foobar)
+diff --git a/iptables/tests/shell/testcases/iptables/0004-return-codes_0 b/iptables/tests/shell/testcases/iptables/0004-return-codes_0
+index 5b6e1f6f1bc7a..9d2493992bd69 100755
+--- a/iptables/tests/shell/testcases/iptables/0004-return-codes_0
++++ b/iptables/tests/shell/testcases/iptables/0004-return-codes_0
+@@ -23,6 +23,12 @@ cmd 1 iptables -N foo
+ # iptables-nft allows this - bug or feature?
+ #cmd 2 iptables -N "invalid name"
+ 
++# test chain flushing/zeroing
++cmd 0 iptables -F foo
++cmd 0 iptables -Z foo
++cmd 1 iptables -F bar
++cmd 1 iptables -Z bar
++
+ # test chain rename
+ cmd 0 iptables -E foo bar
+ cmd 1 iptables -E foo bar
+-- 
+2.20.1
+
diff --git a/SOURCES/0025-xtables-Optimize-user-defined-chain-deletion.patch b/SOURCES/0025-xtables-Optimize-user-defined-chain-deletion.patch
new file mode 100644
index 0000000..768024a
--- /dev/null
+++ b/SOURCES/0025-xtables-Optimize-user-defined-chain-deletion.patch
@@ -0,0 +1,134 @@
+From 7b071787e82cbc53a5a33364826d8e0495b84fc0 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:17 +0100
+Subject: [PATCH] xtables: Optimize user-defined chain deletion
+
+Make use of nftnl_chain_list_lookup_byname() if a chain name was given.
+Move the actual chain deleting code into a callback suitable for passing
+to nftnl_chain_list_foreach().
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 0b950ed4549308ef23ffc7561567df86c90cfed9)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 89 ++++++++++++++++++++++++++------------------------
+ 1 file changed, 46 insertions(+), 43 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 9951bf3212197..162d91e82115b 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1642,63 +1642,66 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
+ #define NLM_F_NONREC	0x100	/* Do not delete recursively    */
+ #endif
+ 
++struct chain_user_del_data {
++	struct nft_handle	*handle;
++	bool			verbose;
++	int			builtin_err;
++};
++
++static int __nft_chain_user_del(struct nftnl_chain *c, void *data)
++{
++	struct chain_user_del_data *d = data;
++	struct nft_handle *h = d->handle;
++	int ret;
++
++	/* don't delete built-in chain */
++	if (nft_chain_builtin(c))
++		return d->builtin_err;
++
++	if (d->verbose)
++		fprintf(stdout, "Deleting chain `%s'\n",
++			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME));
++
++	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
++	if (ret)
++		return -1;
++
++	nftnl_chain_list_del(c);
++	return 0;
++}
++
+ int nft_chain_user_del(struct nft_handle *h, const char *chain,
+ 		       const char *table, bool verbose)
+ {
++	struct chain_user_del_data d = {
++		.handle = h,
++		.verbose = verbose,
++	};
+ 	struct nftnl_chain_list *list;
+-	struct nftnl_chain_list_iter *iter;
+ 	struct nftnl_chain *c;
+ 	int ret = 0;
+-	int deleted_ctr = 0;
+ 
+ 	nft_fn = nft_chain_user_del;
+ 
+ 	list = nft_chain_list_get(h, table);
+ 	if (list == NULL)
+-		goto err;
+-
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
+-		goto err;
+-
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-
+-		/* don't delete built-in chain */
+-		if (nft_chain_builtin(c))
+-			goto next;
+-
+-		if (chain != NULL && strcmp(chain, chain_name) != 0)
+-			goto next;
+-
+-		if (verbose)
+-			fprintf(stdout, "Deleting chain `%s'\n", chain);
+-
+-		ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_DEL, c);
+-
+-		if (ret < 0)
+-			break;
+-
+-		deleted_ctr++;
+-		nftnl_chain_list_del(c);
+-
+-		if (chain != NULL)
+-			break;
+-next:
+-		c = nftnl_chain_list_iter_next(iter);
+-	}
+-
+-	nftnl_chain_list_iter_destroy(iter);
+-err:
++		return 0;
+ 
+-	/* chain not found */
+-	if (chain != NULL && deleted_ctr == 0) {
+-		ret = -1;
+-		errno = ENOENT;
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
++		if (!c) {
++			errno = ENOENT;
++			return 0;
++		}
++		d.builtin_err = -2;
++		ret = __nft_chain_user_del(c, &d);
++		if (ret == -2)
++			errno = EINVAL;
++		goto out;
+ 	}
+ 
++	ret = nftnl_chain_list_foreach(list, __nft_chain_user_del, &d);
++out:
+ 	/* the core expects 1 for success and 0 for error */
+ 	return ret == 0 ? 1 : 0;
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/0026-xtables-Optimize-list-command-with-given-chain.patch b/SOURCES/0026-xtables-Optimize-list-command-with-given-chain.patch
new file mode 100644
index 0000000..58ccc2b
--- /dev/null
+++ b/SOURCES/0026-xtables-Optimize-list-command-with-given-chain.patch
@@ -0,0 +1,139 @@
+From 76b233caab4925277bdbdf99e4ad4516b2d8a280 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:18 +0100
+Subject: [PATCH] xtables: Optimize list command with given chain
+
+Make use of nftnl_chain_list_lookup_byname() even if not listing a
+specific rule. Introduce __nft_print_header() to consolidate chain value
+extraction for printing with ops->print_header().
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 11cbd7291f37fbfd5ebe6ffa1730f7d198ed2ac0)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 78 +++++++++++++++++++++-----------------------------
+ 1 file changed, 32 insertions(+), 46 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 162d91e82115b..e1c997836cb97 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2247,6 +2247,24 @@ static int nft_rule_count(struct nft_handle *h, struct nftnl_chain *c)
+ 	return rule_ctr;
+ }
+ 
++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);
++	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
++	bool basechain = !!nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM);
++	uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
++	uint32_t entries = nft_rule_count(h, c);
++	struct xt_counters ctrs = {
++		.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
++		.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
++	};
++
++	ops->print_header(format, chain_name, policy_name[policy],
++			&ctrs, basechain, refs - entries, entries);
++}
++
+ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 		  int rulenum, unsigned int format)
+ {
+@@ -2275,75 +2293,43 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 		return 0;
+ 	}
+ 
+-	if (chain && rulenum) {
+-		c = nft_chain_find(h, table, chain);
++	list = nft_chain_list_get(h, table);
++	if (!list)
++		return 0;
++
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
+ 		if (!c)
+ 			return 0;
+ 
++		if (!rulenum) {
++			if (ops->print_table_header)
++				ops->print_table_header(table);
++			__nft_print_header(h, ops, c, format);
++		}
+ 		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
+ 		return 1;
+ 	}
+ 
+-	list = nft_chain_list_get(h, table);
+-	if (!list)
+-		return 0;
+-
+ 	iter = nftnl_chain_list_iter_create(list);
+ 	if (iter == NULL)
+-		goto err;
++		return 0;
+ 
+-	if (!chain && ops->print_table_header)
++	if (ops->print_table_header)
+ 		ops->print_table_header(table);
+ 
+ 	c = nftnl_chain_list_iter_next(iter);
+ 	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-		uint32_t policy =
+-			nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+-		uint32_t refs =
+-			nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
+-		struct xt_counters ctrs = {
+-			.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
+-			.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
+-		};
+-		bool basechain = false;
+-		uint32_t entries;
+-
+-		if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
+-			basechain = true;
+-
+-		if (chain) {
+-			if (strcmp(chain, chain_name) != 0)
+-				goto next;
+-			else if (ops->print_table_header)
+-				ops->print_table_header(table);
+-		}
+-
+ 		if (found)
+ 			printf("\n");
+ 
+-		entries = nft_rule_count(h, c);
+-		ops->print_header(format, chain_name, policy_name[policy],
+-				  &ctrs, basechain, refs - entries, entries);
+-
++		__nft_print_header(h, ops, c, format);
+ 		__nft_rule_list(h, c, rulenum, format, ops->print_rule);
+ 
+ 		found = true;
+-
+-		/* we printed the chain we wanted, stop processing. */
+-		if (chain)
+-			break;
+-
+-next:
+ 		c = nftnl_chain_list_iter_next(iter);
+ 	}
+-
+ 	nftnl_chain_list_iter_destroy(iter);
+-err:
+-	if (chain && !found)
+-		return 0;
+-
+ 	return 1;
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0027-xtables-Optimize-list-rules-command-with-given-chain.patch b/SOURCES/0027-xtables-Optimize-list-rules-command-with-given-chain.patch
new file mode 100644
index 0000000..ed896f4
--- /dev/null
+++ b/SOURCES/0027-xtables-Optimize-list-rules-command-with-given-chain.patch
@@ -0,0 +1,158 @@
+From adf121e392eeb87eb77df65527a4fc8580e9a84d Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:19 +0100
+Subject: [PATCH] xtables: Optimize list rules command with given chain
+
+If a chain name was given, make use of nftnl_chain_list_lookup_byname().
+
+Likewise in nftnl_rule_list_chain_save(), but introduce
+__nftnl_rule_list_chain_save() suitable for passing to
+nftnl_chain_list_foreach().
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 6b1871914e4f3717c7e6324727b80cf1d5d985b1)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 93 +++++++++++++++++++++++---------------------------
+ 1 file changed, 43 insertions(+), 50 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index e1c997836cb97..e0455eabda77a 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2339,46 +2339,44 @@ list_save(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 	nft_rule_print_save(r, NFT_RULE_APPEND, format);
+ }
+ 
++static int __nftnl_rule_list_chain_save(struct nftnl_chain *c, void *data)
++{
++	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
++	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
++	int *counters = data;
++
++	if (!nft_chain_builtin(c)) {
++		printf("-N %s\n", chain_name);
++		return 0;
++	}
++
++	/* this is a base chain */
++
++	printf("-P %s %s", chain_name, policy_name[policy]);
++	if (*counters)
++		printf(" -c %"PRIu64" %"PRIu64,
++		       nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
++		       nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
++	printf("\n");
++	return 0;
++}
++
+ static int
+ nftnl_rule_list_chain_save(struct nft_handle *h, const char *chain,
+ 			   struct nftnl_chain_list *list, int counters)
+ {
+-	struct nftnl_chain_list_iter *iter;
+ 	struct nftnl_chain *c;
+ 
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
+-		return 0;
+-
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-		uint32_t policy =
+-			nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+-
+-		if (chain && strcmp(chain, chain_name) != 0)
+-			goto next;
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
++		if (!c)
++			return 0;
+ 
+-		/* this is a base chain */
+-		if (nft_chain_builtin(c)) {
+-			printf("-P %s %s", chain_name, policy_name[policy]);
+-
+-			if (counters) {
+-				printf(" -c %"PRIu64" %"PRIu64"\n",
+-					nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
+-					nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES));
+-			} else
+-				printf("\n");
+-		} else {
+-			printf("-N %s\n", chain_name);
+-		}
+-next:
+-		c = nftnl_chain_list_iter_next(iter);
++		__nftnl_rule_list_chain_save(c, &counters);
++		return 1;
+ 	}
+ 
+-	nftnl_chain_list_iter_destroy(iter);
+-
++	nftnl_chain_list_foreach(list, __nftnl_rule_list_chain_save, &counters);
+ 	return 1;
+ }
+ 
+@@ -2410,41 +2408,36 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 
+ 	list = nft_chain_list_get(h, table);
+ 	if (!list)
+-		goto err;
++		return 0;
+ 
+ 	/* Dump policies and custom chains first */
+ 	if (!rulenum)
+ 		nftnl_rule_list_chain_save(h, chain, list, counters);
+ 
+-	/* Now dump out rules in this table */
+-	iter = nftnl_chain_list_iter_create(list);
+-	if (iter == NULL)
+-		goto err;
+-
+ 	if (counters < 0)
+ 		format = FMT_C_COUNTS;
+ 	else if (counters == 0)
+ 		format = FMT_NOCOUNTS;
+ 
+-	c = nftnl_chain_list_iter_next(iter);
+-	while (c != NULL) {
+-		const char *chain_name =
+-			nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
++	if (chain) {
++		c = nftnl_chain_list_lookup_byname(list, chain);
++		if (!c)
++			return 0;
+ 
+-		if (chain && strcmp(chain, chain_name) != 0)
+-			goto next;
++		return __nft_rule_list(h, c, rulenum, format, list_save);
++	}
+ 
+-		ret = __nft_rule_list(h, c, rulenum, format, list_save);
++	/* Now dump out rules in this table */
++	iter = nftnl_chain_list_iter_create(list);
++	if (iter == NULL)
++		return 0;
+ 
+-		/* we printed the chain we wanted, stop processing. */
+-		if (chain)
+-			break;
+-next:
++	c = nftnl_chain_list_iter_next(iter);
++	while (c != NULL) {
++		ret = __nft_rule_list(h, c, rulenum, format, list_save);
+ 		c = nftnl_chain_list_iter_next(iter);
+ 	}
+-
+ 	nftnl_chain_list_iter_destroy(iter);
+-err:
+ 	return ret;
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0028-nft-Make-use-of-nftnl_rule_lookup_byindex.patch b/SOURCES/0028-nft-Make-use-of-nftnl_rule_lookup_byindex.patch
new file mode 100644
index 0000000..2b08c97
--- /dev/null
+++ b/SOURCES/0028-nft-Make-use-of-nftnl_rule_lookup_byindex.patch
@@ -0,0 +1,76 @@
+From 062f97fdd10d0930fecbbf49438ff856ea46ca9e Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 20 Dec 2018 16:09:20 +0100
+Subject: [PATCH] nft: Make use of nftnl_rule_lookup_byindex()
+
+Use the function where suitable to potentially speedup rule cache lookup
+by rule number.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 039b04896521026d1cb52d60dbacb6ee5226c02d)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 31 ++++++++++++++++++-------------
+ 1 file changed, 18 insertions(+), 13 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index e0455eabda77a..1fd3837f2d334 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1976,27 +1976,21 @@ nft_rule_find(struct nft_handle *h, struct nftnl_chain *c, void *data, int rulen
+ {
+ 	struct nftnl_rule *r;
+ 	struct nftnl_rule_iter *iter;
+-	int rule_ctr = 0;
+ 	bool found = false;
+ 
++	if (rulenum >= 0)
++		/* Delete by rule number case */
++		return nftnl_rule_lookup_byindex(c, rulenum);
++
+ 	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return 0;
+ 
+ 	r = nftnl_rule_iter_next(iter);
+ 	while (r != NULL) {
+-		if (rulenum >= 0) {
+-			/* Delete by rule number case */
+-			if (rule_ctr == rulenum) {
+-			    found = true;
+-			    break;
+-			}
+-		} else {
+-			found = h->ops->rule_find(h->ops, r, data);
+-			if (found)
+-				break;
+-		}
+-		rule_ctr++;
++		found = h->ops->rule_find(h->ops, r, data);
++		if (found)
++			break;
+ 		r = nftnl_rule_iter_next(iter);
+ 	}
+ 
+@@ -2202,6 +2196,17 @@ __nft_rule_list(struct nft_handle *h, struct nftnl_chain *c,
+ 	struct nftnl_rule *r;
+ 	int rule_ctr = 0;
+ 
++	if (rulenum > 0) {
++		r = nftnl_rule_lookup_byindex(c, rulenum - 1);
++		if (!r)
++			/* iptables-legacy returns 0 when listing for
++			 * valid chain but invalid rule number
++			 */
++			return 1;
++		cb(r, rulenum, format);
++		return 1;
++	}
++
+ 	iter = nftnl_rule_iter_create(c);
+ 	if (iter == NULL)
+ 		return 0;
+-- 
+2.20.1
+
diff --git a/SOURCES/0029-nft-Simplify-nft_is_chain_compatible.patch b/SOURCES/0029-nft-Simplify-nft_is_chain_compatible.patch
new file mode 100644
index 0000000..9aaf76c
--- /dev/null
+++ b/SOURCES/0029-nft-Simplify-nft_is_chain_compatible.patch
@@ -0,0 +1,80 @@
+From f13c1474a2f70d7d3cb5f3f5be8a4cceebb324a0 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Sun, 30 Dec 2018 20:06:08 +0100
+Subject: [PATCH] nft: Simplify nft_is_chain_compatible()
+
+Make use of nft_{table,chain}_builtin_find() instead of open-coding the
+list traversal. Since code is pretty obvious now, drop the comments
+added earlier.
+
+Fixes: e774b15299c27 ("nft: Review is_*_compatible() routines")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit fae77a24634365b18687a5f09357dbf4aaee2bc0)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 34 +++++++++-------------------------
+ 1 file changed, 9 insertions(+), 25 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 1fd3837f2d334..25e538b7e35d7 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -3077,11 +3077,12 @@ static int nft_is_rule_compatible(struct nftnl_rule *rule, void *data)
+ 
+ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+ {
+-	const struct builtin_chain *chains = NULL, *chain = NULL;
+-	const char *table, *name, *type;
++	const struct builtin_table *table;
++	const struct builtin_chain *chain;
++	const char *tname, *cname, *type;
+ 	struct nft_handle *h = data;
+ 	enum nf_inet_hooks hook;
+-	int i, prio;
++	int prio;
+ 
+ 	if (nftnl_rule_foreach(c, nft_is_rule_compatible, NULL))
+ 		return -1;
+@@ -3089,33 +3090,16 @@ static int nft_is_chain_compatible(struct nftnl_chain *c, void *data)
+ 	if (!nft_chain_builtin(c))
+ 		return 0;
+ 
+-	/* find chain's table in builtin tables */
+-	table = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
+-	for (i = 0; i < NFT_TABLE_MAX; i++) {
+-		const char *cur_table = h->tables[i].name;
+-
+-		if (!cur_table || strcmp(cur_table, table))
+-			continue;
+-
+-		chains = h->tables[i].chains;
+-		break;
+-	}
+-	if (!chains)
++	tname = nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE);
++	table = nft_table_builtin_find(h, tname);
++	if (!table)
+ 		return -1;
+ 
+-	/* find chain in builtin chain list */
+-	name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-	for (i = 0; i < NF_INET_NUMHOOKS && chains[i].name; i++) {
+-		if (strcmp(name, chains[i].name))
+-			continue;
+-
+-		chain = &chains[i];
+-		break;
+-	}
++	cname = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
++	chain = nft_chain_builtin_find(table, cname);
+ 	if (!chain)
+ 		return -1;
+ 
+-	/* compare properties */
+ 	type = nftnl_chain_get_str(c, NFTNL_CHAIN_TYPE);
+ 	prio = nftnl_chain_get_u32(c, NFTNL_CHAIN_PRIO);
+ 	hook = nftnl_chain_get_u32(c, NFTNL_CHAIN_HOOKNUM);
+-- 
+2.20.1
+
diff --git a/SOURCES/0030-nft-Simplify-flush_chain_cache.patch b/SOURCES/0030-nft-Simplify-flush_chain_cache.patch
new file mode 100644
index 0000000..52e9825
--- /dev/null
+++ b/SOURCES/0030-nft-Simplify-flush_chain_cache.patch
@@ -0,0 +1,66 @@
+From 329090e1c375905ec388ac1025b4e9fab883c3ca Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Sun, 30 Dec 2018 20:06:09 +0100
+Subject: [PATCH] nft: Simplify flush_chain_cache()
+
+With all the checks for 'tablename' being non-NULL, this code was rather
+stupid and really hard to read. And the fix is indeed quite simple: If a
+table name was given, use nft_table_builtin_find() and just flush its
+chain cache. Otherwise iterate over all builtin tables without any
+conditionals for 'tablename'.
+
+Fixes: d4b0d248cc057 ("nft: Reduce indenting level in flush_chain_cache()")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 4441b7da7995ed87741164ef39e99f1065eb9637)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 24 +++++++++++-------------
+ 1 file changed, 11 insertions(+), 13 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 25e538b7e35d7..dafb879ebd6f0 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -793,27 +793,25 @@ static int __flush_chain_cache(struct nftnl_chain *c, void *data)
+ 
+ static void flush_chain_cache(struct nft_handle *h, const char *tablename)
+ {
++	const struct builtin_table *table;
+ 	int i;
+ 
++	if (tablename) {
++		table = nft_table_builtin_find(h, tablename);
++		if (!table || !h->table[table->type].chain_cache)
++			return;
++		nftnl_chain_list_foreach(h->table[table->type].chain_cache,
++					 __flush_chain_cache, NULL);
++		return;
++	}
++
+ 	for (i = 0; i < NFT_TABLE_MAX; i++) {
+ 		if (h->tables[i].name == NULL)
+ 			continue;
+ 
+-		if (tablename && strcmp(h->tables[i].name, tablename))
++		if (!h->table[i].chain_cache)
+ 			continue;
+ 
+-		if (!h->table[i].chain_cache) {
+-			if (tablename)
+-				return;
+-			continue;
+-		}
+-
+-		if (tablename) {
+-			nftnl_chain_list_foreach(h->table[i].chain_cache,
+-						 __flush_chain_cache, NULL);
+-			return;
+-		}
+-
+ 		nftnl_chain_list_free(h->table[i].chain_cache);
+ 		h->table[i].chain_cache = NULL;
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0031-xtables-Set-errno-in-nft_rule_check-if-chain-not-fou.patch b/SOURCES/0031-xtables-Set-errno-in-nft_rule_check-if-chain-not-fou.patch
new file mode 100644
index 0000000..fddf849
--- /dev/null
+++ b/SOURCES/0031-xtables-Set-errno-in-nft_rule_check-if-chain-not-fou.patch
@@ -0,0 +1,68 @@
+From 63123e24c1b957cfabcfa7708994b0d61447724e Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Sun, 30 Dec 2018 20:06:10 +0100
+Subject: [PATCH] xtables: Set errno in nft_rule_check() if chain not found
+
+With this, the explicit check for chain existence can be removed from
+xtables.c since all related commands do this now.
+
+Note that this effectively changes the error message printed by
+iptables-nft when given a non-existing chain, but the new error
+message(s) conform with those printed by legacy iptables.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 41358d474357a39d616302c03cd7f943e19969a2)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c     | 12 +++++++-----
+ iptables/xtables.c |  4 ----
+ 2 files changed, 7 insertions(+), 9 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index dafb879ebd6f0..1ce1ecdd276be 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2007,17 +2007,19 @@ int nft_rule_check(struct nft_handle *h, const char *chain,
+ 
+ 	c = nft_chain_find(h, table, chain);
+ 	if (!c)
+-		return 0;
++		goto fail_enoent;
+ 
+ 	r = nft_rule_find(h, c, data, -1);
+-	if (r == NULL) {
+-		errno = ENOENT;
+-		return 0;
+-	}
++	if (r == NULL)
++		goto fail_enoent;
++
+ 	if (verbose)
+ 		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ 
+ 	return 1;
++fail_enoent:
++	errno = ENOENT;
++	return 0;
+ }
+ 
+ int nft_rule_delete(struct nft_handle *h, const char *chain,
+diff --git a/iptables/xtables.c b/iptables/xtables.c
+index 24a6e234bcf4b..da11e8cc159a0 100644
+--- a/iptables/xtables.c
++++ b/iptables/xtables.c
+@@ -1064,10 +1064,6 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
+ 					   p->chain);
+ 		}
+ 
+-		if (!p->xlate && !nft_chain_exists(h, p->table, p->chain))
+-			xtables_error(OTHER_PROBLEM,
+-				      "Chain '%s' does not exist", p->chain);
+-
+ 		if (!p->xlate && !cs->target && strlen(cs->jumpto) > 0 &&
+ 		    !nft_chain_exists(h, p->table, cs->jumpto))
+ 			xtables_error(PARAMETER_PROBLEM,
+-- 
+2.20.1
+
diff --git a/SOURCES/0032-xtables-Speed-up-chain-deletion-in-large-rulesets.patch b/SOURCES/0032-xtables-Speed-up-chain-deletion-in-large-rulesets.patch
new file mode 100644
index 0000000..08ef8d6
--- /dev/null
+++ b/SOURCES/0032-xtables-Speed-up-chain-deletion-in-large-rulesets.patch
@@ -0,0 +1,34 @@
+From 0925419844d77e7216067208f270cd1d8279b523 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Wed, 12 Dec 2018 20:04:12 +0100
+Subject: [PATCH] xtables: Speed up chain deletion in large rulesets
+
+Kernel prefers to identify chain by handle if it was given which causes
+manual traversal of the chain list. In contrast, chain lookup by name in
+kernel makes use of a hash table so is considerably faster. Force this
+code path by removing the cached chain's handle when removing it.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit a5f517a41d72794fae3d1332e6e0e413a5cd16c1)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 1ce1ecdd276be..9c0ad9a2d054f 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1660,6 +1660,8 @@ 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));
+ 
++	/* 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);
+ 	if (ret)
+ 		return -1;
+-- 
+2.20.1
+
diff --git a/SOURCES/0033-arptables-nft-Fix-listing-rules-without-target.patch b/SOURCES/0033-arptables-nft-Fix-listing-rules-without-target.patch
new file mode 100644
index 0000000..1842b5a
--- /dev/null
+++ b/SOURCES/0033-arptables-nft-Fix-listing-rules-without-target.patch
@@ -0,0 +1,32 @@
+From 6ae39816fe49ad3210a6a4ff1b997775e688be9a Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:50 +0100
+Subject: [PATCH] arptables-nft: Fix listing rules without target
+
+Don't try to print cs.jumpto if it is an empty string, otherwise listing
+(and verbose output) contains '-j' flag without argument.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 043bf38bc9ee020bbf1a9789773050d47f83b807)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-arp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
+index 37850bd328b71..56021223bdbe6 100644
+--- a/iptables/nft-arp.c
++++ b/iptables/nft-arp.c
+@@ -598,7 +598,7 @@ nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 
+ 	nft_rule_to_iptables_command_state(r, &cs);
+ 
+-	if (cs.jumpto)
++	if (strlen(cs.jumpto))
+ 		printf("-j %s ", cs.jumpto);
+ 	nft_arp_print_rule_details(&cs.arp, format);
+ 	print_matches_and_target(&cs, format);
+-- 
+2.20.1
+
diff --git a/SOURCES/0034-arptables-nft-Fix-MARK-target-parsing-and-printing.patch b/SOURCES/0034-arptables-nft-Fix-MARK-target-parsing-and-printing.patch
new file mode 100644
index 0000000..55dee75
--- /dev/null
+++ b/SOURCES/0034-arptables-nft-Fix-MARK-target-parsing-and-printing.patch
@@ -0,0 +1,155 @@
+From c30599e8d9465da351cf2bc96b67574a6b1ae72b Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:51 +0100
+Subject: [PATCH] arptables-nft: Fix MARK target parsing and printing
+
+Legacy arptables parses mark values in hex no matter if prefixed with
+'0x' or not. Sadly, this is not easily achievable with guided option
+parser. Hence fall back to the old 'parse' callback. The introduced
+target definition is valid only for revision 2, but that's consistent
+with legacy arptables.
+
+When printing, use --set-mark option instead of --set-xmark.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit f7fa88020f3bc4ec646ce2a48731a1f5fa2aa0a9)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ extensions/libxt_MARK.c                       | 95 +++++++++++++++++++
+ .../arptables/0001-arptables-save-restore_0   |  2 +-
+ 2 files changed, 96 insertions(+), 1 deletion(-)
+
+diff --git a/extensions/libxt_MARK.c b/extensions/libxt_MARK.c
+index 43aa977924b12..b765af6c35304 100644
+--- a/extensions/libxt_MARK.c
++++ b/extensions/libxt_MARK.c
+@@ -1,3 +1,4 @@
++#include <getopt.h>
+ #include <stdbool.h>
+ #include <stdio.h>
+ #include <xtables.h>
+@@ -245,6 +246,87 @@ static void mark_tg_save(const void *ip, const struct xt_entry_target *target)
+ 	printf(" --set-xmark 0x%x/0x%x", info->mark, info->mask);
+ }
+ 
++static void mark_tg_arp_save(const void *ip, const struct xt_entry_target *target)
++{
++	const struct xt_mark_tginfo2 *info = (const void *)target->data;
++
++	if (info->mark == 0)
++		printf(" --and-mark %x", (unsigned int)(uint32_t)~info->mask);
++	else if (info->mark == info->mask)
++		printf(" --or-mark %x", info->mark);
++	else
++		printf(" --set-mark %x", info->mark);
++}
++
++static void mark_tg_arp_print(const void *ip,
++			      const struct xt_entry_target *target, int numeric)
++{
++	mark_tg_arp_save(ip, target);
++}
++
++#define MARK_OPT 1
++#define AND_MARK_OPT 2
++#define OR_MARK_OPT 3
++
++static struct option mark_tg_arp_opts[] = {
++	{ .name = "set-mark", .has_arg = required_argument, .flag = 0, .val = MARK_OPT },
++	{ .name = "and-mark", .has_arg = required_argument, .flag = 0, .val = AND_MARK_OPT },
++	{ .name = "or-mark", .has_arg = required_argument, .flag = 0, .val =  OR_MARK_OPT },
++	{ .name = NULL}
++};
++
++static int
++mark_tg_arp_parse(int c, char **argv, int invert, unsigned int *flags,
++		  const void *entry, struct xt_entry_target **target)
++{
++	struct xt_mark_tginfo2 *info =
++		(struct xt_mark_tginfo2 *)(*target)->data;
++	int i;
++
++	switch (c) {
++	case MARK_OPT:
++		if (sscanf(argv[optind-1], "%x", &i) != 1) {
++			xtables_error(PARAMETER_PROBLEM,
++				"Bad mark value `%s'", optarg);
++			return 0;
++		}
++		info->mark = i;
++		if (*flags)
++			xtables_error(PARAMETER_PROBLEM,
++				"MARK: Can't specify --set-mark twice");
++		*flags = 1;
++		break;
++	case AND_MARK_OPT:
++		if (sscanf(argv[optind-1], "%x", &i) != 1) {
++			xtables_error(PARAMETER_PROBLEM,
++				"Bad mark value `%s'", optarg);
++			return 0;
++		}
++		info->mark = 0;
++		info->mask = ~i;
++		if (*flags)
++			xtables_error(PARAMETER_PROBLEM,
++				"MARK: Can't specify --and-mark twice");
++		*flags = 1;
++		break;
++	case OR_MARK_OPT:
++		if (sscanf(argv[optind-1], "%x", &i) != 1) {
++			xtables_error(PARAMETER_PROBLEM,
++				"Bad mark value `%s'", optarg);
++			return 0;
++		}
++		info->mark = info->mask = i;
++		if (*flags)
++			xtables_error(PARAMETER_PROBLEM,
++				"MARK: Can't specify --or-mark twice");
++		*flags = 1;
++		break;
++	default:
++		return 0;
++	}
++	return 1;
++}
++
+ static int mark_tg_xlate(struct xt_xlate *xl,
+ 			 const struct xt_xlate_tg_params *params)
+ {
+@@ -335,6 +417,19 @@ static struct xtables_target mark_tg_reg[] = {
+ 		.x6_options    = mark_tg_opts,
+ 		.xlate	       = mark_tg_xlate,
+ 	},
++	{
++		.version       = XTABLES_VERSION,
++		.name          = "MARK",
++		.revision      = 2,
++		.family        = NFPROTO_ARP,
++		.size          = XT_ALIGN(sizeof(struct xt_mark_tginfo2)),
++		.userspacesize = XT_ALIGN(sizeof(struct xt_mark_tginfo2)),
++		.help          = mark_tg_help,
++		.print         = mark_tg_arp_print,
++		.save          = mark_tg_arp_save,
++		.parse         = mark_tg_arp_parse,
++		.extra_opts    = mark_tg_arp_opts,
++	},
+ };
+ 
+ void _init(void)
+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 73b3b0cf88e18..f8629551b0ba9 100755
+--- a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
++++ b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
+@@ -47,7 +47,7 @@ DUMP='*filter
+ -A OUTPUT -o eth432 --h-length 6 --opcode 1 --h-type 1 -j CLASSIFY --set-class feed:babe
+ -A foo -i lo --h-length 6 --h-type 1 -j ACCEPT
+ -A foo --h-length 6 --h-type 1 -j ACCEPT
+--A foo --h-length 6 --h-type 1 -j MARK --set-xmark 0x3039/0xffffffff
++-A foo --h-length 6 --h-type 1 -j MARK --set-mark 12345
+ -A foo --h-length 6 --opcode 1 --h-type 1 -j ACCEPT
+ -A foo --h-length 6 --h-type 1 --proto-type 0x800 -j ACCEPT
+ -A foo -i lo --h-length 6 --opcode 1 --h-type 1 --proto-type 0x800 -j ACCEPT
+-- 
+2.20.1
+
diff --git a/SOURCES/0035-arptables-nft-Fix-CLASSIFY-target-printing.patch b/SOURCES/0035-arptables-nft-Fix-CLASSIFY-target-printing.patch
new file mode 100644
index 0000000..1b385af
--- /dev/null
+++ b/SOURCES/0035-arptables-nft-Fix-CLASSIFY-target-printing.patch
@@ -0,0 +1,99 @@
+From 474d95f86b51ee68b3dbad144b10caa07f4d519e Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:52 +0100
+Subject: [PATCH] arptables-nft: Fix CLASSIFY target printing
+
+In legacy arptables, CLASSIFY target is not printed with fixed hex
+number lengths. Counter this by introducing a dedicated target
+definition for NFPROTO_ARP only having own print/save callbacks.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 756bea26a3dad89c467c703725ce6d3c6b29c871)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ extensions/libxt_CLASSIFY.c | 59 +++++++++++++++++++++++++++++--------
+ 1 file changed, 46 insertions(+), 13 deletions(-)
+
+diff --git a/extensions/libxt_CLASSIFY.c b/extensions/libxt_CLASSIFY.c
+index f90082dc7c50e..75aaf0c41b61a 100644
+--- a/extensions/libxt_CLASSIFY.c
++++ b/extensions/libxt_CLASSIFY.c
+@@ -73,6 +73,24 @@ CLASSIFY_save(const void *ip, const struct xt_entry_target *target)
+ 	       TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
+ }
+ 
++static void
++CLASSIFY_arp_save(const void *ip, const struct xt_entry_target *target)
++{
++	const struct xt_classify_target_info *clinfo =
++		(const struct xt_classify_target_info *)target->data;
++
++	printf(" --set-class %x:%x",
++	       TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
++}
++
++static void
++CLASSIFY_arp_print(const void *ip,
++      const struct xt_entry_target *target,
++      int numeric)
++{
++	CLASSIFY_arp_save(ip, target);
++}
++
+ static int CLASSIFY_xlate(struct xt_xlate *xl,
+ 			  const struct xt_xlate_tg_params *params)
+ {
+@@ -98,21 +116,36 @@ static int CLASSIFY_xlate(struct xt_xlate *xl,
+ 	return 1;
+ }
+ 
+-static struct xtables_target classify_target = { 
+-	.family		= NFPROTO_UNSPEC,
+-	.name		= "CLASSIFY",
+-	.version	= XTABLES_VERSION,
+-	.size		= XT_ALIGN(sizeof(struct xt_classify_target_info)),
+-	.userspacesize	= XT_ALIGN(sizeof(struct xt_classify_target_info)),
+-	.help		= CLASSIFY_help,
+-	.print		= CLASSIFY_print,
+-	.save		= CLASSIFY_save,
+-	.x6_parse	= CLASSIFY_parse,
+-	.x6_options	= CLASSIFY_opts,
+-	.xlate          = CLASSIFY_xlate,
++static struct xtables_target classify_tg_reg[] = {
++	{
++		.family		= NFPROTO_UNSPEC,
++		.name		= "CLASSIFY",
++		.version	= XTABLES_VERSION,
++		.size		= XT_ALIGN(sizeof(struct xt_classify_target_info)),
++		.userspacesize	= XT_ALIGN(sizeof(struct xt_classify_target_info)),
++		.help		= CLASSIFY_help,
++		.print		= CLASSIFY_print,
++		.save		= CLASSIFY_save,
++		.x6_parse	= CLASSIFY_parse,
++		.x6_options	= CLASSIFY_opts,
++		.xlate          = CLASSIFY_xlate,
++	},
++	{
++		.family		= NFPROTO_ARP,
++		.name		= "CLASSIFY",
++		.version	= XTABLES_VERSION,
++		.size		= XT_ALIGN(sizeof(struct xt_classify_target_info)),
++		.userspacesize	= XT_ALIGN(sizeof(struct xt_classify_target_info)),
++		.help		= CLASSIFY_help,
++		.print		= CLASSIFY_arp_print,
++		.save		= CLASSIFY_arp_save,
++		.x6_parse	= CLASSIFY_parse,
++		.x6_options	= CLASSIFY_opts,
++		.xlate          = CLASSIFY_xlate,
++	}
+ };
+ 
+ void _init(void)
+ {
+-	xtables_register_target(&classify_target);
++	xtables_register_targets(classify_tg_reg, ARRAY_SIZE(classify_tg_reg));
+ }
+-- 
+2.20.1
+
diff --git a/SOURCES/0036-arptables-nft-Remove-space-between-cnt-and-value.patch b/SOURCES/0036-arptables-nft-Remove-space-between-cnt-and-value.patch
new file mode 100644
index 0000000..919ce17
--- /dev/null
+++ b/SOURCES/0036-arptables-nft-Remove-space-between-cnt-and-value.patch
@@ -0,0 +1,35 @@
+From de76174a6e76bf29c5a3cdcdab1b16ca96a0b4db Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:53 +0100
+Subject: [PATCH] arptables-nft: Remove space between *cnt= and value
+
+When printing rule counters, call xtables_print_num() with FMT_NOTABLE
+bit set to avoid spaces between equal sign and value.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 9421327926a529ec8300d37f3be8a6cfab701786)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-arp.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
+index 56021223bdbe6..f357fc4a43c4c 100644
+--- a/iptables/nft-arp.c
++++ b/iptables/nft-arp.c
+@@ -605,9 +605,9 @@ nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 
+ 	if (!(format & FMT_NOCOUNTS)) {
+ 		printf(", pcnt=");
+-		xtables_print_num(cs.counters.pcnt, format);
++		xtables_print_num(cs.counters.pcnt, format | FMT_NOTABLE);
+ 		printf("-- bcnt=");
+-		xtables_print_num(cs.counters.bcnt, format);
++		xtables_print_num(cs.counters.bcnt, format | FMT_NOTABLE);
+ 	}
+ 
+ 	if (!(format & FMT_NONEWLINE))
+-- 
+2.20.1
+
diff --git a/SOURCES/0037-arptables-nft-save-Fix-position-of-j-option.patch b/SOURCES/0037-arptables-nft-save-Fix-position-of-j-option.patch
new file mode 100644
index 0000000..75e44c7
--- /dev/null
+++ b/SOURCES/0037-arptables-nft-save-Fix-position-of-j-option.patch
@@ -0,0 +1,296 @@
+From 59e25afcdc0a415c8b1bb6fff5fc14985d79e06b Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:54 +0100
+Subject: [PATCH] arptables-nft-save: Fix position of -j option
+
+Legacy arptables-save (just like arptables itself) prints verdict as
+first option, then matches and finally any target options.
+
+To achieve this without introducing double/trailing spaces everywhere,
+integrate target ('-j') option printing into
+nft_arp_print_rule_details() and make it print separating whitespace
+before each option.
+
+In nft_arp_save_rule(), replace the call to save_matches_and_target() by
+by a direct call to cs->target->save() since the former prints '-j'
+option itself. Since there are no match extensions in arptables, any
+other code from that function is not needed.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 2c3f7a2cd6fd8325b3a84e280cce945c6c20b87f)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-arp.c                            | 65 +++++++++++--------
+ .../arptables/0001-arptables-save-restore_0   | 32 ++++-----
+ .../0002-arptables-restore-defaults_0         |  6 +-
+ 3 files changed, 58 insertions(+), 45 deletions(-)
+
+diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
+index f357fc4a43c4c..2cbdf23214049 100644
+--- a/iptables/nft-arp.c
++++ b/iptables/nft-arp.c
+@@ -434,14 +434,21 @@ static void nft_arp_print_header(unsigned int format, const char *chain,
+ 	}
+ }
+ 
+-static void nft_arp_print_rule_details(const struct arpt_entry *fw,
++static void nft_arp_print_rule_details(const struct iptables_command_state *cs,
+ 				       unsigned int format)
+ {
++	const struct arpt_entry *fw = &cs->arp;
+ 	char buf[BUFSIZ];
+ 	char iface[IFNAMSIZ+2];
++	const char *sep = "";
+ 	int print_iface = 0;
+ 	int i;
+ 
++	if (strlen(cs->jumpto)) {
++		printf("%s-j %s", sep, cs->jumpto);
++		sep = " ";
++	}
++
+ 	iface[0] = '\0';
+ 
+ 	if (fw->arp.iniface[0] != '\0') {
+@@ -453,9 +460,11 @@ static void nft_arp_print_rule_details(const struct arpt_entry *fw,
+ 		if (format & FMT_NUMERIC) strcat(iface, "*");
+ 		else strcat(iface, "any");
+ 	}
+-	if (print_iface)
+-		printf("%s-i %s ", fw->arp.invflags & ARPT_INV_VIA_IN ?
++	if (print_iface) {
++		printf("%s%s-i %s", sep, fw->arp.invflags & ARPT_INV_VIA_IN ?
+ 				   "! " : "", iface);
++		sep = " ";
++	}
+ 
+ 	print_iface = 0;
+ 	iface[0] = '\0';
+@@ -469,12 +478,14 @@ static void nft_arp_print_rule_details(const struct arpt_entry *fw,
+ 		if (format & FMT_NUMERIC) strcat(iface, "*");
+ 		else strcat(iface, "any");
+ 	}
+-	if (print_iface)
+-		printf("%s-o %s ", fw->arp.invflags & ARPT_INV_VIA_OUT ?
++	if (print_iface) {
++		printf("%s%s-o %s", sep, fw->arp.invflags & ARPT_INV_VIA_OUT ?
+ 				   "! " : "", iface);
++		sep = " ";
++	}
+ 
+ 	if (fw->arp.smsk.s_addr != 0L) {
+-		printf("%s", fw->arp.invflags & ARPT_INV_SRCIP
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_SRCIP
+ 			? "! " : "");
+ 		if (format & FMT_NUMERIC)
+ 			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.src)));
+@@ -482,7 +493,8 @@ static void nft_arp_print_rule_details(const struct arpt_entry *fw,
+ 			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.src)));
+ 		strncat(buf, mask_to_dotted(&(fw->arp.smsk)),
+ 			sizeof(buf) - strlen(buf) - 1);
+-		printf("-s %s ", buf);
++		printf("-s %s", buf);
++		sep = " ";
+ 	}
+ 
+ 	for (i = 0; i < ARPT_DEV_ADDR_LEN_MAX; i++)
+@@ -490,16 +502,16 @@ static void nft_arp_print_rule_details(const struct arpt_entry *fw,
+ 			break;
+ 	if (i == ARPT_DEV_ADDR_LEN_MAX)
+ 		goto after_devsrc;
+-	printf("%s", fw->arp.invflags & ARPT_INV_SRCDEVADDR
++	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);
+-	printf(" ");
++	sep = " ";
+ after_devsrc:
+ 
+ 	if (fw->arp.tmsk.s_addr != 0L) {
+-		printf("%s", fw->arp.invflags & ARPT_INV_TGTIP
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_TGTIP
+ 			? "! " : "");
+ 		if (format & FMT_NUMERIC)
+ 			sprintf(buf, "%s", addr_to_dotted(&(fw->arp.tgt)));
+@@ -507,7 +519,8 @@ after_devsrc:
+ 			sprintf(buf, "%s", addr_to_anyname(&(fw->arp.tgt)));
+ 		strncat(buf, mask_to_dotted(&(fw->arp.tmsk)),
+ 			sizeof(buf) - strlen(buf) - 1);
+-		printf("-d %s ", buf);
++		printf("-d %s", buf);
++		sep = " ";
+ 	}
+ 
+ 	for (i = 0; i <ARPT_DEV_ADDR_LEN_MAX; i++)
+@@ -515,28 +528,28 @@ after_devsrc:
+ 			break;
+ 	if (i == ARPT_DEV_ADDR_LEN_MAX)
+ 		goto after_devdst;
+-	printf("%s", fw->arp.invflags & ARPT_INV_TGTDEVADDR
++	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);
+-	printf(" ");
++	sep = " ";
+ 
+ after_devdst:
+ 
+ 	if (fw->arp.arhln_mask != 0) {
+-		printf("%s", fw->arp.invflags & ARPT_INV_ARPHLN
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHLN
+ 			? "! " : "");
+ 		printf("--h-length %d", fw->arp.arhln);
+ 		if (fw->arp.arhln_mask != 255)
+ 			printf("/%d", fw->arp.arhln_mask);
+-		printf(" ");
++		sep = " ";
+ 	}
+ 
+ 	if (fw->arp.arpop_mask != 0) {
+ 		int tmp = ntohs(fw->arp.arpop);
+ 
+-		printf("%s", fw->arp.invflags & ARPT_INV_ARPOP
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPOP
+ 			? "! " : "");
+ 		if (tmp <= NUMOPCODES && !(format & FMT_NUMERIC))
+ 			printf("--opcode %s", opcodes[tmp-1]);
+@@ -545,13 +558,13 @@ after_devdst:
+ 
+ 		if (fw->arp.arpop_mask != 65535)
+ 			printf("/%d", ntohs(fw->arp.arpop_mask));
+-		printf(" ");
++		sep = " ";
+ 	}
+ 
+ 	if (fw->arp.arhrd_mask != 0) {
+ 		uint16_t tmp = ntohs(fw->arp.arhrd);
+ 
+-		printf("%s", fw->arp.invflags & ARPT_INV_ARPHRD
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHRD
+ 			? "! " : "");
+ 		if (tmp == 1 && !(format & FMT_NUMERIC))
+ 			printf("--h-type %s", "Ethernet");
+@@ -559,13 +572,13 @@ after_devdst:
+ 			printf("--h-type %u", tmp);
+ 		if (fw->arp.arhrd_mask != 65535)
+ 			printf("/%d", ntohs(fw->arp.arhrd_mask));
+-		printf(" ");
++		sep = " ";
+ 	}
+ 
+ 	if (fw->arp.arpro_mask != 0) {
+ 		int tmp = ntohs(fw->arp.arpro);
+ 
+-		printf("%s", fw->arp.invflags & ARPT_INV_ARPPRO
++		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPPRO
+ 			? "! " : "");
+ 		if (tmp == 0x0800 && !(format & FMT_NUMERIC))
+ 			printf("--proto-type %s", "IPv4");
+@@ -573,7 +586,7 @@ after_devdst:
+ 			printf("--proto-type 0x%x", tmp);
+ 		if (fw->arp.arpro_mask != 65535)
+ 			printf("/%x", ntohs(fw->arp.arpro_mask));
+-		printf(" ");
++		sep = " ";
+ 	}
+ }
+ 
+@@ -584,8 +597,10 @@ nft_arp_save_rule(const void *data, unsigned int format)
+ 
+ 	format |= FMT_NUMERIC;
+ 
+-	nft_arp_print_rule_details(&cs->arp, format);
+-	save_matches_and_target(cs, false, &cs->arp, format);
++	nft_arp_print_rule_details(cs, format);
++	if (cs->target && cs->target->save)
++		cs->target->save(&cs->fw, cs->target->t);
++	printf("\n");
+ }
+ 
+ static void
+@@ -598,9 +613,7 @@ nft_arp_print_rule(struct nftnl_rule *r, unsigned int num, unsigned int format)
+ 
+ 	nft_rule_to_iptables_command_state(r, &cs);
+ 
+-	if (strlen(cs.jumpto))
+-		printf("-j %s ", cs.jumpto);
+-	nft_arp_print_rule_details(&cs.arp, format);
++	nft_arp_print_rule_details(&cs, format);
+ 	print_matches_and_target(&cs, format);
+ 
+ 	if (!(format & FMT_NOCOUNTS)) {
+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 f8629551b0ba9..0664e3b38d5e8 100755
+--- a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
++++ b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
+@@ -35,22 +35,22 @@ DUMP='*filter
+ :INPUT ACCEPT
+ :OUTPUT DROP
+ :foo -
+--A INPUT -s 10.0.0.0/8 --h-length 6 --h-type 1 -j ACCEPT
+--A INPUT -d 192.168.123.1 --h-length 6 --h-type 1 -j ACCEPT
+--A INPUT --src-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1 -j ACCEPT
+--A INPUT --dst-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1 -j ACCEPT
+--A INPUT --h-length 6 --h-type 1 -j foo
+--A INPUT --h-length 6 --h-type 1 
+--A OUTPUT -o lo --h-length 6 --h-type 1 -j ACCEPT
+--A OUTPUT -o eth134 --h-length 6 --h-type 1 -j mangle --mangle-ip-s 10.0.0.1
+--A OUTPUT -o eth432 --h-length 6 --h-type 1 -j CLASSIFY --set-class feed:babe
+--A OUTPUT -o eth432 --h-length 6 --opcode 1 --h-type 1 -j CLASSIFY --set-class feed:babe
+--A foo -i lo --h-length 6 --h-type 1 -j ACCEPT
+--A foo --h-length 6 --h-type 1 -j ACCEPT
+--A foo --h-length 6 --h-type 1 -j MARK --set-mark 12345
+--A foo --h-length 6 --opcode 1 --h-type 1 -j ACCEPT
+--A foo --h-length 6 --h-type 1 --proto-type 0x800 -j ACCEPT
+--A foo -i lo --h-length 6 --opcode 1 --h-type 1 --proto-type 0x800 -j ACCEPT
++-A INPUT -j ACCEPT -s 10.0.0.0/8 --h-length 6 --h-type 1
++-A INPUT -j ACCEPT -d 192.168.123.1 --h-length 6 --h-type 1
++-A INPUT -j ACCEPT --src-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1
++-A INPUT -j ACCEPT --dst-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1
++-A INPUT -j foo --h-length 6 --h-type 1
++-A INPUT  --h-length 6 --h-type 1
++-A OUTPUT -j ACCEPT -o lo --h-length 6 --h-type 1
++-A OUTPUT -j mangle -o eth134 --h-length 6 --h-type 1 --mangle-ip-s 10.0.0.1
++-A OUTPUT -j CLASSIFY -o eth432 --h-length 6 --h-type 1 --set-class feed:babe
++-A OUTPUT -j CLASSIFY -o eth432 --h-length 6 --opcode 1 --h-type 1 --set-class feed:babe
++-A foo -j ACCEPT -i lo --h-length 6 --h-type 1
++-A foo -j ACCEPT --h-length 6 --h-type 1
++-A foo -j MARK --h-length 6 --h-type 1 --set-mark 12345
++-A foo -j ACCEPT --h-length 6 --opcode 1 --h-type 1
++-A foo -j ACCEPT --h-length 6 --h-type 1 --proto-type 0x800
++-A foo -j ACCEPT -i lo --h-length 6 --opcode 1 --h-type 1 --proto-type 0x800
+ '
+ 
+ diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
+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 ee17da0023b82..d742c3d506305 100755
+--- a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
++++ b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
+@@ -11,7 +11,7 @@ set -e
+ DUMP='*filter
+ :OUTPUT ACCEPT
+ -A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
+--A OUTPUT --h-length 6 --h-type 1 -j mangle --mangle-ip-d 10.0.0.2
++-A OUTPUT -j mangle --h-length 6 --h-type 1 --mangle-ip-d 10.0.0.2
+ '
+ 
+ # note how mangle-ip-s is unset in second rule
+@@ -19,8 +19,8 @@ DUMP='*filter
+ EXPECT='*filter
+ :INPUT ACCEPT
+ :OUTPUT ACCEPT
+--A OUTPUT --h-length 6 --h-type 1 -j mangle --mangle-ip-s 10.0.0.1
+--A OUTPUT --h-length 6 --h-type 1 -j mangle --mangle-ip-d 10.0.0.2
++-A OUTPUT -j mangle --h-length 6 --h-type 1 --mangle-ip-s 10.0.0.1
++-A OUTPUT -j mangle --h-length 6 --h-type 1 --mangle-ip-d 10.0.0.2
+ '
+ 
+ $XT_MULTI arptables -F
+-- 
+2.20.1
+
diff --git a/SOURCES/0038-arptables-nft-Don-t-print-default-h-len-h-type-value.patch b/SOURCES/0038-arptables-nft-Don-t-print-default-h-len-h-type-value.patch
new file mode 100644
index 0000000..2c510df
--- /dev/null
+++ b/SOURCES/0038-arptables-nft-Don-t-print-default-h-len-h-type-value.patch
@@ -0,0 +1,116 @@
+From 65303eb285ba082c24b2f2150918d63ed6c8398f Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:55 +0100
+Subject: [PATCH] arptables-nft: Don't print default h-len/h-type values
+
+Default values for --h-len and --h-type being printed for rules where
+user didn't provide them is unexpected and confusing. The drawback is
+the opposite: If user provided either of them with their default value,
+they are later omitted when listing rules. Though since unlike legacy
+arptables we can't distinguish between not specified and specified with
+default value, we can't fix both - so choose to optimize for the more
+likely case.
+
+Fixes: 5aecb2d8bfdda ("arptables: pre-init hlen and ethertype")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 84331e3ed3f8eb9d53c00c221113ad16b209968a)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-arp.c                            |  4 +--
+ .../arptables/0001-arptables-save-restore_0   | 32 +++++++++----------
+ .../0002-arptables-restore-defaults_0         |  6 ++--
+ 3 files changed, 21 insertions(+), 21 deletions(-)
+
+diff --git a/iptables/nft-arp.c b/iptables/nft-arp.c
+index 2cbdf23214049..37b0985377bef 100644
+--- a/iptables/nft-arp.c
++++ b/iptables/nft-arp.c
+@@ -537,7 +537,7 @@ after_devsrc:
+ 
+ after_devdst:
+ 
+-	if (fw->arp.arhln_mask != 0) {
++	if (fw->arp.arhln_mask != 255 || fw->arp.arhln != 6) {
+ 		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHLN
+ 			? "! " : "");
+ 		printf("--h-length %d", fw->arp.arhln);
+@@ -561,7 +561,7 @@ after_devdst:
+ 		sep = " ";
+ 	}
+ 
+-	if (fw->arp.arhrd_mask != 0) {
++	if (fw->arp.arhrd_mask != 65535 || fw->arp.arhrd != htons(1)) {
+ 		uint16_t tmp = ntohs(fw->arp.arhrd);
+ 
+ 		printf("%s%s", sep, fw->arp.invflags & ARPT_INV_ARPHRD
+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 0664e3b38d5e8..e10f61cc8f95b 100755
+--- a/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
++++ b/iptables/tests/shell/testcases/arptables/0001-arptables-save-restore_0
+@@ -35,22 +35,22 @@ DUMP='*filter
+ :INPUT ACCEPT
+ :OUTPUT DROP
+ :foo -
+--A INPUT -j ACCEPT -s 10.0.0.0/8 --h-length 6 --h-type 1
+--A INPUT -j ACCEPT -d 192.168.123.1 --h-length 6 --h-type 1
+--A INPUT -j ACCEPT --src-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1
+--A INPUT -j ACCEPT --dst-mac fe:ed:ba:be:00:01 --h-length 6 --h-type 1
+--A INPUT -j foo --h-length 6 --h-type 1
+--A INPUT  --h-length 6 --h-type 1
+--A OUTPUT -j ACCEPT -o lo --h-length 6 --h-type 1
+--A OUTPUT -j mangle -o eth134 --h-length 6 --h-type 1 --mangle-ip-s 10.0.0.1
+--A OUTPUT -j CLASSIFY -o eth432 --h-length 6 --h-type 1 --set-class feed:babe
+--A OUTPUT -j CLASSIFY -o eth432 --h-length 6 --opcode 1 --h-type 1 --set-class feed:babe
+--A foo -j ACCEPT -i lo --h-length 6 --h-type 1
+--A foo -j ACCEPT --h-length 6 --h-type 1
+--A foo -j MARK --h-length 6 --h-type 1 --set-mark 12345
+--A foo -j ACCEPT --h-length 6 --opcode 1 --h-type 1
+--A foo -j ACCEPT --h-length 6 --h-type 1 --proto-type 0x800
+--A foo -j ACCEPT -i lo --h-length 6 --opcode 1 --h-type 1 --proto-type 0x800
++-A INPUT -j ACCEPT -s 10.0.0.0/8
++-A INPUT -j ACCEPT -d 192.168.123.1
++-A INPUT -j ACCEPT --src-mac fe:ed:ba:be:00:01
++-A INPUT -j ACCEPT --dst-mac fe:ed:ba:be:00:01
++-A INPUT -j foo
++-A INPUT 
++-A OUTPUT -j ACCEPT -o lo
++-A OUTPUT -j mangle -o eth134 --mangle-ip-s 10.0.0.1
++-A OUTPUT -j CLASSIFY -o eth432 --set-class feed:babe
++-A OUTPUT -j CLASSIFY -o eth432 --opcode 1 --set-class feed:babe
++-A foo -j ACCEPT -i lo
++-A foo -j ACCEPT
++-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
+ '
+ 
+ diff -u <(echo -e "$DUMP") <($XT_MULTI arptables-save)
+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 d742c3d506305..b2ed95e87bb40 100755
+--- a/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
++++ b/iptables/tests/shell/testcases/arptables/0002-arptables-restore-defaults_0
+@@ -11,7 +11,7 @@ set -e
+ DUMP='*filter
+ :OUTPUT ACCEPT
+ -A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
+--A OUTPUT -j mangle --h-length 6 --h-type 1 --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
+@@ -19,8 +19,8 @@ DUMP='*filter
+ EXPECT='*filter
+ :INPUT ACCEPT
+ :OUTPUT ACCEPT
+--A OUTPUT -j mangle --h-length 6 --h-type 1 --mangle-ip-s 10.0.0.1
+--A OUTPUT -j mangle --h-length 6 --h-type 1 --mangle-ip-d 10.0.0.2
++-A OUTPUT -j mangle --mangle-ip-s 10.0.0.1
++-A OUTPUT -j mangle --mangle-ip-d 10.0.0.2
+ '
+ 
+ $XT_MULTI arptables -F
+-- 
+2.20.1
+
diff --git a/SOURCES/0039-tests-shell-Add-arptables-nft-verbose-output-test.patch b/SOURCES/0039-tests-shell-Add-arptables-nft-verbose-output-test.patch
new file mode 100644
index 0000000..13948ac
--- /dev/null
+++ b/SOURCES/0039-tests-shell-Add-arptables-nft-verbose-output-test.patch
@@ -0,0 +1,90 @@
+From a63cfa813bb173414bfe37f8a8e2e5f96a99bb99 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 31 Jan 2019 16:12:56 +0100
+Subject: [PATCH] tests: shell: Add arptables-nft verbose output test
+
+With arptables-nft output being in a very good state now, add a test to
+ensure it stays that way.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 3d8f261c565a024c13d627b18a0fcafc76de8f2c)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ .../arptables/0003-arptables-verbose-output_0 | 64 +++++++++++++++++++
+ 1 file changed, 64 insertions(+)
+ create mode 100755 iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
+
+diff --git a/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0 b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
+new file mode 100755
+index 0000000000000..35126fa7d717c
+--- /dev/null
++++ b/iptables/tests/shell/testcases/arptables/0003-arptables-verbose-output_0
+@@ -0,0 +1,64 @@
++#!/bin/bash
++
++set -e
++set -x
++
++# there is no legacy backend to test
++[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; }
++
++$XT_MULTI arptables -N foo
++
++# check verbose output matches expectations
++
++RULE1='-i eth23 -j ACCEPT'
++VOUT1='-j ACCEPT -i eth23 -o *'
++
++RULE2='-i eth23'
++VOUT2='-i eth23 -o *'
++
++RULE3='-i eth23 -j MARK --set-mark 42'
++VOUT3='-j MARK -i eth23 -o * --set-mark 42'
++
++RULE4='-o eth23 -j CLASSIFY --set-class 23:42'
++VOUT4='-j CLASSIFY -i * -o eth23 --set-class 23:42'
++
++RULE5='-o eth23 -j foo'
++VOUT5='-j foo -i * -o eth23'
++
++RULE6='-o eth23 -j mangle --mangle-ip-s 10.0.0.1'
++VOUT6='-j mangle -i * -o eth23 --mangle-ip-s 10.0.0.1'
++
++diff -u -Z <(echo -e "$VOUT1") <($XT_MULTI arptables -v -A INPUT $RULE1)
++diff -u -Z <(echo -e "$VOUT2") <($XT_MULTI arptables -v -A INPUT $RULE2)
++diff -u -Z <(echo -e "$VOUT3") <($XT_MULTI arptables -v -A INPUT $RULE3)
++diff -u -Z <(echo -e "$VOUT4") <($XT_MULTI arptables -v -A OUTPUT $RULE4)
++diff -u -Z <(echo -e "$VOUT5") <($XT_MULTI arptables -v -A OUTPUT $RULE5)
++diff -u -Z <(echo -e "$VOUT6") <($XT_MULTI arptables -v -A foo $RULE6)
++
++EXPECT='Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
++-j ACCEPT -i eth23 -o *, pcnt=0 -- bcnt=0
++-i eth23 -o *, pcnt=0 -- bcnt=0
++-j MARK -i eth23 -o * --set-mark 42, pcnt=0 -- bcnt=0
++
++Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
++-j CLASSIFY -i * -o eth23 --set-class 23:42, pcnt=0 -- bcnt=0
++-j foo -i * -o eth23, pcnt=0 -- bcnt=0
++
++Chain foo (1 references)
++-j mangle -i * -o eth23 --mangle-ip-s 10.0.0.1, pcnt=0 -- bcnt=0'
++
++diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI arptables -v -n -L)
++
++EXPECT='*filter
++:INPUT ACCEPT
++:OUTPUT ACCEPT
++:foo -
++-A INPUT -j ACCEPT -i eth23
++-A INPUT -i eth23
++-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
++'
++
++diff -u -Z <(echo -e "$EXPECT") <($XT_MULTI arptables-save)
+-- 
+2.20.1
+
diff --git a/SOURCES/0040-arptables-nft-Set-h-type-h-length-masks-by-default-t.patch b/SOURCES/0040-arptables-nft-Set-h-type-h-length-masks-by-default-t.patch
new file mode 100644
index 0000000..6a486aa
--- /dev/null
+++ b/SOURCES/0040-arptables-nft-Set-h-type-h-length-masks-by-default-t.patch
@@ -0,0 +1,40 @@
+From 5ca3f6f4c4e673ecfab59ca81edd8bec69a7f43b Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Fri, 1 Feb 2019 17:06:18 +0100
+Subject: [PATCH] arptables-nft: Set h-type/h-length masks by default, too
+
+These masks are not used in nftables backend, but mangle extension
+checks arhln_mask value to make sure --h-length was given (which is
+implicitly the case).
+
+Fixes: 5aecb2d8bfdda ("arptables: pre-init hlen and ethertype")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit a1da179b0ff3783badca352a42808f4398dd1a98)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/xtables-arp.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
+index 819e7e6c94034..2dce1a52f16fd 100644
+--- a/iptables/xtables-arp.c
++++ b/iptables/xtables-arp.c
+@@ -909,8 +909,12 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
+ {
+ 	struct iptables_command_state cs = {
+ 		.jumpto = "",
+-		.arp.arp.arhln = 6,
+-		.arp.arp.arhrd = htons(ARPHRD_ETHER),
++		.arp.arp = {
++			.arhln = 6,
++			.arhln_mask = 255,
++			.arhrd = htons(ARPHRD_ETHER),
++			.arhrd_mask = 65535,
++		},
+ 	};
+ 	int invert = 0;
+ 	unsigned int nsaddrs = 0, ndaddrs = 0;
+-- 
+2.20.1
+
diff --git a/SOURCES/0041-nft-Add-new-builtin-chains-to-cache-immediately.patch b/SOURCES/0041-nft-Add-new-builtin-chains-to-cache-immediately.patch
new file mode 100644
index 0000000..4ab7b4b
--- /dev/null
+++ b/SOURCES/0041-nft-Add-new-builtin-chains-to-cache-immediately.patch
@@ -0,0 +1,95 @@
+From 598f69c07427a1457c3ac7da766d1c07d64b63ce Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 15 Jan 2019 23:23:03 +0100
+Subject: [PATCH] nft: Add new builtin chains to cache immediately
+
+Newly created builtin chains missing from cache was the sole reason for
+the immediate calls to nft_commit(). With nft_chain_builtin_add()
+inserting the new chain into the table's chain list, this is not needed
+anymore. Just make sure batch_obj_del() doesn't free the payload of
+NFT_COMPAT_CHAIN_ADD jobs since it contains the new chain which has
+been added to cache.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 2b801fc515ae094d04207e840ed191196292b968)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 30 +++++++++---------------------
+ 1 file changed, 9 insertions(+), 21 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 9c0ad9a2d054f..c2af1a6fd0985 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -644,6 +644,7 @@ static void nft_chain_builtin_add(struct nft_handle *h,
+ 		return;
+ 
+ 	batch_chain_add(h, NFT_COMPAT_CHAIN_ADD, c);
++	nftnl_chain_list_add_tail(c, h->table[table->type].chain_cache);
+ }
+ 
+ /* find if built-in table already exists */
+@@ -1216,8 +1217,11 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ 		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ 
+ 	c = nft_chain_find(h, table, chain);
+-	if (c)
+-		nftnl_chain_rule_add_tail(r, c);
++	if (!c) {
++		errno = ENOENT;
++		return 0;
++	}
++	nftnl_chain_rule_add_tail(r, c);
+ 
+ 	return 1;
+ }
+@@ -2282,16 +2286,8 @@ int nft_rule_list(struct nft_handle *h, const char *chain, const char *table,
+ 	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) {
++	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ 		nft_xt_builtin_init(h, table);
+-		/* Force table and chain creation, otherwise first iptables -L
+-		 * lists no table/chains.
+-		 */
+-		if (!list_empty(&h->obj_list)) {
+-			nft_commit(h);
+-			flush_chain_cache(h, NULL);
+-		}
+-	}
+ 
+ 	ops = nft_family_ops_lookup(h->family);
+ 
+@@ -2397,16 +2393,8 @@ int nft_rule_list_save(struct nft_handle *h, const char *chain,
+ 	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) {
++	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+ 		nft_xt_builtin_init(h, table);
+-		/* Force table and chain creation, otherwise first iptables -L
+-		 * lists no table/chains.
+-		 */
+-		if (!list_empty(&h->obj_list)) {
+-			nft_commit(h);
+-			flush_chain_cache(h, NULL);
+-		}
+-	}
+ 
+ 	if (!nft_is_table_compatible(h, table)) {
+ 		xtables_error(OTHER_PROBLEM, "table `%s' is incompatible, use 'nft' tool.\n", table);
+@@ -2525,8 +2513,8 @@ static void batch_obj_del(struct nft_handle *h, struct obj_update *o)
+ 		break;
+ 	case NFT_COMPAT_CHAIN_ZERO:
+ 	case NFT_COMPAT_CHAIN_USER_ADD:
+-		break;
+ 	case NFT_COMPAT_CHAIN_ADD:
++		break;
+ 	case NFT_COMPAT_CHAIN_USER_DEL:
+ 	case NFT_COMPAT_CHAIN_USER_FLUSH:
+ 	case NFT_COMPAT_CHAIN_UPDATE:
+-- 
+2.20.1
+
diff --git a/SOURCES/0042-xtables-Fix-position-of-replaced-rules-in-cache.patch b/SOURCES/0042-xtables-Fix-position-of-replaced-rules-in-cache.patch
new file mode 100644
index 0000000..4cd841a
--- /dev/null
+++ b/SOURCES/0042-xtables-Fix-position-of-replaced-rules-in-cache.patch
@@ -0,0 +1,168 @@
+From 53422c35d925973702e043ac69119f87e08399e0 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 15 Jan 2019 23:23:04 +0100
+Subject: [PATCH] xtables: Fix position of replaced rules in cache
+
+When replacing a rule, the replacement was simply appended to the
+chain's rule list. Instead, insert it where the rule it replaces was.
+
+This also fixes for zero counters command to remove the old rule from
+cache.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 5ca9acf51adf9dcc8e0d82cd8f5b9b2514f900ee)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c         | 34 +++++++++++++++++-----------------
+ iptables/nft.h         |  2 +-
+ iptables/xtables-arp.c |  2 +-
+ iptables/xtables-eb.c  |  2 +-
+ iptables/xtables.c     |  4 ++--
+ 5 files changed, 22 insertions(+), 22 deletions(-)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index c2af1a6fd0985..76764fde4e9fb 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1186,7 +1186,7 @@ nft_chain_find(struct nft_handle *h, const char *table, const char *chain);
+ 
+ int
+ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+-		void *data, uint64_t handle, bool verbose)
++		void *data, struct nftnl_rule *ref, bool verbose)
+ {
+ 	struct nftnl_chain *c;
+ 	struct nftnl_rule *r;
+@@ -1202,8 +1202,9 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ 	if (r == NULL)
+ 		return 0;
+ 
+-	if (handle > 0) {
+-		nftnl_rule_set(r, NFTNL_RULE_HANDLE, &handle);
++	if (ref) {
++		nftnl_rule_set_u64(r, NFTNL_RULE_HANDLE,
++				   nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE));
+ 		type = NFT_COMPAT_RULE_REPLACE;
+ 	} else
+ 		type = NFT_COMPAT_RULE_APPEND;
+@@ -1216,12 +1217,17 @@ nft_rule_append(struct nft_handle *h, const char *chain, const char *table,
+ 	if (verbose)
+ 		h->ops->print_rule(r, 0, FMT_PRINT_RULE);
+ 
+-	c = nft_chain_find(h, table, chain);
+-	if (!c) {
+-		errno = ENOENT;
+-		return 0;
++	if (ref) {
++		nftnl_chain_rule_insert_at(r, ref);
++		nftnl_chain_rule_del(r);
++	} else {
++		c = nft_chain_find(h, table, chain);
++		if (!c) {
++			errno = ENOENT;
++			return 0;
++		}
++		nftnl_chain_rule_add_tail(r, c);
+ 	}
+-	nftnl_chain_rule_add_tail(r, c);
+ 
+ 	return 1;
+ }
+@@ -2109,7 +2115,7 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 			r = nft_rule_find(h, c, data, rulenum - 1);
+ 			if (r != NULL)
+ 				return nft_rule_append(h, chain, table, data,
+-						       0, verbose);
++						       NULL, verbose);
+ 
+ 			errno = ENOENT;
+ 			goto err;
+@@ -2181,11 +2187,7 @@ int nft_rule_replace(struct nft_handle *h, const char *chain,
+ 			(unsigned long long)
+ 			nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE));
+ 
+-		nftnl_rule_list_del(r);
+-
+-		ret = nft_rule_append(h, chain, table, data,
+-				      nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
+-				      verbose);
++		ret = nft_rule_append(h, chain, table, data, r, verbose);
+ 	} else
+ 		errno = ENOENT;
+ 
+@@ -2461,9 +2463,7 @@ int nft_rule_zero_counters(struct nft_handle *h, const char *chain,
+ 
+ 	cs.counters.pcnt = cs.counters.bcnt = 0;
+ 
+-	ret =  nft_rule_append(h, chain, table, &cs,
+-			       nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE),
+-			       false);
++	ret =  nft_rule_append(h, chain, table, &cs, r, false);
+ 
+ error:
+ 	return ret;
+diff --git a/iptables/nft.h b/iptables/nft.h
+index dfdffd69342db..97d73c8b534be 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -98,7 +98,7 @@ bool nft_chain_exists(struct nft_handle *h, const char *table, const char *chain
+  */
+ struct nftnl_rule;
+ 
+-int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, uint64_t handle, bool verbose);
++int nft_rule_append(struct nft_handle *h, const char *chain, const char *table, void *data, struct nftnl_rule *ref, bool verbose);
+ int nft_rule_insert(struct nft_handle *h, const char *chain, const char *table, void *data, int rulenum, bool verbose);
+ int nft_rule_check(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
+ int nft_rule_delete(struct nft_handle *h, const char *chain, const char *table, void *data, bool verbose);
+diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
+index 2dce1a52f16fd..18cdced627c55 100644
+--- a/iptables/xtables-arp.c
++++ b/iptables/xtables-arp.c
+@@ -825,7 +825,7 @@ append_entry(struct nft_handle *h,
+ 		for (j = 0; j < ndaddrs; j++) {
+ 			cs->arp.arp.tgt.s_addr = daddrs[j].s_addr;
+ 			if (append) {
+-				ret = nft_rule_append(h, chain, table, cs, 0,
++				ret = nft_rule_append(h, chain, table, cs, NULL,
+ 						      verbose);
+ 			} else {
+ 				ret = nft_rule_insert(h, chain, table, cs,
+diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
+index 871891442e431..4c52c29aa4817 100644
+--- a/iptables/xtables-eb.c
++++ b/iptables/xtables-eb.c
+@@ -171,7 +171,7 @@ append_entry(struct nft_handle *h,
+ 	int ret = 1;
+ 
+ 	if (append)
+-		ret = nft_rule_append(h, chain, table, cs, 0, verbose);
++		ret = nft_rule_append(h, chain, table, cs, NULL, verbose);
+ 	else
+ 		ret = nft_rule_insert(h, chain, table, cs, rule_nr, verbose);
+ 
+diff --git a/iptables/xtables.c b/iptables/xtables.c
+index da11e8cc159a0..d0167e6396975 100644
+--- a/iptables/xtables.c
++++ b/iptables/xtables.c
+@@ -406,7 +406,7 @@ add_entry(const char *chain,
+ 
+ 				if (append) {
+ 					ret = nft_rule_append(h, chain, table,
+-							      cs, 0,
++							      cs, NULL,
+ 							      verbose);
+ 				} else {
+ 					ret = nft_rule_insert(h, chain, table,
+@@ -426,7 +426,7 @@ add_entry(const char *chain,
+ 				       &d.mask.v6[j], sizeof(struct in6_addr));
+ 				if (append) {
+ 					ret = nft_rule_append(h, chain, table,
+-							      cs, 0,
++							      cs, NULL,
+ 							      verbose);
+ 				} else {
+ 					ret = nft_rule_insert(h, chain, table,
+-- 
+2.20.1
+
diff --git a/SOURCES/0043-xtables-Fix-for-inserting-rule-at-wrong-position.patch b/SOURCES/0043-xtables-Fix-for-inserting-rule-at-wrong-position.patch
new file mode 100644
index 0000000..1036b84
--- /dev/null
+++ b/SOURCES/0043-xtables-Fix-for-inserting-rule-at-wrong-position.patch
@@ -0,0 +1,282 @@
+From 9a6553277dc28dc2e4082646e14e49188aa6f096 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 15 Jan 2019 23:23:05 +0100
+Subject: [PATCH] xtables: Fix for inserting rule at wrong position
+
+iptables-restore allows to insert rules at a certain position which is
+problematic for iptables-nft to realize since rule position is not
+determined by number but handle of previous or following rule and in
+case the rules surrounding the new one are new as well, they don't have
+a handle to refer to yet.
+
+Fix this by making use of NFTNL_RULE_POSITION_ID attribute: When
+inserting before a rule which does not have a handle, refer to it using
+its NFTNL_RULE_ID value. If the latter doesn't exist either, assign a
+new one to it.
+
+The last used rule ID value is tracked in a new field of struct
+nft_handle which is incremented before each use.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+(cherry picked from commit 7ea0b7d809229973d950ed99845bdd0b2eb4cbb7)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c                                |  30 +++--
+ iptables/nft.h                                |   1 +
+ .../ipt-restore/0003-restore-ordering_0       | 117 ++++++++++++++++++
+ .../testcases/iptables/0005-rule-replace_0    |  38 ++++++
+ 4 files changed, 176 insertions(+), 10 deletions(-)
+ create mode 100755 iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
+ create mode 100755 iptables/tests/shell/testcases/iptables/0005-rule-replace_0
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 76764fde4e9fb..2fa973cf03975 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -2065,16 +2065,30 @@ int nft_rule_delete(struct nft_handle *h, const char *chain,
+ static struct nftnl_rule *
+ nft_rule_add(struct nft_handle *h, const char *chain,
+ 	     const char *table, struct iptables_command_state *cs,
+-	     uint64_t handle, bool verbose)
++	     struct nftnl_rule *ref, bool verbose)
+ {
+ 	struct nftnl_rule *r;
++	uint64_t ref_id;
+ 
+ 	r = nft_rule_new(h, chain, table, cs);
+ 	if (r == NULL)
+ 		return NULL;
+ 
+-	if (handle > 0)
+-		nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle);
++	if (ref) {
++		ref_id = nftnl_rule_get_u64(ref, NFTNL_RULE_HANDLE);
++		if (ref_id > 0) {
++			nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, ref_id);
++			DEBUGP("adding after rule handle %"PRIu64"\n", ref_id);
++		} else {
++			ref_id = nftnl_rule_get_u32(ref, NFTNL_RULE_ID);
++			if (!ref_id) {
++				ref_id = ++h->rule_id;
++				nftnl_rule_set_u32(ref, NFTNL_RULE_ID, ref_id);
++			}
++			nftnl_rule_set_u32(r, NFTNL_RULE_POSITION_ID, ref_id);
++			DEBUGP("adding after rule ID %"PRIu64"\n", ref_id);
++		}
++	}
+ 
+ 	if (batch_rule_add(h, NFT_COMPAT_RULE_INSERT, r) < 0) {
+ 		nftnl_rule_free(r);
+@@ -2090,9 +2104,8 @@ nft_rule_add(struct nft_handle *h, const char *chain,
+ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 		    const char *table, void *data, int rulenum, bool verbose)
+ {
+-	struct nftnl_rule *r, *new_rule;
++	struct nftnl_rule *r = NULL, *new_rule;
+ 	struct nftnl_chain *c;
+-	uint64_t handle = 0;
+ 
+ 	/* If built-in chains don't exist for this table, create them */
+ 	if (nft_xtables_config_load(h, XTABLES_CONFIG_DEFAULT, 0) < 0)
+@@ -2120,16 +2133,13 @@ int nft_rule_insert(struct nft_handle *h, const char *chain,
+ 			errno = ENOENT;
+ 			goto err;
+ 		}
+-
+-		handle = nftnl_rule_get_u64(r, NFTNL_RULE_HANDLE);
+-		DEBUGP("adding after rule handle %"PRIu64"\n", handle);
+ 	}
+ 
+-	new_rule = nft_rule_add(h, chain, table, data, handle, verbose);
++	new_rule = nft_rule_add(h, chain, table, data, r, verbose);
+ 	if (!new_rule)
+ 		goto err;
+ 
+-	if (handle)
++	if (r)
+ 		nftnl_chain_rule_insert_at(new_rule, r);
+ 	else
+ 		nftnl_chain_rule_add(new_rule, c);
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 97d73c8b534be..0726923a63dd4 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -32,6 +32,7 @@ struct nft_handle {
+ 	struct mnl_socket	*nl;
+ 	uint32_t		portid;
+ 	uint32_t		seq;
++	uint32_t		rule_id;
+ 	struct list_head	obj_list;
+ 	int			obj_list_num;
+ 	struct nftnl_batch	*batch;
+diff --git a/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0 b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
+new file mode 100755
+index 0000000000000..51f2422e15259
+--- /dev/null
++++ b/iptables/tests/shell/testcases/ipt-restore/0003-restore-ordering_0
+@@ -0,0 +1,117 @@
++#!/bin/bash
++
++# Make sure iptables-restore does the right thing
++# when encountering INSERT rules with index.
++
++set -e
++
++# show rules, drop uninteresting policy settings
++ipt_show() {
++	$XT_MULTI iptables -S | grep -v '^-P'
++}
++
++# basic issue reproducer
++
++$XT_MULTI iptables-restore <<EOF
++*filter
++-A FORWARD -m comment --comment "appended rule" -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
++COMMIT
++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'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++# insert rules into existing ruleset
++
++$XT_MULTI iptables-restore --noflush <<EOF
++*filter
++-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
++COMMIT
++EOF
++
++EXPECT='-A FORWARD -m comment --comment "rule 0.5" -j ACCEPT
++-A FORWARD -m comment --comment "rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "rule 1.5" -j ACCEPT
++-A FORWARD -m comment --comment "rule 2" -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'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++# insert rules in between added ones
++
++$XT_MULTI iptables-restore <<EOF
++*filter
++-A FORWARD -m comment --comment "appended rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 2" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 3" -j ACCEPT
++-I FORWARD 1 -m comment --comment "rule 1" -j ACCEPT
++-I FORWARD 3 -m comment --comment "rule 2" -j ACCEPT
++-I FORWARD 5 -m comment --comment "rule 3" -j ACCEPT
++COMMIT
++EOF
++
++EXPECT='-A FORWARD -m comment --comment "rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "rule 2" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 2" -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++# test rule deletion in dump files
++
++$XT_MULTI iptables-restore --noflush <<EOF
++*filter
++-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
++COMMIT
++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 "manually replaced rule 2" -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT
++-A FORWARD -m comment --comment "appended rule 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++# test rule replacement in dump files
++
++$XT_MULTI iptables-restore <<EOF
++*filter
++-A FORWARD -m comment --comment "rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "rule to be replaced" -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT
++COMMIT
++EOF
++
++$XT_MULTI iptables-restore --noflush <<EOF
++*filter
++-R FORWARD 2 -m comment --comment "replacement" -j ACCEPT
++-I FORWARD 2 -m comment --comment "insert referencing replaced rule" -j ACCEPT
++COMMIT
++EOF
++
++EXPECT='-A FORWARD -m comment --comment "rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "insert referencing replaced rule" -j ACCEPT
++-A FORWARD -m comment --comment replacement -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
+diff --git a/iptables/tests/shell/testcases/iptables/0005-rule-replace_0 b/iptables/tests/shell/testcases/iptables/0005-rule-replace_0
+new file mode 100755
+index 0000000000000..5a3e922e50672
+--- /dev/null
++++ b/iptables/tests/shell/testcases/iptables/0005-rule-replace_0
+@@ -0,0 +1,38 @@
++#!/bin/bash
++
++# test rule replacement
++
++set -e
++
++# show rules, drop uninteresting policy settings
++ipt_show() {
++	$XT_MULTI iptables -S | grep -v '^-P'
++}
++
++$XT_MULTI iptables -A FORWARD -m comment --comment "rule 1" -j ACCEPT
++$XT_MULTI iptables -A FORWARD -m comment --comment "rule 2" -j ACCEPT
++$XT_MULTI iptables -A FORWARD -m comment --comment "rule 3" -j ACCEPT
++
++$XT_MULTI iptables -R FORWARD 2 -m comment --comment "replaced 2" -j ACCEPT
++
++EXPECT='-A FORWARD -m comment --comment "rule 1" -j ACCEPT
++-A FORWARD -m comment --comment "replaced 2" -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++$XT_MULTI iptables -R FORWARD 1 -m comment --comment "replaced 1" -j ACCEPT
++
++EXPECT='-A FORWARD -m comment --comment "replaced 1" -j ACCEPT
++-A FORWARD -m comment --comment "replaced 2" -j ACCEPT
++-A FORWARD -m comment --comment "rule 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
++
++$XT_MULTI iptables -R FORWARD 3 -m comment --comment "replaced 3" -j ACCEPT
++
++EXPECT='-A FORWARD -m comment --comment "replaced 1" -j ACCEPT
++-A FORWARD -m comment --comment "replaced 2" -j ACCEPT
++-A FORWARD -m comment --comment "replaced 3" -j ACCEPT'
++
++diff -u -Z <(echo -e "$EXPECT") <(ipt_show)
+-- 
+2.20.1
+
diff --git a/SOURCES/0044-xtables-Fix-for-crash-when-comparing-rules-with-stan.patch b/SOURCES/0044-xtables-Fix-for-crash-when-comparing-rules-with-stan.patch
new file mode 100644
index 0000000..53b6b42
--- /dev/null
+++ b/SOURCES/0044-xtables-Fix-for-crash-when-comparing-rules-with-stan.patch
@@ -0,0 +1,97 @@
+From be42a2a4a9382a445454eeb9db09aef52f699917 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Fri, 1 Feb 2019 19:17:50 +0100
+Subject: [PATCH] xtables: Fix for crash when comparing rules with standard
+ target
+
+When parsing an nftnl_rule with a standard verdict,
+nft_rule_to_iptables_command_state() initialized cs->target but didn't
+care about cs->target->t. When later comparing that rule to another,
+compare_targets() crashed due to unconditional access to t's fields.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit a880cc28358a32f96467e248266973b6ab83f080)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-shared.c                         | 23 +++++++++++++++----
+ .../testcases/iptables/0005-delete-rules_0    |  7 ++++++
+ iptables/xtables.c                            |  4 +++-
+ 3 files changed, 29 insertions(+), 5 deletions(-)
+ create mode 100755 iptables/tests/shell/testcases/iptables/0005-delete-rules_0
+
+diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
+index 7b8ca5e4becaf..dfc1c803cb68d 100644
+--- a/iptables/nft-shared.c
++++ b/iptables/nft-shared.c
+@@ -660,19 +660,34 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+ 		match->m = m;
+ 	}
+ 
+-	if (cs->target != NULL)
++	if (cs->target != NULL) {
+ 		cs->jumpto = cs->target->name;
+-	else if (cs->jumpto != NULL)
++	} else if (cs->jumpto != NULL) {
++		struct xt_entry_target *t;
++		uint32_t size;
++
+ 		cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+-	else
++		if (!cs->target)
++			return;
++
++		size = XT_ALIGN(sizeof(struct xt_entry_target)) + cs->target->size;
++		t = xtables_calloc(1, size);
++		t->u.target_size = size;
++		t->u.user.revision = cs->target->revision;
++		strcpy(t->u.user.name, cs->jumpto);
++		cs->target->t = t;
++	} else {
+ 		cs->jumpto = "";
++	}
+ }
+ 
+ void nft_clear_iptables_command_state(struct iptables_command_state *cs)
+ {
+ 	xtables_rule_matches_free(&cs->matches);
+-	if (cs->target)
++	if (cs->target) {
+ 		free(cs->target->t);
++		cs->target->t = NULL;
++	}
+ }
+ 
+ void print_header(unsigned int format, const char *chain, const char *pol,
+diff --git a/iptables/tests/shell/testcases/iptables/0005-delete-rules_0 b/iptables/tests/shell/testcases/iptables/0005-delete-rules_0
+new file mode 100755
+index 0000000000000..9312fd53c3437
+--- /dev/null
++++ b/iptables/tests/shell/testcases/iptables/0005-delete-rules_0
+@@ -0,0 +1,7 @@
++#!/bin/bash
++
++# test for crash when comparing rules with standard target
++
++$XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j DROP
++$XT_MULTI iptables -D FORWARD -i eth23 -o eth42 -j REJECT
++[[ $? -eq 1 ]] || exit 1
+diff --git a/iptables/xtables.c b/iptables/xtables.c
+index d0167e6396975..eaa9fedeb03bb 100644
+--- a/iptables/xtables.c
++++ b/iptables/xtables.c
+@@ -1185,8 +1185,10 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table,
+ 	*table = p.table;
+ 
+ 	xtables_rule_matches_free(&cs.matches);
+-	if (cs.target)
++	if (cs.target) {
+ 		free(cs.target->t);
++		cs.target->t = NULL;
++	}
+ 
+ 	if (h->family == AF_INET) {
+ 		free(args.s.addr.v4);
+-- 
+2.20.1
+
diff --git a/SOURCES/0045-xtables-Fix-for-false-positive-rule-matching.patch b/SOURCES/0045-xtables-Fix-for-false-positive-rule-matching.patch
new file mode 100644
index 0000000..baf8875
--- /dev/null
+++ b/SOURCES/0045-xtables-Fix-for-false-positive-rule-matching.patch
@@ -0,0 +1,258 @@
+From ec020c1d789d9d8562105af2cd2fe23b797505d0 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Mon, 4 Feb 2019 21:52:53 +0100
+Subject: [PATCH] xtables: Fix for false-positive rule matching
+
+When comparing two rules with non-standard targets, differences in
+targets' payloads wasn't respected.
+
+The cause is a rather hideous one: Unlike xtables_find_match(),
+xtables_find_target() did not care whether the found target was already
+in use or not, so the same target instance was assigned to both rules
+and therefore payload comparison happened over the same memory location.
+
+With legacy iptables it is not possible to reuse a target: The only case
+where two rules (i.e., iptables_command_state instances) could exist at
+the same time is when comparing rules, but that's handled using libiptc.
+
+The above change clashes with ebtables-nft's reuse of target objects:
+While input parsing still just assigns the object from xtables_targets
+list, rule conversion from nftnl to iptables_command_state allocates new
+data. To fix this, make ebtables-nft input parsing use the common
+command_jump() routine instead of its own simplified copy. In turn, this
+also eliminates the ebtables-nft-specific variants of parse_target(),
+though with a slight change of behaviour: Names of user-defined chains
+are no longer allowed to contain up to 31 but merely 28 characters.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 148131f20421046fea028e638581e938ec985783)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-bridge.c                         | 10 ++++
+ iptables/nft-bridge.h                         |  2 -
+ iptables/nft-shared.c                         |  5 ++
+ .../testcases/iptables/0005-delete-rules_0    |  7 +++
+ iptables/xtables-eb-translate.c               | 24 +---------
+ iptables/xtables-eb.c                         | 47 +------------------
+ libxtables/xtables.c                          | 18 ++++++-
+ 7 files changed, 41 insertions(+), 72 deletions(-)
+
+diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
+index ad583a60c424d..140fcf0a31b84 100644
+--- a/iptables/nft-bridge.c
++++ b/iptables/nft-bridge.c
+@@ -45,6 +45,16 @@ void ebt_cs_clean(struct iptables_command_state *cs)
+ 		free(m);
+ 		m = nm;
+ 	}
++
++	if (cs->target) {
++		free(cs->target->t);
++		cs->target->t = NULL;
++
++		if (cs->target == cs->target->next) {
++			free(cs->target);
++			cs->target = NULL;
++		}
++	}
+ }
+ 
+ static void ebt_print_mac(const unsigned char *mac)
+diff --git a/iptables/nft-bridge.h b/iptables/nft-bridge.h
+index de52cd7195bbb..d90066f1030a2 100644
+--- a/iptables/nft-bridge.h
++++ b/iptables/nft-bridge.h
+@@ -32,7 +32,6 @@ int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mas
+  */
+ 
+ #define EBT_TABLE_MAXNAMELEN 32
+-#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ #define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+ 
+ /* verdicts >0 are "branches" */
+@@ -122,6 +121,5 @@ void ebt_add_match(struct xtables_match *m,
+ void ebt_add_watcher(struct xtables_target *watcher,
+                      struct iptables_command_state *cs);
+ int ebt_command_default(struct iptables_command_state *cs);
+-struct xtables_target *ebt_command_jump(const char *jumpto);
+ 
+ #endif
+diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
+index dfc1c803cb68d..ce40787f92f7d 100644
+--- a/iptables/nft-shared.c
++++ b/iptables/nft-shared.c
+@@ -687,6 +687,11 @@ void nft_clear_iptables_command_state(struct iptables_command_state *cs)
+ 	if (cs->target) {
+ 		free(cs->target->t);
+ 		cs->target->t = NULL;
++
++		if (cs->target == cs->target->next) {
++			free(cs->target);
++			cs->target = NULL;
++		}
+ 	}
+ }
+ 
+diff --git a/iptables/tests/shell/testcases/iptables/0005-delete-rules_0 b/iptables/tests/shell/testcases/iptables/0005-delete-rules_0
+index 9312fd53c3437..5038cbce5a5cf 100755
+--- a/iptables/tests/shell/testcases/iptables/0005-delete-rules_0
++++ b/iptables/tests/shell/testcases/iptables/0005-delete-rules_0
+@@ -5,3 +5,10 @@
+ $XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j DROP
+ $XT_MULTI iptables -D FORWARD -i eth23 -o eth42 -j REJECT
+ [[ $? -eq 1 ]] || exit 1
++
++# test incorrect deletion of rules with deviating payload
++# in non-standard target
++
++$XT_MULTI iptables -A FORWARD -i eth23 -o eth42 -j MARK --set-mark 23
++$XT_MULTI iptables -D FORWARD -i eth23 -o eth42 -j MARK --set-mark 42
++[[ $? -eq 1 ]] || exit 1
+diff --git a/iptables/xtables-eb-translate.c b/iptables/xtables-eb-translate.c
+index f98c385555eb1..0fe14d2d0db32 100644
+--- a/iptables/xtables-eb-translate.c
++++ b/iptables/xtables-eb-translate.c
+@@ -64,27 +64,6 @@ static int parse_rule_number(const char *rule)
+ 	return rule_nr;
+ }
+ 
+-static const char *
+-parse_target(const char *targetname)
+-{
+-	const char *ptr;
+-
+-	if (strlen(targetname) < 1)
+-		xtables_error(PARAMETER_PROBLEM,
+-			      "Invalid target name (too short)");
+-
+-	if (strlen(targetname)+1 > EBT_CHAIN_MAXNAMELEN)
+-		xtables_error(PARAMETER_PROBLEM,
+-			      "Invalid target '%s' (%d chars max)",
+-			      targetname, EBT_CHAIN_MAXNAMELEN);
+-
+-	for (ptr = targetname; *ptr; ptr++)
+-		if (isspace(*ptr))
+-			xtables_error(PARAMETER_PROBLEM,
+-				      "Invalid target name `%s'", targetname);
+-	return targetname;
+-}
+-
+ static int get_current_chain(const char *chain)
+ {
+ 	if (strcmp(chain, "PREROUTING") == 0)
+@@ -411,8 +390,7 @@ print_zero:
+ 				break;
+ 			} else if (c == 'j') {
+ 				ebt_check_option2(&flags, OPT_JUMP);
+-				cs.jumpto = parse_target(optarg);
+-				cs.target = ebt_command_jump(cs.jumpto);
++				command_jump(&cs);
+ 				break;
+ 			} else if (c == 's') {
+ 				ebt_check_option2(&flags, OPT_SOURCE);
+diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
+index 4c52c29aa4817..55cb0fe204748 100644
+--- a/iptables/xtables-eb.c
++++ b/iptables/xtables-eb.c
+@@ -139,27 +139,6 @@ static int parse_rule_number(const char *rule)
+ 	return rule_nr;
+ }
+ 
+-static const char *
+-parse_target(const char *targetname)
+-{
+-	const char *ptr;
+-
+-	if (strlen(targetname) < 1)
+-		xtables_error(PARAMETER_PROBLEM,
+-			      "Invalid target name (too short)");
+-
+-	if (strlen(targetname)+1 > EBT_CHAIN_MAXNAMELEN)
+-		xtables_error(PARAMETER_PROBLEM,
+-			      "Invalid target '%s' (%d chars max)",
+-			      targetname, EBT_CHAIN_MAXNAMELEN);
+-
+-	for (ptr = targetname; *ptr; ptr++)
+-		if (isspace(*ptr))
+-			xtables_error(PARAMETER_PROBLEM,
+-				      "Invalid target name `%s'", targetname);
+-	return targetname;
+-}
+-
+ static int
+ append_entry(struct nft_handle *h,
+ 	     const char *chain,
+@@ -376,29 +355,6 @@ static struct option *merge_options(struct option *oldopts,
+ 	return merge;
+ }
+ 
+-/*
+- * More glue code.
+- */
+-struct xtables_target *ebt_command_jump(const char *jumpto)
+-{
+-	struct xtables_target *target;
+-	unsigned int verdict;
+-
+-	/* Standard target? */
+-	if (!ebt_fill_target(jumpto, &verdict))
+-		jumpto = "standard";
+-
+-	/* For ebtables, all targets are preloaded. Hence it is either in
+-	 * xtables_targets or a custom chain to jump to, in which case
+-	 * returning NULL is fine. */
+-	for (target = xtables_targets; target; target = target->next) {
+-		if (!strcmp(target->name, jumpto))
+-			break;
+-	}
+-
+-	return target;
+-}
+-
+ static void print_help(const struct xtables_target *t,
+ 		       const struct xtables_rule_match *m, const char *table)
+ {
+@@ -1066,8 +1022,7 @@ print_zero:
+ 			} else if (c == 'j') {
+ 				ebt_check_option2(&flags, OPT_JUMP);
+ 				if (strcmp(optarg, "CONTINUE") != 0) {
+-					cs.jumpto = parse_target(optarg);
+-					cs.target = ebt_command_jump(cs.jumpto);
++					command_jump(&cs);
+ 				}
+ 				break;
+ 			} else if (c == 's') {
+diff --git a/libxtables/xtables.c b/libxtables/xtables.c
+index ea9bb102c8eb4..895f6988eaf57 100644
+--- a/libxtables/xtables.c
++++ b/libxtables/xtables.c
+@@ -756,8 +756,24 @@ xtables_find_target(const char *name, enum xtables_tryload tryload)
+ 	}
+ 
+ 	for (ptr = xtables_targets; ptr; ptr = ptr->next) {
+-		if (extension_cmp(name, ptr->name, ptr->family))
++		if (extension_cmp(name, ptr->name, ptr->family)) {
++			struct xtables_target *clone;
++
++			/* First target of this type: */
++			if (ptr->t == NULL)
++				break;
++
++			/* Second and subsequent clones */
++			clone = xtables_malloc(sizeof(struct xtables_target));
++			memcpy(clone, ptr, sizeof(struct xtables_target));
++			clone->udata = NULL;
++			clone->tflags = 0;
++			/* This is a clone: */
++			clone->next = clone;
++
++			ptr = clone;
+ 			break;
++		}
+ 	}
+ 
+ #ifndef NO_SHARED_LIBS
+-- 
+2.20.1
+
diff --git a/SOURCES/0046-ebtables-Fix-rule-listing-with-counters.patch b/SOURCES/0046-ebtables-Fix-rule-listing-with-counters.patch
new file mode 100644
index 0000000..fd40165
--- /dev/null
+++ b/SOURCES/0046-ebtables-Fix-rule-listing-with-counters.patch
@@ -0,0 +1,75 @@
+From 9092e808178de68c21ef35faeff153ddde1cffd1 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Mon, 21 Jan 2019 17:43:34 +0100
+Subject: [PATCH] ebtables: Fix rule listing with counters
+
+This is a partial revert of commit 583b27eabcad6 ("ebtables-save: add -c
+option, using xtables-style counters") which broke ruleset listing with
+'--Lc' flag turned on:
+
+| # ebtables-nft -L --Lc
+| Bridge table: filter
+|
+| Bridge chain: INPUT, entries: 0, policy: ACCEPT
+|
+| Bridge chain: FORWARD, entries: 2, policy: ACCEPT
+| -j foo
+|  , pcnt = 0 -- bcnt = 0-j ACCEPT
+|  , pcnt = 0 -- bcnt = 0
+| Bridge chain: OUTPUT, entries: 0, policy: ACCEPT
+|
+| Bridge chain: foo, entries: 1, policy: RETURN
+| -j ACCEPT
+|  , pcnt = 0 -- bcnt = 0%
+
+(That percentage sign means no newline after last line of output and
+doesn't belong to ebtables-nft's output.)
+
+Problem was that nft_bridge_print_rule() printed the counters after
+nft_bridge_save_rule() had already printed the newline character.
+
+Note also that there is no need to remove FMT_EBT_SAVE bit from 'format'
+variable: It is set only by ebtables-nft-save which doesn't call
+nft_bridge_print_rule().
+
+Fixes: 583b27eabcad6 ("ebtables-save: add -c option, using xtables-style counters")
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 32ebc39f71e8107d6069a9f6fba8338a2823889d)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-bridge.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
+index 140fcf0a31b84..43b3e3e9649b8 100644
+--- a/iptables/nft-bridge.c
++++ b/iptables/nft-bridge.c
+@@ -479,6 +479,11 @@ static void nft_bridge_save_rule(const void *data, unsigned int format)
+ 		       (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_NONEWLINE))
+ 		fputc('\n', stdout);
+ }
+@@ -492,11 +497,7 @@ static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num,
+ 		printf("%d ", num);
+ 
+ 	nft_rule_to_ebtables_command_state(r, &cs);
+-	nft_bridge_save_rule(&cs, format & ~FMT_EBT_SAVE);
+-	if (!(format & FMT_NOCOUNTS))
+-		printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"",
+-		       (uint64_t)cs.counters.pcnt,
+-		       (uint64_t)cs.counters.bcnt);
++	nft_bridge_save_rule(&cs, format);
+ 	ebt_cs_clean(&cs);
+ }
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0047-Revert-ebtables-use-extrapositioned-negation-consist.patch b/SOURCES/0047-Revert-ebtables-use-extrapositioned-negation-consist.patch
new file mode 100644
index 0000000..f8d3c0e
--- /dev/null
+++ b/SOURCES/0047-Revert-ebtables-use-extrapositioned-negation-consist.patch
@@ -0,0 +1,384 @@
+From 71eb9a84f50211f78cf2b7b5ef34f99944a76c02 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 5 Feb 2019 18:18:02 +0100
+Subject: [PATCH] Revert "ebtables: use extrapositioned negation consistently"
+
+This reverts commit 5f508b76a0cebaf91965ffa678089222e2d47964.
+
+While attempts at unifying syntax between arp-, eb- and iptables-nft
+increase the opportunity for more code-sharing, they are problematic
+when it comes to compatibility. Accepting the old syntax on input helps,
+but due to the fact that neither arptables nor ebtables support --check
+command we must expect for users to test existence of a rule by
+comparing input with output. If that happens in a script, deviating from
+the old syntax in output has a high chance of breaking it.
+
+Therefore revert Florian's patch changing inversion character position
+in output and review the old code for consistency - the only thing
+changed on top of the actual revert is ebtables' own copy of
+print_iface() to make it adhere to the intrapositioned negation scheme
+used throughout ebtables.
+
+Added extension tests by the reverted commit have been kept.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 35b22e82fa62e10950d8e0fa53a755d4abadf346)
+
+Conflicts:
+- Drop changes to extensions/*.t since these files don't exist in
+  release tarballs.
+
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ extensions/libebt_802_3.c   |  4 ++--
+ extensions/libebt_arp.c     | 14 +++++++-------
+ extensions/libebt_ip.c      | 16 ++++++++--------
+ extensions/libebt_ip6.c     | 14 +++++++-------
+ extensions/libebt_mark_m.c  |  2 +-
+ extensions/libebt_pkttype.c |  5 +----
+ extensions/libebt_stp.c     |  5 ++---
+ extensions/libebt_vlan.c    | 13 ++++---------
+ iptables/nft-bridge.c       |  6 +++---
+ 9 files changed, 35 insertions(+), 44 deletions(-)
+
+diff --git a/extensions/libebt_802_3.c b/extensions/libebt_802_3.c
+index 9e91d05262591..f05d02ead5a4a 100644
+--- a/extensions/libebt_802_3.c
++++ b/extensions/libebt_802_3.c
+@@ -98,15 +98,15 @@ static void br802_3_print(const void *ip, const struct xt_entry_match *match,
+ 	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+ 
+ 	if (info->bitmask & EBT_802_3_SAP) {
++		printf("--802_3-sap ");
+ 		if (info->invflags & EBT_802_3_SAP)
+ 			printf("! ");
+-		printf("--802_3-sap ");
+ 		printf("0x%.2x ", info->sap);
+ 	}
+ 	if (info->bitmask & EBT_802_3_TYPE) {
++		printf("--802_3-type ");
+ 		if (info->invflags & EBT_802_3_TYPE)
+ 			printf("! ");
+-		printf("--802_3-type ");
+ 		printf("0x%.4x ", ntohs(info->type));
+ 	}
+ }
+diff --git a/extensions/libebt_arp.c b/extensions/libebt_arp.c
+index c1b0ab1db0cf1..a062b7e7e5864 100644
+--- a/extensions/libebt_arp.c
++++ b/extensions/libebt_arp.c
+@@ -338,51 +338,51 @@ static void brarp_print(const void *ip, const struct xt_entry_match *match, int
+ 
+ 	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+ 		int opcode = ntohs(arpinfo->opcode);
++		printf("--arp-op ");
+ 		if (arpinfo->invflags & EBT_ARP_OPCODE)
+ 			printf("! ");
+-		printf("--arp-op ");
+ 		if (opcode > 0 && opcode <= ARRAY_SIZE(opcodes))
+ 			printf("%s ", opcodes[opcode - 1]);
+ 		else
+ 			printf("%d ", opcode);
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_HTYPE) {
++		printf("--arp-htype ");
+ 		if (arpinfo->invflags & EBT_ARP_HTYPE)
+ 			printf("! ");
+-		printf("--arp-htype ");
+ 		printf("%d ", ntohs(arpinfo->htype));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_PTYPE) {
++		printf("--arp-ptype ");
+ 		if (arpinfo->invflags & EBT_ARP_PTYPE)
+ 			printf("! ");
+-		printf("--arp-ptype ");
+ 		printf("0x%x ", ntohs(arpinfo->ptype));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
++		printf("--arp-ip-src ");
+ 		if (arpinfo->invflags & EBT_ARP_SRC_IP)
+ 			printf("! ");
+-		printf("--arp-ip-src ");
+ 		printf("%s%s ", xtables_ipaddr_to_numeric((const struct in_addr*) &arpinfo->saddr),
+ 		       xtables_ipmask_to_numeric((const struct in_addr*)&arpinfo->smsk));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
++		printf("--arp-ip-dst ");
+ 		if (arpinfo->invflags & EBT_ARP_DST_IP)
+ 			printf("! ");
+-		printf("--arp-ip-dst ");
+ 		printf("%s%s ", xtables_ipaddr_to_numeric((const struct in_addr*) &arpinfo->daddr),
+ 		       xtables_ipmask_to_numeric((const struct in_addr*)&arpinfo->dmsk));
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_SRC_MAC) {
++		printf("--arp-mac-src ");
+ 		if (arpinfo->invflags & EBT_ARP_SRC_MAC)
+ 			printf("! ");
+-		printf("--arp-mac-src ");
+ 		xtables_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk);
+ 		printf(" ");
+ 	}
+ 	if (arpinfo->bitmask & EBT_ARP_DST_MAC) {
++		printf("--arp-mac-dst ");
+ 		if (arpinfo->invflags & EBT_ARP_DST_MAC)
+ 			printf("! ");
+-		printf("--arp-mac-dst ");
+ 		xtables_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk);
+ 		printf(" ");
+ 	}
+diff --git a/extensions/libebt_ip.c b/extensions/libebt_ip.c
+index d48704fe1c802..acb9bfcdbbd9f 100644
+--- a/extensions/libebt_ip.c
++++ b/extensions/libebt_ip.c
+@@ -472,35 +472,35 @@ static void brip_print(const void *ip, const struct xt_entry_match *match,
+ 	struct in_addr *addrp, *maskp;
+ 
+ 	if (info->bitmask & EBT_IP_SOURCE) {
++		printf("--ip-src ");
+ 		if (info->invflags & EBT_IP_SOURCE)
+ 			printf("! ");
+-		printf("--ip-src ");
+ 		addrp = (struct in_addr *)&info->saddr;
+ 		maskp = (struct in_addr *)&info->smsk;
+ 		printf("%s%s ", xtables_ipaddr_to_numeric(addrp),
+ 		       xtables_ipmask_to_numeric(maskp));
+ 	}
+ 	if (info->bitmask & EBT_IP_DEST) {
++		printf("--ip-dst ");
+ 		if (info->invflags & EBT_IP_DEST)
+ 			printf("! ");
+-		printf("--ip-dst ");
+ 		addrp = (struct in_addr *)&info->daddr;
+ 		maskp = (struct in_addr *)&info->dmsk;
+ 		printf("%s%s ", xtables_ipaddr_to_numeric(addrp),
+ 		       xtables_ipmask_to_numeric(maskp));
+ 	}
+ 	if (info->bitmask & EBT_IP_TOS) {
++		printf("--ip-tos ");
+ 		if (info->invflags & EBT_IP_TOS)
+ 			printf("! ");
+-		printf("--ip-tos ");
+ 		printf("0x%02X ", info->tos);
+ 	}
+ 	if (info->bitmask & EBT_IP_PROTO) {
+ 		struct protoent *pe;
+ 
++		printf("--ip-proto ");
+ 		if (info->invflags & EBT_IP_PROTO)
+ 			printf("! ");
+-		printf("--ip-proto ");
+ 		pe = getprotobynumber(info->protocol);
+ 		if (pe == NULL) {
+ 			printf("%d ", info->protocol);
+@@ -509,28 +509,28 @@ static void brip_print(const void *ip, const struct xt_entry_match *match,
+ 		}
+ 	}
+ 	if (info->bitmask & EBT_IP_SPORT) {
++		printf("--ip-sport ");
+ 		if (info->invflags & EBT_IP_SPORT)
+ 			printf("! ");
+-		printf("--ip-sport ");
+ 		print_port_range(info->sport);
+ 	}
+ 	if (info->bitmask & EBT_IP_DPORT) {
++		printf("--ip-dport ");
+ 		if (info->invflags & EBT_IP_DPORT)
+ 			printf("! ");
+-		printf("--ip-dport ");
+ 		print_port_range(info->dport);
+ 	}
+ 	if (info->bitmask & EBT_IP_ICMP) {
++		printf("--ip-icmp-type ");
+ 		if (info->invflags & EBT_IP_ICMP)
+ 			printf("! ");
+-		printf("--ip-icmp-type ");
+ 		ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
+ 				    info->icmp_type, info->icmp_code);
+ 	}
+ 	if (info->bitmask & EBT_IP_IGMP) {
++		printf("--ip-igmp-type ");
+ 		if (info->invflags & EBT_IP_IGMP)
+ 			printf("! ");
+-		printf("--ip-igmp-type ");
+ 		ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
+ 				    info->igmp_type, NULL);
+ 	}
+diff --git a/extensions/libebt_ip6.c b/extensions/libebt_ip6.c
+index b727764903ffa..b8a5a5d8c3a92 100644
+--- a/extensions/libebt_ip6.c
++++ b/extensions/libebt_ip6.c
+@@ -399,31 +399,31 @@ static void brip6_print(const void *ip, const struct xt_entry_match *match,
+ 	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+ 
+ 	if (ipinfo->bitmask & EBT_IP6_SOURCE) {
++		printf("--ip6-src ");
+ 		if (ipinfo->invflags & EBT_IP6_SOURCE)
+ 			printf("! ");
+-		printf("--ip6-src ");
+ 		printf("%s", xtables_ip6addr_to_numeric(&ipinfo->saddr));
+ 		printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->smsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_DEST) {
++		printf("--ip6-dst ");
+ 		if (ipinfo->invflags & EBT_IP6_DEST)
+ 			printf("! ");
+-		printf("--ip6-dst ");
+ 		printf("%s", xtables_ip6addr_to_numeric(&ipinfo->daddr));
+ 		printf("%s ", xtables_ip6mask_to_numeric(&ipinfo->dmsk));
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_TCLASS) {
++		printf("--ip6-tclass ");
+ 		if (ipinfo->invflags & EBT_IP6_TCLASS)
+ 			printf("! ");
+-		printf("--ip6-tclass ");
+ 		printf("0x%02X ", ipinfo->tclass);
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_PROTO) {
+ 		struct protoent *pe;
+ 
++		printf("--ip6-proto ");
+ 		if (ipinfo->invflags & EBT_IP6_PROTO)
+ 			printf("! ");
+-		printf("--ip6-proto ");
+ 		pe = getprotobynumber(ipinfo->protocol);
+ 		if (pe == NULL) {
+ 			printf("%d ", ipinfo->protocol);
+@@ -432,21 +432,21 @@ static void brip6_print(const void *ip, const struct xt_entry_match *match,
+ 		}
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_SPORT) {
++		printf("--ip6-sport ");
+ 		if (ipinfo->invflags & EBT_IP6_SPORT)
+ 			printf("! ");
+-		printf("--ip6-sport ");
+ 		print_port_range(ipinfo->sport);
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_DPORT) {
++		printf("--ip6-dport ");
+ 		if (ipinfo->invflags & EBT_IP6_DPORT)
+ 			printf("! ");
+-		printf("--ip6-dport ");
+ 		print_port_range(ipinfo->dport);
+ 	}
+ 	if (ipinfo->bitmask & EBT_IP6_ICMP6) {
++		printf("--ip6-icmp-type ");
+ 		if (ipinfo->invflags & EBT_IP6_ICMP6)
+ 			printf("! ");
+-		printf("--ip6-icmp-type ");
+ 		print_icmp_type(ipinfo->icmpv6_type, ipinfo->icmpv6_code);
+ 	}
+ }
+diff --git a/extensions/libebt_mark_m.c b/extensions/libebt_mark_m.c
+index 64ad926f19959..2462d0af7d0bc 100644
+--- a/extensions/libebt_mark_m.c
++++ b/extensions/libebt_mark_m.c
+@@ -86,9 +86,9 @@ static void brmark_m_print(const void *ip, const struct xt_entry_match *match,
+ {
+ 	struct ebt_mark_m_info *info = (struct ebt_mark_m_info *)match->data;
+ 
++	printf("--mark ");
+ 	if (info->invert)
+ 		printf("! ");
+-	printf("--mark ");
+ 	if (info->bitmask == EBT_MARK_OR)
+ 		printf("/0x%lx ", info->mask);
+ 	else if (info->mask != 0xffffffff)
+diff --git a/extensions/libebt_pkttype.c b/extensions/libebt_pkttype.c
+index 265674d19bde6..4e2d19de7983b 100644
+--- a/extensions/libebt_pkttype.c
++++ b/extensions/libebt_pkttype.c
+@@ -75,10 +75,7 @@ static void brpkttype_print(const void *ip, const struct xt_entry_match *match,
+ {
+ 	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
+ 
+-	if (pt->invert)
+-		printf("! ");
+-
+-	printf("--pkttype-type ");
++	printf("--pkttype-type %s", pt->invert ? "! " : "");
+ 
+ 	if (pt->pkt_type < ARRAY_SIZE(classes))
+ 		printf("%s ", classes[pt->pkt_type]);
+diff --git a/extensions/libebt_stp.c b/extensions/libebt_stp.c
+index 33e4c8d9c615d..06cf93b8d8449 100644
+--- a/extensions/libebt_stp.c
++++ b/extensions/libebt_stp.c
+@@ -307,9 +307,8 @@ static void brstp_print(const void *ip, const struct xt_entry_match *match,
+ 	for (i = 0; i < STP_NUMOPS; i++) {
+ 		if (!(stpinfo->bitmask & (1 << i)))
+ 			continue;
+-		if (stpinfo->invflags & (1 << i))
+-			printf("! ");
+-		printf("--%s ", brstp_opts[i].name);
++		printf("--%s %s", brstp_opts[i].name,
++		       (stpinfo->invflags & (1 << i)) ? "! " : "");
+ 		if (EBT_STP_TYPE == (1 << i)) {
+ 			if (stpinfo->type == BPDU_TYPE_CONFIG)
+ 				printf("%s", BPDU_TYPE_CONFIG_STRING);
+diff --git a/extensions/libebt_vlan.c b/extensions/libebt_vlan.c
+index 4a2eb7126895e..a2a9dcce531ce 100644
+--- a/extensions/libebt_vlan.c
++++ b/extensions/libebt_vlan.c
+@@ -108,19 +108,14 @@ static void brvlan_print(const void *ip, const struct xt_entry_match *match,
+ 	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data;
+ 
+ 	if (vlaninfo->bitmask & EBT_VLAN_ID) {
+-		if (vlaninfo->invflags & EBT_VLAN_ID)
+-			printf("! ");
+-		printf("--vlan-id %d ", vlaninfo->id);
++		printf("--vlan-id %s%d ", (vlaninfo->invflags & EBT_VLAN_ID) ? "! " : "", vlaninfo->id);
+ 	}
+ 	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+-		if (vlaninfo->invflags & EBT_VLAN_PRIO)
+-			printf("! ");
+-		printf("--vlan-prio %d ", vlaninfo->prio);
++		printf("--vlan-prio %s%d ", (vlaninfo->invflags & EBT_VLAN_PRIO) ? "! " : "", vlaninfo->prio);
+ 	}
+ 	if (vlaninfo->bitmask & EBT_VLAN_ENCAP) {
+-		if (vlaninfo->invflags & EBT_VLAN_ENCAP)
+-			printf("! ");
+-		printf("--vlan-encap %4.4X ", ntohs(vlaninfo->encap));
++		printf("--vlan-encap %s", (vlaninfo->invflags & EBT_VLAN_ENCAP) ? "! " : "");
++		printf("%4.4X ", ntohs(vlaninfo->encap));
+ 	}
+ }
+ 
+diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
+index 43b3e3e9649b8..2b79ca951cd92 100644
+--- a/iptables/nft-bridge.c
++++ b/iptables/nft-bridge.c
+@@ -344,7 +344,7 @@ static void nft_rule_to_ebtables_command_state(const struct nftnl_rule *r,
+ static void print_iface(const char *option, const char *name, bool invert)
+ {
+ 	if (*name)
+-		printf("%s%s %s ", invert ? "! " : "", option, name);
++		printf("%s%s %s ", option, invert ? " !" : "", name);
+ }
+ 
+ static void nft_bridge_print_table_header(const char *tablename)
+@@ -389,9 +389,9 @@ static void print_mac(char option, const unsigned char *mac,
+ 		      const unsigned char *mask,
+ 		      bool invert)
+ {
++	printf("-%c ", option);
+ 	if (invert)
+ 		printf("! ");
+-	printf("-%c ", option);
+ 	ebt_print_mac_and_mask(mac, mask);
+ 	printf(" ");
+ }
+@@ -406,9 +406,9 @@ static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask)
+ 	if (bitmask & EBT_NOPROTO)
+ 		return;
+ 
++	printf("-p ");
+ 	if (invert)
+ 		printf("! ");
+-	printf("-p ");
+ 
+ 	if (bitmask & EBT_802_3) {
+ 		printf("length ");
+-- 
+2.20.1
+
diff --git a/SOURCES/0048-arptables-Support-set-counters-option.patch b/SOURCES/0048-arptables-Support-set-counters-option.patch
new file mode 100644
index 0000000..1799940
--- /dev/null
+++ b/SOURCES/0048-arptables-Support-set-counters-option.patch
@@ -0,0 +1,42 @@
+From a071294e42f6b670b7e00819d2024ae7dd959993 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 22 Nov 2018 20:50:13 +0100
+Subject: [PATCH] arptables: Support --set-counters option
+
+Relevant code for this was already present (short option '-c'), just the
+long option definition was missing.
+
+While being at it, add '-c' to help text.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit b0466ae6fbc0a93b69591171c54aa79063e23f3d)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/xtables-arp.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
+index 18cdced627c55..85bcc841b21f5 100644
+--- a/iptables/xtables-arp.c
++++ b/iptables/xtables-arp.c
+@@ -144,6 +144,7 @@ static struct option original_opts[] = {
+ 	{ "help", 2, 0, 'h' },
+ 	{ "line-numbers", 0, 0, '0' },
+ 	{ "modprobe", 1, 0, 'M' },
++	{ "set-counters", 1, 0, 'c' },
+ 	{ 0 }
+ };
+ 
+@@ -481,7 +482,7 @@ exit_printhelp(void)
+ "  --line-numbers		print line numbers when listing\n"
+ "  --exact	-x		expand numbers (display exact values)\n"
+ "  --modprobe=<command>		try to insert modules using this command\n"
+-"  --set-counters PKTS BYTES	set the counter during insert/append\n"
++"  --set-counters -c PKTS BYTES	set the counter during insert/append\n"
+ "[!] --version	-V		print package version.\n");
+ 	printf(" opcode strings: \n");
+         for (i = 0; i < NUMOPCODES; i++)
+-- 
+2.20.1
+
diff --git a/SOURCES/0049-xshared-Explicitly-pass-target-to-command_jump.patch b/SOURCES/0049-xshared-Explicitly-pass-target-to-command_jump.patch
new file mode 100644
index 0000000..2efc3ca
--- /dev/null
+++ b/SOURCES/0049-xshared-Explicitly-pass-target-to-command_jump.patch
@@ -0,0 +1,136 @@
+From 1d8efdc3ce231f08b57ee5eb5d784ccb5867c69b Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Tue, 5 Feb 2019 17:01:42 +0100
+Subject: [PATCH] xshared: Explicitly pass target to command_jump()
+
+The use of global 'optarg' variable inside that function is a mess, but
+most importantly it limits its applicability to input parsers. Fix this
+by having it take the option argument as a parameter.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit 932d5c3fb94acc499c8a6264e354ab1e33316b72)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/ip6tables.c            | 2 +-
+ iptables/iptables.c             | 2 +-
+ iptables/xshared.c              | 4 ++--
+ iptables/xshared.h              | 2 +-
+ iptables/xtables-arp.c          | 2 +-
+ iptables/xtables-eb-translate.c | 2 +-
+ iptables/xtables-eb.c           | 2 +-
+ iptables/xtables.c              | 2 +-
+ 8 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/iptables/ip6tables.c b/iptables/ip6tables.c
+index fe089de4c85d7..050afa9a36458 100644
+--- a/iptables/ip6tables.c
++++ b/iptables/ip6tables.c
+@@ -1441,7 +1441,7 @@ int do_command6(int argc, char *argv[], char **table,
+ 		case 'j':
+ 			set_option(&cs.options, OPT_JUMP, &cs.fw6.ipv6.invflags,
+ 					cs.invert);
+-			command_jump(&cs);
++			command_jump(&cs, optarg);
+ 			break;
+ 
+ 
+diff --git a/iptables/iptables.c b/iptables/iptables.c
+index f8041f56ce70d..38c4bfe8ecf5c 100644
+--- a/iptables/iptables.c
++++ b/iptables/iptables.c
+@@ -1421,7 +1421,7 @@ int do_command4(int argc, char *argv[], char **table,
+ 		case 'j':
+ 			set_option(&cs.options, OPT_JUMP, &cs.fw.ip.invflags,
+ 				   cs.invert);
+-			command_jump(&cs);
++			command_jump(&cs, optarg);
+ 			break;
+ 
+ 
+diff --git a/iptables/xshared.c b/iptables/xshared.c
+index b16f5fa68e569..fb186fb1ac657 100644
+--- a/iptables/xshared.c
++++ b/iptables/xshared.c
+@@ -653,12 +653,12 @@ const char *xt_parse_target(const char *targetname)
+ 	return targetname;
+ }
+ 
+-void command_jump(struct iptables_command_state *cs)
++void command_jump(struct iptables_command_state *cs, const char *jumpto)
+ {
+ 	struct option *opts = xt_params->opts;
+ 	size_t size;
+ 
+-	cs->jumpto = xt_parse_target(optarg);
++	cs->jumpto = xt_parse_target(jumpto);
+ 	/* TRY_LOAD (may be chain name) */
+ 	cs->target = xtables_find_target(cs->jumpto, XTF_TRY_LOAD);
+ 
+diff --git a/iptables/xshared.h b/iptables/xshared.h
+index db499f29236ed..fd1f96bad1b98 100644
+--- a/iptables/xshared.h
++++ b/iptables/xshared.h
+@@ -176,6 +176,6 @@ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
+ 
+ void command_match(struct iptables_command_state *cs);
+ const char *xt_parse_target(const char *targetname);
+-void command_jump(struct iptables_command_state *cs);
++void command_jump(struct iptables_command_state *cs, const char *jumpto);
+ 
+ #endif /* IPTABLES_XSHARED_H */
+diff --git a/iptables/xtables-arp.c b/iptables/xtables-arp.c
+index 85bcc841b21f5..4a873b15c6833 100644
+--- a/iptables/xtables-arp.c
++++ b/iptables/xtables-arp.c
+@@ -1161,7 +1161,7 @@ int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table,
+ 		case 'j':
+ 			set_option(&options, OPT_JUMP, &cs.arp.arp.invflags,
+ 				   invert);
+-			command_jump(&cs);
++			command_jump(&cs, optarg);
+ 			break;
+ 
+ 		case 'i':
+diff --git a/iptables/xtables-eb-translate.c b/iptables/xtables-eb-translate.c
+index 0fe14d2d0db32..96b2730fa97ed 100644
+--- a/iptables/xtables-eb-translate.c
++++ b/iptables/xtables-eb-translate.c
+@@ -390,7 +390,7 @@ print_zero:
+ 				break;
+ 			} else if (c == 'j') {
+ 				ebt_check_option2(&flags, OPT_JUMP);
+-				command_jump(&cs);
++				command_jump(&cs, optarg);
+ 				break;
+ 			} else if (c == 's') {
+ 				ebt_check_option2(&flags, OPT_SOURCE);
+diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
+index 55cb0fe204748..21344843a365a 100644
+--- a/iptables/xtables-eb.c
++++ b/iptables/xtables-eb.c
+@@ -1022,7 +1022,7 @@ print_zero:
+ 			} else if (c == 'j') {
+ 				ebt_check_option2(&flags, OPT_JUMP);
+ 				if (strcmp(optarg, "CONTINUE") != 0) {
+-					command_jump(&cs);
++					command_jump(&cs, optarg);
+ 				}
+ 				break;
+ 			} else if (c == 's') {
+diff --git a/iptables/xtables.c b/iptables/xtables.c
+index eaa9fedeb03bb..1d777554076d7 100644
+--- a/iptables/xtables.c
++++ b/iptables/xtables.c
+@@ -820,7 +820,7 @@ void do_parse(struct nft_handle *h, int argc, char *argv[],
+ 		case 'j':
+ 			set_option(&cs->options, OPT_JUMP, &cs->fw.ip.invflags,
+ 				   cs->invert);
+-			command_jump(cs);
++			command_jump(cs, optarg);
+ 			break;
+ 
+ 
+-- 
+2.20.1
+
diff --git a/SOURCES/0050-nft-Don-t-assume-NFTNL_RULE_USERDATA-holds-a-comment.patch b/SOURCES/0050-nft-Don-t-assume-NFTNL_RULE_USERDATA-holds-a-comment.patch
new file mode 100644
index 0000000..fbdd4b9
--- /dev/null
+++ b/SOURCES/0050-nft-Don-t-assume-NFTNL_RULE_USERDATA-holds-a-comment.patch
@@ -0,0 +1,72 @@
+From da5f63cd381cb109b73d2d31f5da35242bf5ae99 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 7 Feb 2019 22:08:53 +0100
+Subject: [PATCH] nft: Don't assume NFTNL_RULE_USERDATA holds a comment
+
+If this rule attribute is present but does not contain a comment,
+get_comment() returns NULL which is then fed into strncpy() causing a
+crash.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit d1df0a36b0486c780211cfa574301132bf55f194)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-shared.c | 39 ++++++++++++++++++++++-----------------
+ 1 file changed, 22 insertions(+), 17 deletions(-)
+
+diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
+index ce40787f92f7d..fc484b49e2318 100644
+--- a/iptables/nft-shared.c
++++ b/iptables/nft-shared.c
+@@ -639,25 +639,30 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
+ 	if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
+ 		const void *data;
+ 		uint32_t len, size;
+-		struct xtables_match *match;
+-		struct xt_entry_match *m;
++		const char *comment;
+ 
+ 		data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
+-		match = xtables_find_match("comment", XTF_TRY_LOAD,
+-					   &cs->matches);
+-		if (match == NULL)
+-			return;
+-
+-		size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+-		m = xtables_calloc(1, size);
+-
+-		strncpy((char *)m->data, get_comment(data, len),
+-			match->size - 1);
+-		m->u.match_size = size;
+-		m->u.user.revision = 0;
+-		strcpy(m->u.user.name, match->name);
+-
+-		match->m = m;
++		comment = get_comment(data, len);
++		if (comment) {
++			struct xtables_match *match;
++			struct xt_entry_match *m;
++
++			match = xtables_find_match("comment", XTF_TRY_LOAD,
++						   &cs->matches);
++			if (match == NULL)
++				return;
++
++			size = XT_ALIGN(sizeof(struct xt_entry_match))
++				+ match->size;
++			m = xtables_calloc(1, size);
++
++			strncpy((char *)m->data, comment, match->size - 1);
++			m->u.match_size = size;
++			m->u.user.revision = 0;
++			strcpy(m->u.user.name, match->name);
++
++			match->m = m;
++		}
+ 	}
+ 
+ 	if (cs->target != NULL) {
+-- 
+2.20.1
+
diff --git a/SOURCES/0051-nft-Introduce-UDATA_TYPE_EBTABLES_POLICY.patch b/SOURCES/0051-nft-Introduce-UDATA_TYPE_EBTABLES_POLICY.patch
new file mode 100644
index 0000000..0f5cb6e
--- /dev/null
+++ b/SOURCES/0051-nft-Introduce-UDATA_TYPE_EBTABLES_POLICY.patch
@@ -0,0 +1,41 @@
+From 0af602be4dbda5bf510815d3e448d91098da594e Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 7 Feb 2019 22:08:54 +0100
+Subject: [PATCH] nft: Introduce UDATA_TYPE_EBTABLES_POLICY
+
+This will be used later to identify ebtables user-defined chain policy
+rules.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit b06cc4e0f67f4beba7560fc329d20f108c87b5fb)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 2fa973cf03975..6129afdbad281 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -1115,6 +1115,7 @@ int add_counters(struct nftnl_rule *r, uint64_t packets, uint64_t bytes)
+ 
+ enum udata_type {
+ 	UDATA_TYPE_COMMENT,
++	UDATA_TYPE_EBTABLES_POLICY,
+ 	__UDATA_TYPE_MAX,
+ };
+ #define UDATA_TYPE_MAX (__UDATA_TYPE_MAX - 1)
+@@ -1131,6 +1132,8 @@ static int parse_udata_cb(const struct nftnl_udata *attr, void *data)
+ 		if (value[len - 1] != '\0')
+ 			return -1;
+ 		break;
++	case UDATA_TYPE_EBTABLES_POLICY:
++		break;
+ 	default:
+ 		return 0;
+ 	}
+-- 
+2.20.1
+
diff --git a/SOURCES/0052-ebtables-nft-Support-user-defined-chain-policies.patch b/SOURCES/0052-ebtables-nft-Support-user-defined-chain-policies.patch
new file mode 100644
index 0000000..1bfb1e8
--- /dev/null
+++ b/SOURCES/0052-ebtables-nft-Support-user-defined-chain-policies.patch
@@ -0,0 +1,506 @@
+From 008ecae50b63744d9f51fcbe794ac896163c4639 Mon Sep 17 00:00:00 2001
+From: Phil Sutter <phil@nwl.cc>
+Date: Thu, 7 Feb 2019 22:08:55 +0100
+Subject: [PATCH] ebtables-nft: Support user-defined chain policies
+
+Legacy ebtables supports policies for user-defined chains - and what's
+worse, they default to ACCEPT unlike anywhere else. So lack of support
+for this braindead feature in ebtables-nft is actually a change of
+behaviour which very likely affects all ebtables users out there.
+
+The solution implemented here uses an implicit (and transparent) last
+rule in all user-defined ebtables-nft chains with policy other than
+RETURN. This rule is identified by an nft comment
+"XTABLES_EB_INTERNAL_POLICY_RULE" (since commit ccf154d7420c0 ("xtables:
+Don't use native nftables comments") nft comments are not used
+otherwise).
+
+To minimize interference with existing code, this policy rule is removed
+from chains during cache population and the policy is saved in
+NFTNL_CHAIN_POLICY attribute. When committing changes to the kernel,
+nft_commit() traverses through the list of chains and (re-)creates
+policy rules if required.
+
+In ebtables-nft-restore, table flushes are problematic. To avoid weird
+kernel error responses, introduce a custom 'table_flush' callback which
+removes any pending policy rule add/remove jobs prior to creating the
+NFT_COMPAT_TABLE_FLUSH one.
+
+I've hidden all this mess behind checks for h->family, so hopefully
+impact on {ip,ip6,arp}tables-nft should be negligible.
+
+Signed-off-by: Phil Sutter <phil@nwl.cc>
+Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+(cherry picked from commit aff1162b3e4b7ef805425a40306044c7d7dddc67)
+Signed-off-by: Phil Sutter <psutter@redhat.com>
+---
+ iptables/nft-bridge.c                         |   2 +-
+ iptables/nft.c                                | 228 +++++++++++++++++-
+ iptables/nft.h                                |   4 +
+ .../ebtables/0002-ebtables-save-restore_0     |   7 +
+ iptables/xtables-eb.c                         |  20 +-
+ iptables/xtables-restore.c                    |  23 +-
+ 6 files changed, 265 insertions(+), 19 deletions(-)
+
+diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
+index 2b79ca951cd92..a51792ef03ae1 100644
+--- a/iptables/nft-bridge.c
++++ b/iptables/nft-bridge.c
+@@ -358,7 +358,7 @@ static void nft_bridge_print_header(unsigned int format, const char *chain,
+ 				    bool basechain, uint32_t refs, uint32_t entries)
+ {
+ 	printf("Bridge chain: %s, entries: %u, policy: %s\n",
+-	       chain, entries, basechain ? pol : "RETURN");
++	       chain, entries, pol ?: "RETURN");
+ }
+ 
+ static void print_matches_and_watchers(const struct iptables_command_state *cs,
+diff --git a/iptables/nft.c b/iptables/nft.c
+index 6129afdbad281..4fdc789d99928 100644
+--- a/iptables/nft.c
++++ b/iptables/nft.c
+@@ -55,6 +55,7 @@
+ #include "nft.h"
+ #include "xshared.h" /* proto_to_name */
+ #include "nft-shared.h"
++#include "nft-bridge.h" /* EBT_NOPROTO */
+ #include "xtables-config-parser.h"
+ 
+ static void *nft_fn;
+@@ -1325,6 +1326,87 @@ retry:
+ 	return ret;
+ }
+ 
++static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
++{
++	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
++	const void *data;
++	uint32_t len;
++
++	if (!nftnl_rule_is_set(r, NFTNL_RULE_USERDATA))
++		return false;
++
++	data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
++	if (nftnl_udata_parse(data, len, parse_udata_cb, tb) < 0)
++		return NULL;
++
++	if (!tb[UDATA_TYPE_EBTABLES_POLICY] ||
++	    nftnl_udata_get_u32(tb[UDATA_TYPE_EBTABLES_POLICY]) != 1)
++		return false;
++
++	return true;
++}
++
++static struct nftnl_rule *nft_chain_last_rule(struct nftnl_chain *c)
++{
++	struct nftnl_rule *r = NULL, *last;
++	struct nftnl_rule_iter *iter;
++
++	iter = nftnl_rule_iter_create(c);
++	if (!iter)
++		return NULL;
++
++	do {
++		last = r;
++		r = nftnl_rule_iter_next(iter);
++	} while (r);
++	nftnl_rule_iter_destroy(iter);
++
++	return last;
++}
++
++static 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;
++	struct nftnl_expr *expr;
++	int verdict;
++
++	if (!last || !nft_rule_is_policy_rule(last))
++		return;
++
++	iter = nftnl_expr_iter_create(last);
++	if (!iter)
++		return;
++
++	expr = nftnl_expr_iter_next(iter);
++	if (!expr ||
++	    strcmp("counter", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)))
++		goto out_iter;
++
++	expr = nftnl_expr_iter_next(iter);
++	if (!expr ||
++	    strcmp("immediate", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)) ||
++	    !nftnl_expr_is_set(expr, NFTNL_EXPR_IMM_VERDICT))
++		goto out_iter;
++
++	verdict = nftnl_expr_get_u32(expr, NFTNL_EXPR_IMM_VERDICT);
++	switch (verdict) {
++	case NF_ACCEPT:
++	case NF_DROP:
++		break;
++	default:
++		goto out_iter;
++	}
++
++	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, verdict);
++	if (batch_rule_add(h, NFT_COMPAT_RULE_DELETE, last) < 0)
++		fprintf(stderr, "Failed to delete old policy rule\n");
++	nftnl_chain_rule_del(last);
++out_iter:
++	nftnl_expr_iter_destroy(iter);
++}
++
+ static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
+ {
+ 	struct nftnl_chain *c = data;
+@@ -1378,6 +1460,10 @@ retry:
+ 	}
+ 
+ 	nftnl_rule_free(rule);
++
++	if (h->family == NFPROTO_BRIDGE)
++		nft_bridge_chain_postprocess(h, c);
++
+ 	return 0;
+ }
+ 
+@@ -1443,6 +1529,15 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
+ 			if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
+ 				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+ 			policy = policy_name[pol];
++		} else if (h->family == NFPROTO_BRIDGE) {
++			if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) {
++				uint32_t pol;
++
++				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
++				policy = policy_name[pol];
++			} else {
++				policy = "RETURN";
++			}
+ 		}
+ 
+ 		if (ops->save_chain)
+@@ -1637,6 +1732,8 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
+ 
+ 	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
+ 	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)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);
+ 
+@@ -2278,7 +2375,6 @@ static void __nft_print_header(struct nft_handle *h,
+ 			       struct nftnl_chain *c, unsigned int format)
+ {
+ 	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
+-	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
+ 	bool basechain = !!nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM);
+ 	uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
+ 	uint32_t entries = nft_rule_count(h, c);
+@@ -2286,8 +2382,12 @@ static void __nft_print_header(struct nft_handle *h,
+ 		.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
+ 		.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
+ 	};
++	const char *pname = NULL;
+ 
+-	ops->print_header(format, chain_name, policy_name[policy],
++	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,
+ 			&ctrs, basechain, refs - entries, entries);
+ }
+ 
+@@ -2671,8 +2771,111 @@ static int nft_action(struct nft_handle *h, int action)
+ 	return ret == 0 ? 1 : 0;
+ }
+ 
++static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
++{
++	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
++	struct iptables_command_state cs = {
++		.eb.bitmask = EBT_NOPROTO,
++	};
++	struct nftnl_udata_buf *udata;
++	struct nft_handle *h = data;
++	struct nftnl_rule *r;
++	const char *pname;
++
++	if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
++		return 0; /* ignore base chains */
++
++	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
++		return 0;
++
++	nftnl_chain_unset(c, NFTNL_CHAIN_POLICY);
++
++	switch (policy) {
++	case NFT_RETURN:
++		return 0; /* return policy is default for nft chains */
++	case NF_ACCEPT:
++		pname = "ACCEPT";
++		break;
++	case NF_DROP:
++		pname = "DROP";
++		break;
++	default:
++		return -1;
++	}
++
++	command_jump(&cs, pname);
++
++	r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
++			 nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
++	if (!r)
++		return -1;
++
++	udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
++	if (!udata)
++		return -1;
++
++	if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1))
++		return -1;
++
++	nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
++			    nftnl_udata_buf_data(udata),
++			    nftnl_udata_buf_len(udata));
++	nftnl_udata_buf_free(udata);
++
++	if (batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r) < 0) {
++		nftnl_rule_free(r);
++		return -1;
++	}
++
++	return 0;
++}
++
++int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
++			      const char *chain, const char *policy)
++{
++	struct nftnl_chain *c = nft_chain_find(h, table, chain);
++	int pval;
++
++	if (!c)
++		return 0;
++
++	if (!strcmp(policy, "DROP"))
++		pval = NF_DROP;
++	else if (!strcmp(policy, "ACCEPT"))
++		pval = NF_ACCEPT;
++	else if (!strcmp(policy, "RETURN"))
++		pval = NFT_RETURN;
++	else
++		return 0;
++
++	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval);
++	return 1;
++}
++
++static void nft_bridge_commit_prepare(struct nft_handle *h)
++{
++	const struct builtin_table *t;
++	struct nftnl_chain_list *list;
++	int i;
++
++	for (i = 0; i < NFT_TABLE_MAX; i++) {
++		t = &h->tables[i];
++
++		if (!t->name)
++			continue;
++
++		list = h->table[t->type].chain_cache;
++		if (!list)
++			continue;
++
++		nftnl_chain_list_foreach(list, ebt_add_policy_rule, 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);
+ }
+ 
+@@ -2681,6 +2884,27 @@ int nft_abort(struct nft_handle *h)
+ 	return nft_action(h, NFT_COMPAT_ABORT);
+ }
+ 
++int nft_abort_policy_rule(struct nft_handle *h, const char *table)
++{
++	struct obj_update *n, *tmp;
++
++	list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
++		if (n->type != NFT_COMPAT_RULE_APPEND &&
++		    n->type != NFT_COMPAT_RULE_DELETE)
++			continue;
++
++		if (strcmp(table,
++			   nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE)))
++			continue;
++
++		if (!nft_rule_is_policy_rule(n->rule))
++			continue;
++
++		batch_obj_del(h, n);
++	}
++	return 0;
++}
++
+ int nft_compatible_revision(const char *name, uint8_t rev, int opt)
+ {
+ 	struct mnl_socket *nl;
+diff --git a/iptables/nft.h b/iptables/nft.h
+index 0726923a63dd4..56dc207608855 100644
+--- a/iptables/nft.h
++++ b/iptables/nft.h
+@@ -137,6 +137,7 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
+  */
+ int nft_commit(struct nft_handle *h);
+ int nft_abort(struct nft_handle *h);
++int nft_abort_policy_rule(struct nft_handle *h, const char *table);
+ 
+ /*
+  * revision compatibility.
+@@ -203,4 +204,7 @@ 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);
+ 
++int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
++			      const char *chain, const char *policy);
++
+ #endif
+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 b23c1ee18c8ae..080ba49a4974d 100755
+--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
++++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
+@@ -50,6 +50,9 @@ $XT_MULTI ebtables -A foo --pkttype-type multicast --limit 100 -j ACCEPT
+ 
+ $XT_MULTI ebtables -A FORWARD -j foo
+ 
++$XT_MULTI ebtables -N bar
++$XT_MULTI ebtables -P bar RETURN
++
+ $XT_MULTI ebtables -t nat -A PREROUTING --redirect-target ACCEPT
+ #$XT_MULTI ebtables -t nat -A PREROUTING --to-src fe:ed:ba:be:00:01
+ 
+@@ -59,6 +62,8 @@ $XT_MULTI ebtables -t nat -P OUTPUT DROP
+ $XT_MULTI ebtables -t nat -A POSTROUTING -j ACCEPT
+ #$XT_MULTI ebtables -t nat -A POSTROUTING --to-dst fe:ed:ba:be:00:01 --dnat-target ACCEPT
+ 
++$XT_MULTI ebtables -t nat -N nat_foo -P DROP
++
+ # compare against stored ebtables dump
+ 
+ DUMP='*filter
+@@ -66,6 +71,7 @@ DUMP='*filter
+ :FORWARD DROP
+ :OUTPUT ACCEPT
+ :foo ACCEPT
++:bar RETURN
+ -A INPUT -p IPv4 -i lo -j ACCEPT
+ -A FORWARD -j foo
+ -A OUTPUT -s Broadcast -j DROP
+@@ -98,6 +104,7 @@ DUMP='*filter
+ :PREROUTING ACCEPT
+ :OUTPUT DROP
+ :POSTROUTING ACCEPT
++:nat_foo DROP
+ -A PREROUTING -j redirect 
+ -A OUTPUT -j ACCEPT
+ -A POSTROUTING -j ACCEPT
+diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
+index 21344843a365a..d0f0026e9c538 100644
+--- a/iptables/xtables-eb.c
++++ b/iptables/xtables-eb.c
+@@ -811,7 +811,6 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
+ 		case 'E': /* Rename chain */
+ 		case 'X': /* Delete chain */
+ 			/* We allow -N chainname -P policy */
+-			/* XXX: Not in ebtables-compat */
+ 			if (command == 'N' && c == 'P') {
+ 				command = c;
+ 				optind--; /* No table specified */
+@@ -1236,17 +1235,16 @@ print_zero:
+ 
+ 	if (command == 'P') {
+ 		if (selected_chain < 0) {
+-			xtables_error(PARAMETER_PROBLEM,
+-				      "Policy %s not allowed for user defined chains",
+-				      policy);
+-		}
+-		if (strcmp(policy, "RETURN") == 0) {
+-			xtables_error(PARAMETER_PROBLEM,
+-				      "Policy RETURN only allowed for user defined chains");
++			ret = ebt_set_user_chain_policy(h, *table, chain, policy);
++		} else {
++			if (strcmp(policy, "RETURN") == 0) {
++				xtables_error(PARAMETER_PROBLEM,
++					      "Policy RETURN only allowed for user defined chains");
++			}
++			ret = nft_chain_set(h, *table, chain, policy, NULL);
++			if (ret < 0)
++				xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+ 		}
+-		ret = nft_chain_set(h, *table, chain, policy, NULL);
+-		if (ret < 0)
+-			xtables_error(PARAMETER_PROBLEM, "Wrong policy");
+ 	} else if (command == 'L') {
+ 		ret = list_rules(h, chain, *table, rule_nr,
+ 				 0,
+diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
+index 4e00ed86be06d..6e6daffc9a1df 100644
+--- a/iptables/xtables-restore.c
++++ b/iptables/xtables-restore.c
+@@ -226,14 +226,20 @@ void xtables_restore_parse(struct nft_handle *h,
+ 							     curtable->name, chain);
+ 			} else if (cb->chain_user_add &&
+ 				   cb->chain_user_add(h, chain,
+-						      curtable->name) < 0) {
+-				if (errno == EEXIST)
+-					continue;
+-
++						      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) {
+@@ -462,11 +468,18 @@ int xtables_ip6_restore_main(int argc, char *argv[])
+ 				    argc, argv);
+ }
+ 
++static int ebt_table_flush(struct nft_handle *h, const char *table)
++{
++	/* drop any pending policy rule add/removal jobs */
++	nft_abort_policy_rule(h, table);
++	return nft_table_flush(h, table);
++}
++
+ struct nft_xt_restore_cb ebt_restore_cb = {
+ 	.chain_list	= get_chain_list,
+ 	.commit		= nft_commit,
+ 	.table_new	= nft_table_new,
+-	.table_flush	= nft_table_flush,
++	.table_flush	= ebt_table_flush,
+ 	.chain_user_flush = nft_chain_user_flush,
+ 	.do_command	= do_commandeb,
+ 	.chain_set	= nft_chain_set,
+-- 
+2.20.1
+
diff --git a/SOURCES/arptables-helper b/SOURCES/arptables-helper
new file mode 100644
index 0000000..4039e7d
--- /dev/null
+++ b/SOURCES/arptables-helper
@@ -0,0 +1,89 @@
+#!/bin/bash
+# config: /etc/sysconfig/arptables
+
+# Source 'em up
+. /etc/init.d/functions
+
+ARPTABLES_CONFIG=/etc/sysconfig/arptables
+
+flush_delete_chains() {
+	echo -n $"Flushing all chains: "
+	if arptables -F; then
+		success
+	else
+		failure
+	fi
+	echo
+
+	echo -n $"Removing user defined chains: "
+	if arptables -X; then
+		success
+	else
+		failure
+	fi
+	echo
+}
+
+start() {
+	if [ ! -x /usr/sbin/arptables ]; then
+		exit 4
+	fi
+
+	# don't do squat if we don't have the config file
+	if [ -f $ARPTABLES_CONFIG ]; then
+		# If we don't clear these first, we might be adding to
+		# pre-existing rules.
+                flush_delete_chains
+
+		arptables -Z
+
+		echo -n $"Applying arptables firewall rules: "
+		/usr/sbin/arptables-restore < $ARPTABLES_CONFIG && \
+			success || \
+			failure
+		echo
+		touch /var/lock/subsys/arptables
+	else
+		failure
+		echo
+		echo $"Configuration file /etc/sysconfig/arptables missing"
+		exit 6
+	fi
+}
+
+stop() {
+        flush_delete_chains
+	echo -n $"Resetting built-in chains to the default ACCEPT policy:"
+	arptables -P INPUT ACCEPT && \
+		arptables -P OUTPUT ACCEPT && \
+		success || \
+		failure
+	echo
+	rm -f /var/lock/subsys/arptables
+}
+
+case "$1" in
+start)
+	start
+	;;
+
+stop)
+	stop
+	;;
+
+restart|reload)
+	# "restart" is really just "start" as this isn't a daemon,
+	# and "start" clears any pre-defined rules anyway.
+	# This is really only here to make those who expect it happy
+	start
+	;;
+
+condrestart|try-restart|force-reload)
+	[ -e /var/lock/subsys/arptables ] && start
+	;;
+
+*)
+	exit 2
+esac
+
+exit 0
diff --git a/SOURCES/arptables.8 b/SOURCES/arptables.8
new file mode 100644
index 0000000..6a5e21d
--- /dev/null
+++ b/SOURCES/arptables.8
@@ -0,0 +1,330 @@
+.TH ARPTABLES 8  "November 2011"
+.\"
+.\" Man page originally written by Jochen Friedrich <jochen@scram.de>,
+.\" maintained by Bart De Schuymer.
+.\" It is based on the iptables man page.
+.\"
+.\" Iptables page by Herve Eychenne March 2000.
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+arptables \- ARP table administration
+.SH SYNOPSIS
+.BR "arptables " [ "-t table" ] " -" [ AD ] " chain rule-specification " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ RI ] " chain rulenum rule-specification " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -D chain rulenum " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ "LFZ" ] " " [ chain ] " " [ options ]
+.br
+.BR "arptables " [ "-t table" ] " -" [ "NX" ] " chain"
+.br
+.BR "arptables " [ "-t table" ] " -E old-chain-name new-chain-name"
+.br
+.BR "arptables " [ "-t table" ] " -P chain target " [ options ]
+.SH DESCRIPTION
+.B arptables
+is a user space tool, it is used to set up and maintain the
+tables of ARP rules in the Linux kernel. These rules inspect
+the ARP frames which they see.
+.B arptables
+is analogous to the
+.B iptables
+user space tool, but
+.B arptables
+is less complicated.
+
+.SS CHAINS
+The kernel table is used to divide functionality into
+different sets of rules. Each set of rules is called a chain.
+Each chain is an ordered list of rules that can match ARP frames. If a
+rule matches an ARP frame, then a processing specification tells
+what to do with that matching frame. The processing specification is
+called a 'target'. However, if the frame does not match the current
+rule in the chain, then the next rule in the chain is examined and so forth.
+The user can create new (user-defined) chains which can be used as the 'target' of a rule.
+
+.SS TARGETS
+A firewall rule specifies criteria for an ARP frame and a frame
+processing specification called a target.  When a frame matches a rule,
+then the next action performed by the kernel is specified by the target.
+The target can be one of these values:
+.IR ACCEPT ,
+.IR DROP ,
+.IR CONTINUE ,
+.IR RETURN ,
+an 'extension' (see below) or a user-defined chain.
+.PP
+.I ACCEPT
+means to let the frame through.
+.I DROP
+means the frame has to be dropped.
+.I CONTINUE
+means the next rule has to be checked. This can be handy to know how many
+frames pass a certain point in the chain or to log those frames.
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.
+For the extension targets please see the
+.B "TARGET EXTENSIONS"
+section of this man page.
+.SS TABLES
+There is only one ARP table in the Linux
+kernel.  The table is
+.BR filter.
+You can drop the '-t filter' argument to the arptables command.
+The -t argument must be the
+first argument on the arptables command line, if used.
+.TP
+.B "-t, --table"
+.br
+.BR filter ,
+is the only table and contains two built-in chains:
+.B INPUT 
+(for frames destined for the host) and
+.B OUTPUT 
+(for locally-generated frames).
+.br
+.br
+.SH ARPTABLES COMMAND LINE ARGUMENTS
+After the initial arptables command line argument, the remaining
+arguments can be divided into several different groups.  These groups
+are commands, miscellaneous commands, rule-specifications, match-extensions,
+and watcher-extensions.
+.SS COMMANDS
+The arptables command arguments specify the actions to perform on the table
+defined with the -t argument.  If you do not use the -t argument to name
+a table, the commands apply to the default filter table.
+With the exception of the
+.B "-Z"
+command, only one command may be used on the command line at a time.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to delete, syntax: start_nr[:end_nr]. Using negative numbers is allowed, for more
+details about using negative numbers, see the -I command. The second usage is by
+specifying the complete rule as it would have been specified when it was added.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number.
+If the current number of rules equals N, then the specified number can be
+between -N and N+1. For a positive number i, it holds that i and i-N-1 specify the
+same place in the chain where the rule should be inserted. The number 0 specifies
+the place past the last rule in the chain and using this number is therefore
+equivalent with using the -A command.
+.TP
+.B "-R, --replace"
+Replaces the specified rule into the selected chain at the specified rule number.
+If the current number of rules equals N, then the specified number can be
+between 1 and N. i specifies the place in the chain where the rule should be replaced.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy can be
+.BR ACCEPT ", " DROP " or " RETURN .
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, then every chain will be
+flushed. Flushing the chain does not change the policy of the
+chain, however.
+.TP
+.B "-Z, --zero"
+Set the counters of the selected chain to zero. If no chain is selected, all the counters
+are set to zero. The
+.B "-Z"
+command can be used in conjunction with the 
+.B "-L"
+command.
+When both the
+.B "-Z"
+and
+.B "-L"
+commands are used together in this way, the rule counters are printed on the screen
+before they are set to zero.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain with the given name. The number of
+user-defined chains is unlimited. A user-defined chain name has maximum
+length of 31 characters.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no remaining references
+to the specified chain, otherwise
+.B arptables
+will refuse to delete it. If no chain is specified, all user-defined
+chains that aren't referenced will be removed.
+.TP
+.B "-E, --rename-chain"
+Rename the specified chain to a new name.  Besides renaming a user-defined
+chain, you may rename a standard chain name to a name that suits your
+taste. For example, if you like PREBRIDGING more than PREROUTING,
+then you can use the -E command to rename the PREROUTING chain. If you do
+rename one of the standard
+.B arptables
+chain names, please be sure to mention
+this fact should you post a question on the
+.B arptables
+mailing lists.
+It would be wise to use the standard name in your post. Renaming a standard
+.B arptables
+chain in this fashion has no effect on the structure or function
+of the
+.B arptables
+kernel table.
+
+.SS MISCELLANOUS COMMANDS
+.TP
+.B "-V, --version"
+Show the version of the arptables userspace program.
+.TP
+.B "-h, --help"
+Give a brief description of the command syntax.
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+a target extension (see
+.BR "TARGET EXTENSIONS" ")"
+or a user-defined chain name.
+.TP
+.BI "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+
+.SS RULE-SPECIFICATIONS
+The following command line arguments make up a rule specification (as used 
+in the add and delete commands). A "!" option before the specification 
+inverts the test for that specification. Apart from these standard rule 
+specifications there are some other command line arguments of interest.
+.TP
+.BR "-s, --source-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+The Source IP specification.
+.TP 
+.BR "-d, --destination-ip " "[!] \fIaddress\fP[/\fImask]\fP"
+The Destination IP specification.
+.TP 
+.BR "--source-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+The source mac address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons.
+.TP
+.BR "--destination-mac " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination mac address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons.
+.TP 
+.BR "-i, --in-interface " "[!] \fIname\fP"
+The interface via which a frame is received (for the
+.B INPUT
+chain). The flag
+.B --in-if
+is an alias for this option.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface via which a frame is going to be sent (for the
+.B OUTPUT
+chain). The flag
+.B --out-if
+is an alias for this option.
+.TP
+.BR "-l, --h-length " "\fIlength\fP[/\fImask\fP]"
+The hardware length (nr of bytes)
+.TP
+.BR "--opcode " "\fIcode\fP[/\fImask\fP]
+The operation code (2 bytes). Available values are:
+.BR 1 = Request
+.BR 2 = Reply
+.BR 3 = Request_Reverse
+.BR 4 = Reply_Reverse
+.BR 5 = DRARP_Request
+.BR 6 = DRARP_Reply
+.BR 7 = DRARP_Error
+.BR 8 = InARP_Request
+.BR 9 = ARP_NAK .
+.TP
+.BR "--h-type " "\fItype\fP[/\fImask\fP]"
+The hardware type (2 bytes, hexadecimal). Available values are:
+.BR 1 = Ethernet .
+.TP
+.BR "--proto-type " "\fItype\fP[/\fImask\fP]"
+The protocol type (2 bytes). Available values are:
+.BR 0x800 = IPv4 .
+
+.SS TARGET-EXTENSIONS
+.B arptables
+extensions are precompiled into the userspace tool. So there is no need
+to explicitly load them with a -m option like in
+.BR iptables .
+However, these
+extensions deal with functionality supported by supplemental kernel modules.
+.SS mangle
+.TP
+.BR "--mangle-ip-s IP address"
+Mangles Source IP Address to given value.
+.TP
+.BR "--mangle-ip-d IP address"
+Mangles Destination IP Address to given value.
+.TP
+.BR "--mangle-mac-s MAC address"
+Mangles Source MAC Address to given value.
+.TP
+.BR "--mangle-mac-d MAC address"
+Mangles Destination MAC Address to given value.
+.TP
+.BR "--mangle-target target "
+Target of ARP mangle operation
+.BR "" ( DROP ", " CONTINUE " or " ACCEPT " -- default is " ACCEPT ).
+.SS CLASSIFY
+This  module  allows you to set the skb->priority value (and thus clas-
+sify the packet into a specific CBQ class).
+
+.TP
+.BR "--set-class major:minor"
+
+Set the major and minor  class  value.  The  values  are  always
+interpreted as hexadecimal even if no 0x prefix is given.
+
+.SH NOTES
+In this nft-based version of
+.BR arptables ,
+support for
+.B FORWARD
+chain has not been implemented. Since ARP packets are "forwarded" only by Linux
+bridges, the same may be achieved using
+.B FORWARD
+chain in
+.BR ebtables .
+
+.SH MAILINGLISTS
+.BR "" "See " http://netfilter.org/mailinglists.html
+.SH SEE ALSO
+.BR xtables-nft "(8), " iptables "(8), " ebtables "(8), " arp "(8), " rarp "(8), " ifconfig "(8), " route (8)
+.PP
+.BR "" "See " http://ebtables.sf.net
diff --git a/SOURCES/arptables.service b/SOURCES/arptables.service
new file mode 100644
index 0000000..df6c7d6
--- /dev/null
+++ b/SOURCES/arptables.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Automates a packet filtering firewall with arptables
+After=network.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/libexec/arptables-helper start
+ExecStop=/usr/libexec/arptables-helper stop
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/SOURCES/ebtables-config b/SOURCES/ebtables-config
new file mode 100644
index 0000000..69d9289
--- /dev/null
+++ b/SOURCES/ebtables-config
@@ -0,0 +1,11 @@
+# Save current firewall rules on stop.
+#   Value: yes|no,  default: no
+# Saves all firewall rules if firewall gets stopped
+# (e.g. on system shutdown).
+EBTABLES_SAVE_ON_STOP="no"
+
+# Save (and restore) rule counters.
+#   Value: yes|no,  default: no
+# Save rule counters when saving a kernel table to a file. If the
+# rule counters were saved, they will be restored when restoring the table.
+EBTABLES_SAVE_COUNTER="no"
diff --git a/SOURCES/ebtables.8 b/SOURCES/ebtables.8
new file mode 100644
index 0000000..67201b6
--- /dev/null
+++ b/SOURCES/ebtables.8
@@ -0,0 +1,1096 @@
+.TH EBTABLES 8  "December 2011"
+.\"
+.\" Man page written by Bart De Schuymer <bdschuym@pandora.be>
+.\" It is based on the iptables man page.
+.\"
+.\" The man page was edited, February 25th 2003, by 
+.\"      Greg Morgan <" dr_kludge_at_users_sourceforge_net >
+.\"
+.\" Iptables page by Herve Eychenne March 2000.
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     You should have received a copy of the GNU General Public License
+.\"     along with this program; if not, write to the Free Software
+.\"     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"     
+.\"
+.SH NAME
+ebtables-nft \- Ethernet bridge frame table administration
+.SH SYNOPSIS
+.BR "ebtables " [ -t " table ] " - [ ACDI "] chain rule specification [match extensions] [watcher extensions] target"
+.br
+.BR "ebtables " [ -t " table ] " -P " chain " ACCEPT " | " DROP " | " RETURN
+.br
+.BR "ebtables " [ -t " table ] " -F " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -Z " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -L " [" -Z "] [chain] [ [" --Ln "] | [" --Lx "] ] [" --Lc "] [" --Lmac2 ]
+.br
+.BR "ebtables " [ -t " table ] " -N " chain [" "-P ACCEPT " | " DROP " | " RETURN" ]
+.br
+.BR "ebtables " [ -t " table ] " -X " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -E " old-chain-name new-chain-name"
+.br
+.BR "ebtables " [ -t " table ] " --init-table
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-commit
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-init
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-save
+.br
+.SH DESCRIPTION
+.B ebtables
+is an application program used to set up and maintain the
+tables of rules (inside the Linux kernel) that inspect
+Ethernet frames.
+It is analogous to the
+.B iptables
+application, but less complicated, due to the fact that the Ethernet protocol
+is much simpler than the IP protocol.
+.SS CHAINS
+There are two ebtables-nft tables with built-in chains in the
+Linux kernel. These tables are used to divide functionality into
+different sets of rules. Each set of rules is called a chain.
+Each chain is an ordered list of rules that can match Ethernet frames. If a
+rule matches an Ethernet frame, then a processing specification tells
+what to do with that matching frame. The processing specification is
+called a 'target'. However, if the frame does not match the current
+rule in the chain, then the next rule in the chain is examined and so forth.
+The user can create new (user-defined) chains that can be used as the 'target'
+of a rule. User-defined chains are very useful to get better performance
+over the linear traversal of the rules and are also essential for structuring
+the filtering rules into well-organized and maintainable sets of rules.
+.SS TARGETS
+A firewall rule specifies criteria for an Ethernet frame and a frame
+processing specification called a target.  When a frame matches a rule,
+then the next action performed by the kernel is specified by the target.
+The target can be one of these values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+an 'extension' (see below) or a jump to a user-defined chain.
+.PP
+.B ACCEPT
+means to let the frame through.
+.B DROP
+means the frame has to be dropped.
+.B CONTINUE
+means the next rule has to be checked. This can be handy, f.e., to know how many
+frames pass a certain point in the chain, to log those frames or to apply multiple
+targets on a frame.
+.B RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.
+For the extension targets please refer to the
+.B "TARGET EXTENSIONS"
+section of this man page.
+.SS TABLES
+As stated earlier, there are two ebtables-nft tables in the Linux
+kernel.  The table names are
+.BR filter " and " nat .
+Of these two tables,
+the filter table is the default table that the command operates on.
+If you are working with the filter table, then you can drop the '-t filter'
+argument to the ebtables command.  However, you will need to provide
+the -t argument for
+.B nat
+table.  Moreover, the -t argument must be the
+first argument on the ebtables command line, if used. 
+.TP
+.B "-t, --table"
+.br
+.B filter
+is the default table and contains three built-in chains:
+.B INPUT 
+(for frames destined for the bridge itself, on the level of the MAC destination address), 
+.B OUTPUT 
+(for locally-generated or (b)routed frames) and
+.B FORWARD 
+(for frames being forwarded by the bridge).
+.br
+.br
+.B nat
+is mostly used to change the mac addresses and contains three built-in chains:
+.B PREROUTING 
+(for altering frames as soon as they come in), 
+.B OUTPUT 
+(for altering locally generated or (b)routed frames before they are bridged) and 
+.B POSTROUTING
+(for altering frames as they are about to go out). A small note on the naming
+of chains PREROUTING and POSTROUTING: it would be more accurate to call them
+PREFORWARDING and POSTFORWARDING, but for all those who come from the
+iptables world to ebtables it is easier to have the same names. Note that you
+can change the name
+.BR "" ( -E )
+if you don't like the default.
+.SH EBTABLES COMMAND LINE ARGUMENTS
+After the initial ebtables '-t table' command line argument, the remaining
+arguments can be divided into several groups.  These groups
+are commands, miscellaneous commands, rule specifications, match extensions,
+watcher extensions and target extensions.
+.SS COMMANDS
+The ebtables command arguments specify the actions to perform on the table
+defined with the -t argument.  If you do not use the -t argument to name
+a table, the commands apply to the default filter table.
+Only one command may be used on the command line at a time, except when
+the commands
+.BR -L " and " -Z
+are combined, the commands
+.BR -N " and " -P
+are combined, or when
+.B --atomic-file
+is used.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to delete (directly after
+.BR -D ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). When \fIend_nr\fP is omitted, all rules starting
+from \fIstart_nr\fP are deleted. Using negative numbers is allowed, for more
+details about using negative numbers, see the
+.B -I
+command. The second usage is by
+specifying the complete rule as it would have been specified when it was added. Only
+the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, is deleted.
+.TP
+.B "-C, --change-counters"
+Change the counters of the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to do the changes on (directly after
+.BR -C ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). The details are the same as for the
+.BR -D " command. The second usage is by"
+specifying the complete rule as it would have been specified when it was added. Only
+the counters of the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, are changed.
+In the first usage, the counters are specified directly after the interval specification,
+in the second usage directly after
+.BR -C .
+First the packet counter is specified, then the byte counter. If the specified counters start
+with a '+', the counter values are added to the respective current counter values.
+If the specified counters start with a '-', the counter values are decreased from the respective
+current counter values. No bounds checking is done. If the counters don't start with '+' or '-',
+the current counters are changed to the specified counters.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number. If the
+rule number is not specified, the rule is added at the head of the chain.
+If the current number of rules equals
+.IR N ,
+then the specified number can be
+between
+.IR -N " and " N+1 .
+For a positive number
+.IR i ,
+it holds that
+.IR i " and " i-N-1
+specify the same place in the chain where the rule should be inserted. The rule number
+0 specifies the place past the last rule in the chain and using this number is therefore
+equivalent to using the
+.BR -A " command."
+Rule numbers structly smaller than 0 can be useful when more than one rule needs to be inserted
+in a chain.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy can be
+.BR ACCEPT ", " DROP " or " RETURN .
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, then every chain will be
+flushed. Flushing a chain does not change the policy of the
+chain, however.
+.TP
+.B "-Z, --zero"
+Set the counters of the selected chain to zero. If no chain is selected, all the counters
+are set to zero. The
+.B "-Z"
+command can be used in conjunction with the 
+.B "-L"
+command.
+When both the
+.B "-Z"
+and
+.B "-L"
+commands are used together in this way, the rule counters are printed on the screen
+before they are set to zero.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed.
+.br
+The following options change the output of the
+.B "-L"
+command.
+.br
+.B "--Ln"
+.br
+Places the rule number in front of every rule. This option is incompatible with the
+.BR --Lx " option."
+.br
+.B "--Lc"
+.br
+Shows the counters at the end of each rule displayed by the
+.B "-L"
+command. Both a frame counter (pcnt) and a byte counter (bcnt) are displayed.
+The frame counter shows how many frames have matched the specific rule, the byte
+counter shows the sum of the frame sizes of these matching frames. Using this option
+.BR "" "in combination with the " --Lx " option causes the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lx"
+.br
+Changes the output so that it produces a set of ebtables commands that construct
+the contents of the chain, when specified.
+If no chain is specified, ebtables commands to construct the contents of the
+table are given, including commands for creating the user-defined chains (if any).
+You can use this set of commands in an ebtables boot or reload
+script.  For example the output could be used at system startup.
+The 
+.B "--Lx"
+option is incompatible with the
+.B "--Ln"
+listing option. Using the
+.BR --Lx " option together with the " --Lc " option will cause the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lmac2"
+.br
+Shows all MAC addresses with the same length, adding leading zeroes
+if necessary. The default representation omits leading zeroes in the addresses.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain with the given name. The number of
+user-defined chains is limited only by the number of possible chain names.
+A user-defined chain name has a maximum
+length of 31 characters. The standard policy of the user-defined chain is
+ACCEPT. The policy of the new chain can be initialized to a different standard
+target by using the
+.B -P
+command together with the
+.B -N
+command. In this case, the chain name does not have to be specified for the
+.B -P
+command.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no remaining references (jumps)
+to the specified chain, otherwise ebtables will refuse to delete it. If no chain is
+specified, all user-defined chains that aren't referenced will be removed.
+.TP
+.B "-E, --rename-chain"
+Rename the specified chain to a new name.  Besides renaming a user-defined
+chain, you can rename a standard chain to a name that suits your
+taste. For example, if you like PREFORWARDING more than PREROUTING,
+then you can use the -E command to rename the PREROUTING chain. If you do
+rename one of the standard ebtables chain names, please be sure to mention
+this fact should you post a question on the ebtables mailing lists.
+It would be wise to use the standard name in your post. Renaming a standard
+ebtables chain in this fashion has no effect on the structure or functioning
+of the ebtables kernel table.
+.TP
+.B "--init-table"
+Replace the current table data by the initial table data.
+.TP
+.B "--atomic-init"
+Copy the kernel's initial data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-save"
+Copy the kernel's current data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-commit"
+Replace the kernel table data with the data contained in the specified
+file. This is a useful command that allows you to load all your rules of a
+certain table into the kernel at once, saving the kernel a lot of precious
+time and allowing atomic updates of the tables. The file which contains
+the table data is constructed by using either the
+.B "--atomic-init"
+or the
+.B "--atomic-save"
+command to generate a starting file. After that, using the
+.B "--atomic-file"
+command when constructing rules or setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable"
+allows you to extend the file and build the complete table before
+committing it to the kernel. This command can be very useful in boot scripts
+to populate the ebtables tables in a fast way.
+.SS MISCELLANOUS COMMANDS
+.TP
+.B "-V, --version"
+Show the version of the ebtables userspace program.
+.TP
+.BR "-h, --help " "[\fIlist of module names\fP]"
+Give a brief description of the command syntax. Here you can also specify
+names of extensions and ebtables will try to write help about those
+extensions. E.g.
+.IR "ebtables -h snat log ip arp" .
+Specify
+.I list_extensions
+to list all extensions supported by the userspace
+utility.
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+a target extension (see
+.BR "TARGET EXTENSIONS" ")"
+or a user-defined chain name.
+.TP
+.B --atomic-file "\fIfile\fP"
+Let the command operate on the specified
+.IR file .
+The data of the table to
+operate on will be extracted from the file and the result of the operation
+will be saved back into the file. If specified, this option should come
+before the command specification. An alternative that should be preferred,
+is setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B -M, --modprobe "\fIprogram\fP"
+When talking to the kernel, use this
+.I program
+to try to automatically load missing kernel modules.
+.TP
+.B --concurrent
+Use a file lock to support concurrent scripts updating the ebtables kernel tables.
+
+.SS
+RULE SPECIFICATIONS
+The following command line arguments make up a rule specification (as used 
+in the add and delete commands). A "!" option before the specification 
+inverts the test for that specification. Apart from these standard rule 
+specifications there are some other command line arguments of interest.
+See both the 
+.BR "MATCH EXTENSIONS" 
+and the
+.BR "WATCHER EXTENSIONS" 
+below.
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol that was responsible for creating the frame. This can be a
+hexadecimal number, above 
+.IR 0x0600 ,
+a name (e.g.
+.I ARP
+) or
+.BR LENGTH .
+The protocol field of the Ethernet frame can be used to denote the
+length of the header (802.2/802.3 networks). When the value of that field is
+below or equals
+.IR 0x0600 ,
+the value equals the size of the header and shouldn't be used as a
+protocol number. Instead, all frames where the protocol field is used as
+the length field are assumed to be of the same 'protocol'. The protocol
+name used in ebtables for these frames is
+.BR LENGTH .
+.br
+The file
+.B /etc/ethertypes
+can be used to show readable
+characters instead of hexadecimal numbers for the protocols. For example,
+.I 0x0800
+will be represented by 
+.IR IPV4 .
+The use of this file is not case sensitive. 
+See that file for more information. The flag 
+.B --proto
+is an alias for this option.
+.TP 
+.BR "-i, --in-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --in-if
+is an alias for this option.
+.TP
+.BR "--logical-in " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is going to be sent (this option is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --out-if
+is an alias for this option.
+.TP
+.BR "--logical-out " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is going to be sent (this option
+is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source MAC address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons. Alternatively one can specify Unicast,
+Multicast, Broadcast or BGA (Bridge Group Address):
+.br
+.IR "Unicast" "=00:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Multicast" "=01:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Broadcast" "=ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff or"
+.IR "BGA" "=01:80:c2:00:00:00/ff:ff:ff:ff:ff:ff."
+Note that a broadcast
+address will also match the multicast specification. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination MAC address. See
+.B -s
+(above) for more details on MAC addresses. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BR "-c, --set-counter " "\fIpcnt bcnt\fP"
+If used with
+.BR -A " or " -I ", then the packet and byte counters of the new rule will be set to
+.IR pcnt ", resp. " bcnt ".
+If used with the
+.BR -C " or " -D " commands, only rules with a packet and byte count equal to"
+.IR pcnt ", resp. " bcnt " will match."
+
+.SS MATCH EXTENSIONS
+Ebtables extensions are dynamically loaded into the userspace tool,
+there is therefore no need to explicitly load them with a
+-m option like is done in iptables.
+These extensions deal with functionality supported by kernel modules supplemental to
+the core ebtables code.
+.SS 802_3
+Specify 802.3 DSAP/SSAP fields or SNAP type.  The protocol must be specified as
+.IR "LENGTH " "(see the option " " -p " above).
+.TP
+.BR "--802_3-sap " "[!] \fIsap\fP"
+DSAP and SSAP are two one byte 802.3 fields.  The bytes are always
+equal, so only one byte (hexadecimal) is needed as an argument.
+.TP
+.BR "--802_3-type " "[!] \fItype\fP"
+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 arp
+Specify (R)ARP fields. The protocol must be specified as
+.IR ARP " or " RARP .
+.TP
+.BR "--arp-opcode " "[!] \fIopcode\fP"
+The (R)ARP opcode (decimal or a string, for more details see
+.BR "ebtables -h arp" ).
+.TP
+.BR "--arp-htype " "[!] \fIhardware type\fP"
+The hardware type, this can be a decimal or the string
+.I Ethernet
+(which sets
+.I type
+to 1). Most (R)ARP packets have Eternet as hardware type.
+.TP
+.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+The protocol type for which the (r)arp is used (hexadecimal or the string
+.IR IPv4 ,
+denoting 0x0800).
+Most (R)ARP packets have protocol type IPv4.
+.TP
+.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP source address specification.
+.TP
+.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP destination address specification.
+.TP
+.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC source address specification.
+.TP
+.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC destination address specification.
+.TP
+.BR "" "[!]" " --arp-gratuitous"
+Checks for ARP gratuitous packets: checks equality of IPv4 source
+address and IPv4 destination address inside the ARP header.
+.SS ip
+Specify IPv4 fields. The protocol must be specified as
+.IR IPv4 .
+.TP
+.BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IP address.
+The flag
+.B --ip-src
+is an alias for this option.
+.TP
+.BR "--ip-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IP address.
+The flag
+.B --ip-dst
+is an alias for this option.
+.TP
+.BR "--ip-tos " "[!] \fItos\fP"
+The IP type of service, in hexadecimal numbers.
+.BR IPv4 .
+.TP
+.BR "--ip-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip-proto
+is an alias for this option.
+.TP
+.BR "--ip-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IP protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-sport
+is an alias for this option.
+.TP
+.BR "--ip-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for ip protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-dport
+is an alias for this option.
+.SS ip6
+Specify IPv6 fields. The protocol must be specified as
+.IR IPv6 .
+.TP
+.BR "--ip6-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IPv6 address.
+The flag
+.B --ip6-src
+is an alias for this option.
+.TP
+.BR "--ip6-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IPv6 address.
+The flag
+.B --ip6-dst
+is an alias for this option.
+.TP
+.BR "--ip6-tclass " "[!] \fItclass\fP"
+The IPv6 traffic class, in hexadecimal numbers.
+.TP
+.BR "--ip6-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip6-proto
+is an alias for this option.
+.TP
+.BR "--ip6-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-sport
+is an alias for this option.
+.TP
+.BR "--ip6-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-dport
+is an alias for this option.
+.TP
+.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}"
+Specify ipv6\-icmp type and code to match.
+Ranges for both type and code are supported. Type and code are
+separated by a slash. Valid numbers for type and range are 0 to 255.
+To match a single type including all valid codes, symbolic names can
+be used instead of numbers. The list of known type names is shown by the command
+.nf
+  ebtables \-\-help ip6
+.fi
+This option is only valid for \-\-ip6-prococol ipv6-icmp.
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached.
+It can be used with the
+.B --log
+watcher to give limited logging, for example. Its use is the same
+as the limit match of iptables.
+.TP
+.BR "--limit " "[\fIvalue\fP]"
+Maximum average matching rate: specified as a number, with an optional
+.IR /second ", " /minute ", " /hour ", or " /day " suffix; the default is " 3/hour .
+.TP
+.BR "--limit-burst " "[\fInumber\fP]"
+Maximum initial number of packets to match: this number gets recharged by
+one every time the limit specified above is not reached, up to this
+number; the default is
+.IR 5 .
+.SS mark_m
+.TP
+.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
+Matches frames with the given unsigned mark value. If a
+.IR value " and " mask " are specified, the logical AND of the mark value of the frame and"
+the user-specified
+.IR mask " is taken before comparing it with the"
+user-specified mark
+.IR value ". When only a mark "
+.IR value " is specified, the packet"
+only matches when the mark value of the frame equals the user-specified
+mark
+.IR value .
+If only a
+.IR mask " is specified, the logical"
+AND of the mark value of the frame and the user-specified
+.IR mask " is taken and the frame matches when the result of this logical AND is"
+non-zero. Only specifying a
+.IR mask " is useful to match multiple mark values."
+.SS pkttype
+.TP
+.BR "--pkttype-type " "[!] \fItype\fP"
+Matches on the Ethernet "class" of the frame, which is determined by the
+generic networking code. Possible values:
+.IR broadcast " (MAC destination is the broadcast address),"
+.IR multicast " (MAC destination is a multicast address),"
+.IR host " (MAC destination is the receiving network device), or "
+.IR otherhost " (none of the above)."
+.SS stp
+Specify stp BPDU (bridge protocol data unit) fields. The destination
+address
+.BR "" ( -d ") must be specified as the bridge group address"
+.IR "" ( BGA ).
+For all options for which a range of values can be specified, it holds that
+if the lower bound is omitted (but the colon is not), then the lowest possible lower bound
+for that option is used, while if the upper bound is omitted (but the colon again is not), the
+highest possible upper bound for that option is used.
+.TP
+.BR "--stp-type " "[!] \fItype\fP"
+The BPDU type (0-255), recognized non-numerical types are
+.IR config ", denoting a configuration BPDU (=0), and"
+.IR tcn ", denothing a topology change notification BPDU (=128)."
+.TP
+.BR "--stp-flags " "[!] \fIflag\fP"
+The BPDU flag (0-255), recognized non-numerical flags are
+.IR topology-change ", denoting the topology change flag (=1), and"
+.IR topology-change-ack ", denoting the topology change acknowledgement flag (=128)."
+.TP
+.BR "--stp-root-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The root priority (0-65535) range.
+.TP
+.BR "--stp-root-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The root mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-root-cost " "[!] [\fIcost\fP][:\fIcost\fP]"
+The root path cost (0-4294967295) range.
+.TP
+.BR "--stp-sender-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The BPDU's sender priority (0-65535) range.
+.TP
+.BR "--stp-sender-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The BPDU's sender mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-port " "[!] [\fIport\fP][:\fIport\fP]"
+The port identifier (0-65535) range.
+.TP
+.BR "--stp-msg-age " "[!] [\fIage\fP][:\fIage\fP]"
+The message age timer (0-65535) range.
+.TP
+.BR "--stp-max-age " "[!] [\fIage\fP][:\fIage\fP]"
+The max age timer (0-65535) range.
+.TP
+.BR "--stp-hello-time " "[!] [\fItime\fP][:\fItime\fP]"
+The hello time timer (0-65535) range.
+.TP
+.BR "--stp-forward-delay " "[!] [\fIdelay\fP][:\fIdelay\fP]"
+The forward delay timer (0-65535) range.
+.SS vlan
+Specify 802.1Q Tag Control Information fields.
+The protocol must be specified as
+.IR 802_1Q " (0x8100)."
+.TP
+.BR "--vlan-id " "[!] \fIid\fP"
+The VLAN identifier field (VID). Decimal number from 0 to 4095.
+.TP
+.BR "--vlan-prio " "[!] \fIprio\fP"
+The user priority field, a decimal number from 0 to 7.
+The VID should be set to 0 ("null VID") or unspecified
+(in the latter case the VID is deliberately set to 0).
+.TP
+.BR "--vlan-encap " "[!] \fItype\fP"
+The encapsulated Ethernet frame type/length.
+Specified as a hexadecimal
+number from 0x0000 to 0xFFFF or as a symbolic name
+from
+.BR /etc/ethertypes .
+
+.SS WATCHER EXTENSIONS
+Watchers only look at frames passing by, they don't modify them nor decide
+to accept the frames or not. These watchers only
+see the frame if the frame matches the rule, and they see it before the
+target is executed.
+.SS log
+The log watcher writes descriptive data about a frame to the syslog.
+.TP
+.B "--log"
+.br
+Log with the default loggin options: log-level=
+.IR info ,
+log-prefix="", no ip logging, no arp logging.
+.TP
+.B --log-level "\fIlevel\fP"
+.br
+Defines the logging level. For the possible values, see
+.BR "ebtables -h log" .
+The default level is 
+.IR info .
+.TP
+.BR --log-prefix " \fItext\fP"
+.br
+Defines the prefix
+.I text
+to be printed at the beginning of the line with the logging information.
+.TP
+.B --log-ip 
+.br
+Will log the ip information when a frame made by the ip protocol matches 
+the rule. The default is no ip information logging.
+.TP
+.B --log-ip6 
+.br
+Will log the ipv6 information when a frame made by the ipv6 protocol matches 
+the rule. The default is no ipv6 information logging.
+.TP
+.B --log-arp
+.br
+Will log the (r)arp information when a frame made by the (r)arp protocols
+matches the rule. The default is no (r)arp information logging.
+.SS nflog
+The nflog watcher passes the packet to the loaded logging backend
+in order to log the packet. This is usually used in combination with
+nfnetlink_log as logging backend, which will multicast the packet
+through a
+.IR netlink
+socket to the specified multicast group. One or more userspace processes
+may subscribe to the group to receive the packets.
+.TP
+.B "--nflog"
+.br
+Log with the default logging options
+.TP
+.B --nflog-group "\fInlgroup\fP"
+.br
+The netlink group (1 - 2^32-1) to which packets are (only applicable for
+nfnetlink_log). The default value is 1.
+.TP
+.B --nflog-prefix "\fIprefix\fP"
+.br
+A prefix string to include in the log message, up to 30 characters
+long, useful for distinguishing messages in the logs.
+.TP
+.B --nflog-range "\fIsize\fP"
+.br
+The number of bytes to be copied to userspace (only applicable for
+nfnetlink_log). nfnetlink_log instances may specify their own
+range, this option overrides it.
+.TP
+.B --nflog-threshold "\fIsize\fP"
+.br
+Number of packets to queue inside the kernel before sending them
+to userspace (only applicable for nfnetlink_log). Higher values
+result in less overhead per packet, but increase delay until the
+packets reach userspace. The default value is 1.
+.SS ulog
+The ulog watcher passes the packet to a userspace
+logging daemon using netlink multicast sockets. This differs
+from the log watcher in the sense that the complete packet is
+sent to userspace instead of a descriptive text and that
+netlink multicast sockets are used instead of the syslog.
+This watcher enables parsing of packets with userspace programs, the
+physical bridge in and out ports are also included in the netlink messages.
+The ulog watcher module accepts 2 parameters when the module is loaded
+into the kernel (e.g. with modprobe):
+.B nlbufsiz
+specifies how big the buffer for each netlink multicast
+group is. If you say
+.IR nlbufsiz=8192 ,
+for example, up to eight kB of packets will
+get accumulated in the kernel until they are sent to userspace. It is
+not possible to allocate more than 128kB. Please also keep in mind that
+this buffer size is allocated for each nlgroup you are using, so the
+total kernel memory usage increases by that factor. The default is 4096.
+.B flushtimeout
+specifies after how many hundredths of a second the queue should be
+flushed, even if it is not full yet. The default is 10 (one tenth of
+a second).
+.TP
+.B "--ulog"
+.br
+Use the default settings: ulog-prefix="", ulog-nlgroup=1,
+ulog-cprange=4096, ulog-qthreshold=1.
+.TP
+.B --ulog-prefix "\fItext\fP"
+.br
+Defines the prefix included with the packets sent to userspace.
+.TP
+.BR --ulog-nlgroup " \fIgroup\fP"
+.br
+Defines which netlink group number to use (a number from 1 to 32).
+Make sure the netlink group numbers used for the iptables ULOG
+target differ from those used for the ebtables ulog watcher.
+The default group number is 1.
+.TP
+.BR --ulog-cprange " \fIrange\fP"
+.br
+Defines the maximum copy range to userspace, for packets matching the
+rule. The default range is 0, which means the maximum copy range is
+given by
+.BR nlbufsiz .
+A maximum copy range larger than
+128*1024 is meaningless as the packets sent to userspace have an upper
+size limit of 128*1024.
+.TP
+.BR --ulog-qthreshold " \fIthreshold\fP"
+.br
+Queue at most
+.I threshold
+number of packets before sending them to
+userspace with a netlink socket. Note that packets can be sent to
+userspace before the queue is full, this happens when the ulog
+kernel timer goes off (the frequency of this timer depends on
+.BR flushtimeout ).
+.SS TARGET EXTENSIONS
+.SS arpreply
+The
+.B arpreply
+target can be used in the
+.BR PREROUTING " chain of the " nat " table."
+If this target sees an ARP request it will automatically reply
+with an ARP reply. The used MAC address for the reply can be specified.
+The protocol must be specified as
+.IR ARP .
+When the ARP message is not an ARP request or when the ARP request isn't
+for an IP address on an Ethernet network, it is ignored by this target
+.BR "" ( CONTINUE ).
+When the ARP request is malformed, it is dropped
+.BR "" ( DROP ).
+.TP
+.BR "--arpreply-mac " "\fIaddress\fP"
+Specifies the MAC address to reply with: the Ethernet source MAC and the
+ARP payload source MAC will be filled in with this address.
+.TP
+.BR "--arpreply-target " "\fItarget\fP"
+Specifies the standard target. After sending the ARP reply, the rule still
+has to give a standard target so ebtables knows what to do with the ARP request.
+The default target
+.BR "" "is " DROP .
+.SS dnat
+The
+.B dnat
+target can only be used in the
+.BR PREROUTING " and " OUTPUT " chains of the " nat " table."
+It specifies that the destination MAC address has to be changed.
+.TP
+.BR "--to-destination " "\fIaddress\fP"
+.br
+Change the destination MAC address to the specified
+.IR address .
+The flag
+.B --to-dst
+is an alias for this option.
+.TP
+.BR "--dnat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the dnat, the rule still has to
+give a standard target so ebtables knows what to do with the dnated frame.
+The default target is
+.BR ACCEPT .
+Making it
+.BR CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " only makes"
+sense in the
+.BR BROUTING " chain but using the " redirect " target is more logical there. " RETURN " is also allowed. Note that using " RETURN
+in a base chain is not allowed (for obvious reasons).
+.SS mark
+.BR "" "The " mark " target can be used in every chain of every table. It is possible"
+to use the marking of a frame/packet in both ebtables and iptables,
+if the bridge-nf code is compiled into the kernel. Both put the marking at the
+same place. This allows for a form of communication between ebtables and iptables.
+.TP
+.BR "--mark-set " "\fIvalue\fP"
+.br
+Mark the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-or " "\fIvalue\fP"
+.br
+Or the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-and " "\fIvalue\fP"
+.br
+And the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-xor " "\fIvalue\fP"
+.br
+Xor the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-target " "\fItarget\fP"
+.br
+Specifies the standard target. After marking the frame, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " can let you do other"
+things with the frame in subsequent rules of the chain.
+.SS redirect
+The
+.B redirect
+target will change the MAC target address to that of the bridge device the
+frame arrived on. This target can only be used in the
+.BR PREROUTING " chain of the " nat " table."
+The MAC address of the bridge is used as destination address."
+.TP
+.BR "--redirect-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the MAC redirect, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " in the " BROUTING " chain will let the frames be routed. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.SS snat
+The
+.B snat
+target can only be used in the
+.BR POSTROUTING " chain of the " nat " table."
+It specifies that the source MAC address has to be changed.
+.TP
+.BR "--to-source " "\fIaddress\fP"
+.br
+Changes the source MAC address to the specified
+.IR address ". The flag"
+.B --to-src
+is an alias for this option.
+.TP
+.BR "--snat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the snat, the rule still has 
+to give a standard target so ebtables knows what to do.
+.BR "" "The default target is " ACCEPT ". Making it " CONTINUE " could let you use"
+.BR "" "multiple target extensions on the same frame. Making it " DROP " doesn't"
+.BR "" "make sense, but you could do that too. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.br
+.TP
+.BR "--snat-arp "
+.br
+Also change the hardware source address inside the arp header if the packet is an
+arp message and the hardware address length in the arp header is 6 bytes.
+.br
+.SH FILES
+.I /etc/ethertypes
+.I /run/ebtables.lock
+.SH ENVIRONMENT VARIABLES
+.I EBTABLES_ATOMIC_FILE
+.SH MAILINGLISTS
+.BR "" "See " http://netfilter.org/mailinglists.html
+.SH BUGS
+The version of ebtables this man page ships with does not support the
+.B broute
+table. Also there is no support for the
+.B among
+match. And finally, this list is probably not complete.
+.SH SEE ALSO
+.BR xtables-nft "(8), " iptables "(8), " brctl "(8), " ifconfig "(8), " route (8)
+.PP
+.BR "" "See " http://ebtables.sf.net
diff --git a/SOURCES/ebtables.service b/SOURCES/ebtables.service
new file mode 100644
index 0000000..e0b0162
--- /dev/null
+++ b/SOURCES/ebtables.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Ethernet Bridge Filtering tables
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/libexec/ebtables start
+ExecStop=/usr/libexec/ebtables stop
+
+[Install]
+WantedBy=multi-user.target
diff --git a/SOURCES/ebtables.systemd b/SOURCES/ebtables.systemd
new file mode 100644
index 0000000..c31ddc0
--- /dev/null
+++ b/SOURCES/ebtables.systemd
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+RETVAL=0
+EBTCONF=/etc/sysconfig/ebtables
+
+initialize() {
+  # Initialize $TYPE tables
+  echo -n $"  $TYPE tables: "
+  if [ -r /etc/sysconfig/ebtables.$TYPE ]; then
+    /sbin/ebtables -t $TYPE --atomic-file /etc/sysconfig/ebtables.$TYPE --atomic-commit > /dev/null || RETVAL=1
+  else
+    echo -n "not configured"
+  fi
+  if [ $RETVAL -eq 0 ]; then
+    echo -n $"[  OK  ]"
+    echo -ne "\r"
+  else
+    echo -n $"[FAILED]"
+    echo -ne "\r"
+  fi
+}
+
+case $1 in
+  start)
+    if [[ -r $EBTCONF ]]; then
+      ebtables-restore <$EBTCONF
+      RETVAL=$?
+    else
+      echo -n "not configured"
+    fi
+    if [ $RETVAL -eq 0 ]; then
+      echo -n $"[  OK  ]"
+      echo -ne "\r"
+    else
+      echo -n $"[FAILED]"
+      echo -ne "\r"
+    fi
+  ;;
+  stop)
+    [[ $EBTABLES_SAVE_ON_STOP == "yes" ]] && $0 save
+    /sbin/ebtables --init-table
+    RETVAL=$?
+
+    if [ $RETVAL -eq 0 ]; then
+      echo -n $"[  OK  ]"
+      echo -ne "\r"
+    else
+      echo -n $"[FAILED]"
+      echo -ne "\r"
+    fi
+  ;;
+  save)
+    echo -n $"Saving Ethernet bridge filtering (ebtables): "
+    ebtables-save >$EBTCONF
+    RETVAL=$?
+
+    if [ $RETVAL -eq 0 ]; then
+      echo -n $"[  OK  ]"
+      echo -ne "\r"
+    else
+      echo -n $"[FAILED]"
+      echo -ne "\r"
+    fi
+  ;;
+  *)
+    echo "usage: ${0##*/} {start|stop|save}" >&2
+    exit 1
+  ;;
+esac
+
+# vim:set ts=2 sw=2 ft=sh et:
diff --git a/SOURCES/iptables-config b/SOURCES/iptables-config
new file mode 100644
index 0000000..3d7e176
--- /dev/null
+++ b/SOURCES/iptables-config
@@ -0,0 +1,59 @@
+# Load additional iptables modules (nat helpers)
+#   Default: -none-
+# Space separated list of nat helpers (e.g. 'ip_nat_ftp ip_nat_irc'), which
+# are loaded after the firewall rules are applied. Options for the helpers are
+# stored in /etc/modprobe.conf.
+IPTABLES_MODULES=""
+
+# Save current firewall rules on stop.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets stopped
+# (e.g. on system shutdown).
+IPTABLES_SAVE_ON_STOP="no"
+
+# Save current firewall rules on restart.
+#   Value: yes|no,  default: no
+# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets
+# restarted.
+IPTABLES_SAVE_ON_RESTART="no"
+
+# Save (and restore) rule and chain counter.
+#   Value: yes|no,  default: no
+# Save counters for rules and chains to /etc/sysconfig/iptables if
+# 'service iptables save' is called or on stop or restart if SAVE_ON_STOP or
+# SAVE_ON_RESTART is enabled.
+IPTABLES_SAVE_COUNTER="no"
+
+# Numeric status output
+#   Value: yes|no,  default: yes
+# Print IP addresses and port numbers in numeric format in the status output.
+IPTABLES_STATUS_NUMERIC="yes"
+
+# Verbose status output
+#   Value: yes|no,  default: yes
+# Print info about the number of packets and bytes plus the "input-" and
+# "outputdevice" in the status output.
+IPTABLES_STATUS_VERBOSE="no"
+
+# Status output with numbered lines
+#   Value: yes|no,  default: yes
+# Print a counter/number for every rule in the status output.
+IPTABLES_STATUS_LINENUMBERS="yes"
+
+# Reload sysctl settings on start and restart
+#   Default: -none-
+# Space separated list of sysctl items which are to be reloaded on start.
+# List items will be matched by fgrep.
+#IPTABLES_SYSCTL_LOAD_LIST=".nf_conntrack .bridge-nf"
+
+# Set wait option for iptables-restore calls in seconds
+#   Default: 600
+# Set to 0 to deactivate the wait.
+#IPTABLES_RESTORE_WAIT=600
+
+# Set wait interval option for iptables-restore calls in microseconds
+#   Default: 1000000
+# Set to 100000 to try to get the lock every 100000 microseconds, 10 times a
+# second.
+# Only usable with IPTABLES_RESTORE_WAIT > 0
+#IPTABLES_RESTORE_WAIT_INTERVAL=1000000
diff --git a/SOURCES/iptables.init b/SOURCES/iptables.init
new file mode 100755
index 0000000..6b391fd
--- /dev/null
+++ b/SOURCES/iptables.init
@@ -0,0 +1,374 @@
+#!/bin/bash
+#
+# iptables	Start iptables firewall
+#
+# chkconfig: 2345 08 92
+# description:	Starts, stops and saves iptables firewall
+#
+# config: /etc/sysconfig/iptables
+# config: /etc/sysconfig/iptables-config
+#
+### BEGIN INIT INFO
+# Provides: iptables
+# Required-Start:
+# Required-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: start and stop iptables firewall
+# Description: Start, stop and save iptables firewall
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+IPTABLES=iptables
+IPTABLES_DATA=/etc/sysconfig/$IPTABLES
+IPTABLES_FALLBACK_DATA=${IPTABLES_DATA}.fallback
+IPTABLES_CONFIG=/etc/sysconfig/${IPTABLES}-config
+IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6
+[ "$IPV" = "ip" ] && _IPV="ipv4" || _IPV="ipv6"
+VAR_SUBSYS_IPTABLES=/var/lock/subsys/$IPTABLES
+
+# only usable for root
+if [ $EUID != 0 ]; then
+    echo -n $"${IPTABLES}: Only usable by root."; warning; echo
+    exit 4
+fi
+
+if [ ! -x /sbin/$IPTABLES ]; then
+    echo -n $"${IPTABLES}: /sbin/$IPTABLES does not exist."; warning; echo
+    exit 5
+fi
+
+# Default firewall configuration:
+IPTABLES_MODULES=""
+IPTABLES_SAVE_ON_STOP="no"
+IPTABLES_SAVE_ON_RESTART="no"
+IPTABLES_SAVE_COUNTER="no"
+IPTABLES_STATUS_NUMERIC="yes"
+IPTABLES_STATUS_VERBOSE="no"
+IPTABLES_STATUS_LINENUMBERS="yes"
+IPTABLES_SYSCTL_LOAD_LIST=""
+IPTABLES_RESTORE_WAIT=600
+IPTABLES_RESTORE_WAIT_INTERVAL=1000000
+
+# Load firewall configuration.
+[ -f "$IPTABLES_CONFIG" ] && . "$IPTABLES_CONFIG"
+
+# explicitly omit security table from this list as
+# it should be reserved for SELinux use
+NF_TABLES="raw mangle filter nat"
+
+
+flush_n_delete() {
+    # Flush firewall rules and delete chains.
+    echo -n $"${IPTABLES}: Flushing firewall rules: "
+    ret=0
+    # For all tables
+    for i in $NF_TABLES; do
+        # Flush firewall rules.
+	$IPTABLES -t $i -F;
+	let ret+=$?;
+
+        # Delete firewall chains.
+	$IPTABLES -t $i -X;
+	let ret+=$?;
+
+	# Set counter to zero.
+	$IPTABLES -t $i -Z;
+	let ret+=$?;
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+set_policy() {
+    # Set policy for configured tables.
+    policy=$1
+    echo -n $"${IPTABLES}: Setting chains to policy $policy: "
+    ret=0
+    for i in $NF_TABLES; do
+	echo -n "$i "
+	case "$i" in
+	    raw)
+		$IPTABLES -t raw -P PREROUTING $policy \
+		    && $IPTABLES -t raw -P OUTPUT $policy \
+		    || let ret+=1
+		;;
+	    filter)
+                $IPTABLES -t filter -P INPUT $policy \
+		    && $IPTABLES -t filter -P OUTPUT $policy \
+		    && $IPTABLES -t filter -P FORWARD $policy \
+		    || let ret+=1
+		;;
+	    nat)
+		$IPTABLES -t nat -P PREROUTING $policy \
+		    && $IPTABLES -t nat -P POSTROUTING $policy \
+		    && $IPTABLES -t nat -P OUTPUT $policy \
+		    || let ret+=1
+		;;
+	    mangle)
+	        $IPTABLES -t mangle -P PREROUTING $policy \
+		    && $IPTABLES -t mangle -P POSTROUTING $policy \
+		    && $IPTABLES -t mangle -P INPUT $policy \
+		    && $IPTABLES -t mangle -P OUTPUT $policy \
+		    && $IPTABLES -t mangle -P FORWARD $policy \
+		    || let ret+=1
+		;;
+	    *)
+	        let ret+=1
+		;;
+        esac
+    done
+
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+load_sysctl() {
+    # load matched sysctl values
+    if [ -n "$IPTABLES_SYSCTL_LOAD_LIST" ]; then
+        echo -n $"Loading sysctl settings: "
+        ret=0
+        for item in $IPTABLES_SYSCTL_LOAD_LIST; do
+            fgrep -hs $item /etc/sysctl.d/* | sysctl -p - >/dev/null
+            let ret+=$?;
+        done
+        [ $ret -eq 0 ] && success || failure
+        echo
+    fi
+    return $ret
+}
+
+start() {
+    # Do not start if there is no config file.
+    if [ ! -f "$IPTABLES_DATA" ]; then
+	echo -n $"${IPTABLES}: No config file."; warning; echo
+	return 6
+    fi
+
+    # check if ipv6 module load is deactivated
+    if [ "${_IPV}" = "ipv6" ] \
+	&& grep -qIsE "^install[[:space:]]+${_IPV}[[:space:]]+/bin/(true|false)" /etc/modprobe.conf /etc/modprobe.d/* ; then
+	echo $"${IPTABLES}: ${_IPV} is disabled."
+	return 150
+    fi
+
+    echo -n $"${IPTABLES}: Applying firewall rules: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+    if [ $IPTABLES_RESTORE_WAIT -ne 0 ]; then
+       OPT="${OPT} --wait ${IPTABLES_RESTORE_WAIT}"
+       if [ $IPTABLES_RESTORE_WAIT_INTERVAL -lt 1000000 ]; then
+           OPT="${OPT} --wait-interval ${IPTABLES_RESTORE_WAIT_INTERVAL}"
+       fi
+    fi
+
+    $IPTABLES-restore $OPT $IPTABLES_DATA
+    if [ $? -eq 0 ]; then
+	success; echo
+    else
+	failure; echo;
+	if [ -f "$IPTABLES_FALLBACK_DATA" ]; then
+	    echo -n $"${IPTABLES}: Applying firewall fallback rules: "
+	    $IPTABLES-restore $OPT $IPTABLES_FALLBACK_DATA
+	    if [ $? -eq 0 ]; then
+		success; echo
+	    else
+		failure; echo; return 1
+	    fi
+	else
+	    return 1
+	fi
+    fi
+    
+    # Load additional modules (helpers)
+    if [ -n "$IPTABLES_MODULES" ]; then
+	echo -n $"${IPTABLES}: Loading additional modules: "
+	ret=0
+	for mod in $IPTABLES_MODULES; do
+	    echo -n "$mod "
+	    modprobe $mod > /dev/null 2>&1
+	    let ret+=$?;
+	done
+	[ $ret -eq 0 ] && success || failure
+	echo
+    fi
+    
+    # Load sysctl settings
+    load_sysctl
+
+    touch $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+stop() {
+    # Set default chain policy to ACCEPT, in order to not break shutdown
+    # on systems where the default policy is DROP and root device is
+    # network-based (i.e.: iSCSI, NFS)
+    set_policy ACCEPT
+    # And then, flush the rules and delete chains
+    flush_n_delete
+    
+    rm -f $VAR_SUBSYS_IPTABLES
+    return $ret
+}
+
+save() {
+    echo -n $"${IPTABLES}: Saving firewall rules to $IPTABLES_DATA: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+
+    ret=0
+    TMP_FILE=$(/bin/mktemp -q $IPTABLES_DATA.XXXXXX) \
+	&& chmod 600 "$TMP_FILE" \
+	&& $IPTABLES-save $OPT > $TMP_FILE 2>/dev/null \
+	&& size=$(stat -c '%s' $TMP_FILE) && [ $size -gt 0 ] \
+	|| ret=1
+    if [ $ret -eq 0 ]; then
+	if [ -e $IPTABLES_DATA ]; then
+	    cp -f $IPTABLES_DATA $IPTABLES_DATA.save \
+		&& chmod 600 $IPTABLES_DATA.save \
+		&& restorecon $IPTABLES_DATA.save \
+		|| ret=1
+	fi
+	if [ $ret -eq 0 ]; then
+	    mv -f $TMP_FILE $IPTABLES_DATA \
+		&& chmod 600 $IPTABLES_DATA \
+		&& restorecon $IPTABLES_DATA \
+	        || ret=1
+	fi
+    fi
+    rm -f $TMP_FILE
+    [ $ret -eq 0 ] && success || failure
+    echo
+    return $ret
+}
+
+status() {
+    if [ ! -f "$VAR_SUBSYS_IPTABLES" ]; then
+	echo $"${IPTABLES}: Firewall is not running."
+	return 3
+    fi
+
+    NUM=
+    [ "x$IPTABLES_STATUS_NUMERIC" = "xyes" ] && NUM="-n"
+    VERBOSE= 
+    [ "x$IPTABLES_STATUS_VERBOSE" = "xyes" ] && VERBOSE="--verbose"
+    COUNT=
+    [ "x$IPTABLES_STATUS_LINENUMBERS" = "xyes" ] && COUNT="--line-numbers"
+
+    for table in $NF_TABLES; do
+	echo $"Table: $table"
+	$IPTABLES -t $table --list $NUM $VERBOSE $COUNT && echo
+    done
+
+    return 0
+}
+
+reload() {
+    # Do not reload if there is no config file.
+    if [ ! -f "$IPTABLES_DATA" ]; then
+	echo -n $"${IPTABLES}: No config file."; warning; echo
+	return 6
+    fi
+
+    # check if ipv6 module load is deactivated
+    if [ "${_IPV}" = "ipv6" ] \
+	&& grep -qIsE "^install[[:space:]]+${_IPV}[[:space:]]+/bin/(true|false)" /etc/modprobe.conf /etc/modprobe.d/* ; then
+	echo $"${IPTABLES}: ${_IPV} is disabled."
+	return 150
+    fi
+
+    echo -n $"${IPTABLES}: Trying to reload firewall rules: "
+
+    OPT=
+    [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c"
+    if [ $IPTABLES_RESTORE_WAIT -ne 0 ]; then
+       OPT="${OPT} --wait ${IPTABLES_RESTORE_WAIT}"
+       if [ $IPTABLES_RESTORE_WAIT_INTERVAL -lt 1000000 ]; then
+           OPT="${OPT} --wait-interval ${IPTABLES_RESTORE_WAIT_INTERVAL}"
+       fi
+    fi
+
+    $IPTABLES-restore $OPT $IPTABLES_DATA
+    if [ $? -eq 0 ]; then
+	success; echo
+    else
+	failure; echo; echo "Firewall rules are not changed."; return 1
+    fi
+
+    # Load additional modules (helpers)
+    if [ -n "$IPTABLES_MODULES" ]; then
+	echo -n $"${IPTABLES}: Loading additional modules: "
+	ret=0
+	for mod in $IPTABLES_MODULES; do
+	    echo -n "$mod "
+	    modprobe $mod > /dev/null 2>&1
+	    let ret+=$?;
+	done
+	[ $ret -eq 0 ] && success || failure
+	echo
+    fi
+
+    # Load sysctl settings
+    load_sysctl
+
+    return $ret
+}
+
+restart() {
+    [ "x$IPTABLES_SAVE_ON_RESTART" = "xyes" ] && save
+    stop
+    start
+}
+
+
+case "$1" in
+    start)
+	[ -f "$VAR_SUBSYS_IPTABLES" ] && exit 0
+	start
+	RETVAL=$?
+	;;
+    stop)
+	[ "x$IPTABLES_SAVE_ON_STOP" = "xyes" ] && save
+	stop
+	RETVAL=$?
+	;;
+    restart|force-reload)
+	restart
+	RETVAL=$?
+	;;
+    reload)
+	[ -e "$VAR_SUBSYS_IPTABLES" ] && reload
+	RETVAL=$?
+	;;      
+    condrestart|try-restart)
+	[ ! -e "$VAR_SUBSYS_IPTABLES" ] && exit 0
+	restart
+	RETVAL=$?
+	;;
+    status)
+	status
+	RETVAL=$?
+	;;
+    panic)
+	set_policy DROP
+	RETVAL=$?
+        ;;
+    save)
+	save
+	RETVAL=$?
+	;;
+    *)
+	echo $"Usage: ${IPTABLES} {start|stop|reload|restart|condrestart|status|panic|save}"
+	RETVAL=2
+	;;
+esac
+
+exit $RETVAL
diff --git a/SOURCES/iptables.service b/SOURCES/iptables.service
new file mode 100644
index 0000000..6722c7a
--- /dev/null
+++ b/SOURCES/iptables.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=IPv4 firewall with iptables
+After=syslog.target
+AssertPathExists=/etc/sysconfig/iptables
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/libexec/iptables/iptables.init start
+ExecReload=/usr/libexec/iptables/iptables.init reload
+ExecStop=/usr/libexec/iptables/iptables.init stop
+Environment=BOOTUP=serial
+Environment=CONSOLETYPE=serial
+StandardOutput=syslog
+StandardError=syslog
+
+[Install]
+WantedBy=basic.target
diff --git a/SOURCES/sysconfig_ip6tables b/SOURCES/sysconfig_ip6tables
new file mode 100644
index 0000000..34b8b87
--- /dev/null
+++ b/SOURCES/sysconfig_ip6tables
@@ -0,0 +1,15 @@
+# sample configuration for ip6tables service
+# you can edit this manually or use system-config-firewall
+# please do not ask us to add additional ports/services to this default configuration
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+-A INPUT -p ipv6-icmp -j ACCEPT
+-A INPUT -i lo -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+-A INPUT -d fe80::/64 -p udp -m udp --dport 546 -m state --state NEW -j ACCEPT
+-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
+-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
+COMMIT
diff --git a/SOURCES/sysconfig_iptables b/SOURCES/sysconfig_iptables
new file mode 100644
index 0000000..5183250
--- /dev/null
+++ b/SOURCES/sysconfig_iptables
@@ -0,0 +1,14 @@
+# sample configuration for iptables service
+# you can edit this manually or use system-config-firewall
+# please do not ask us to add additional ports/services to this default configuration
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
+-A INPUT -p icmp -j ACCEPT
+-A INPUT -i lo -j ACCEPT
+-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+-A INPUT -j REJECT --reject-with icmp-host-prohibited
+-A FORWARD -j REJECT --reject-with icmp-host-prohibited
+COMMIT
diff --git a/SPECS/iptables.spec b/SPECS/iptables.spec
new file mode 100644
index 0000000..53c2264
--- /dev/null
+++ b/SPECS/iptables.spec
@@ -0,0 +1,1572 @@
+# install init scripts to /usr/libexec with systemd
+%global script_path %{_libexecdir}/iptables
+
+# service legacy actions (RHBZ#748134)
+%global legacy_actions %{_libexecdir}/initscripts/legacy-actions
+
+Name: iptables
+Summary: Tools for managing Linux kernel packet filtering capabilities
+Version: 1.8.2
+Release: 9%{?dist}
+Source: http://www.netfilter.org/projects/iptables/files/%{name}-%{version}.tar.bz2
+Source1: iptables.init
+Source2: iptables-config
+Source3: iptables.service
+Source4: sysconfig_iptables
+Source5: sysconfig_ip6tables
+Source6: arptables.service
+Source7: arptables-helper
+Source8: ebtables.systemd
+Source9: ebtables.service
+Source10: ebtables-config
+Source11: ebtables.8
+Source12: arptables.8
+Patch1: 0001-iptables-apply-Use-mktemp-instead-of-tempfile.patch
+Patch2: 0002-extensions-format-security-fixes-in-libip-6-t_icmp.patch
+Patch3: 0003-arptables-nft-use-generic-expression-parsing-functio.patch
+Patch4: 0004-xtables-Don-t-use-native-nftables-comments.patch
+Patch5: 0005-xtables-Introduce-per-table-chain-caches.patch
+Patch6: 0006-nft-add-type-field-to-builtin_table.patch
+Patch7: 0007-nft-move-chain_cache-back-to-struct-nft_handle.patch
+Patch8: 0008-nft-move-initialize-to-struct-nft_handle.patch
+Patch9: 0009-xtables-constify-struct-builtin_table-and-struct-bui.patch
+Patch10: 0010-nft-Simplify-nftnl_rule_list_chain_save.patch
+Patch11: 0011-nft-Review-unclear-return-points.patch
+Patch12: 0012-xtables-restore-Review-chain-handling.patch
+Patch13: 0013-nft-Review-is_-_compatible-routines.patch
+Patch14: 0014-nft-Reduce-__nft_rule_del-signature.patch
+Patch15: 0015-nft-Reduce-indenting-level-in-flush_chain_cache.patch
+Patch16: 0016-nft-Simplify-per-table-chain-cache-update.patch
+Patch17: 0017-nft-Simplify-nft_rule_insert-a-bit.patch
+Patch18: 0018-nft-Introduce-fetch_chain_cache.patch
+Patch19: 0019-nft-Move-nft_rule_list_get-above-nft_chain_list_get.patch
+Patch20: 0020-xtables-Implement-per-chain-rule-cache.patch
+Patch21: 0021-nft-Drop-nft_chain_list_find.patch
+Patch22: 0022-xtables-Optimize-flushing-a-specific-chain.patch
+Patch23: 0023-xtables-Optimize-nft_chain_zero_counters.patch
+Patch24: 0024-tests-Extend-verbose-output-and-return-code-tests.patch
+Patch25: 0025-xtables-Optimize-user-defined-chain-deletion.patch
+Patch26: 0026-xtables-Optimize-list-command-with-given-chain.patch
+Patch27: 0027-xtables-Optimize-list-rules-command-with-given-chain.patch
+Patch28: 0028-nft-Make-use-of-nftnl_rule_lookup_byindex.patch
+Patch29: 0029-nft-Simplify-nft_is_chain_compatible.patch
+Patch30: 0030-nft-Simplify-flush_chain_cache.patch
+Patch31: 0031-xtables-Set-errno-in-nft_rule_check-if-chain-not-fou.patch
+Patch32: 0032-xtables-Speed-up-chain-deletion-in-large-rulesets.patch
+Patch33: 0033-arptables-nft-Fix-listing-rules-without-target.patch
+Patch34: 0034-arptables-nft-Fix-MARK-target-parsing-and-printing.patch
+Patch35: 0035-arptables-nft-Fix-CLASSIFY-target-printing.patch
+Patch36: 0036-arptables-nft-Remove-space-between-cnt-and-value.patch
+Patch37: 0037-arptables-nft-save-Fix-position-of-j-option.patch
+Patch38: 0038-arptables-nft-Don-t-print-default-h-len-h-type-value.patch
+Patch39: 0039-tests-shell-Add-arptables-nft-verbose-output-test.patch
+Patch40: 0040-arptables-nft-Set-h-type-h-length-masks-by-default-t.patch
+Patch41: 0041-nft-Add-new-builtin-chains-to-cache-immediately.patch
+Patch42: 0042-xtables-Fix-position-of-replaced-rules-in-cache.patch
+Patch43: 0043-xtables-Fix-for-inserting-rule-at-wrong-position.patch
+Patch44: 0044-xtables-Fix-for-crash-when-comparing-rules-with-stan.patch
+Patch45: 0045-xtables-Fix-for-false-positive-rule-matching.patch
+Patch46: 0046-ebtables-Fix-rule-listing-with-counters.patch
+Patch47: 0047-Revert-ebtables-use-extrapositioned-negation-consist.patch
+Patch48: 0048-arptables-Support-set-counters-option.patch
+Patch49: 0049-xshared-Explicitly-pass-target-to-command_jump.patch
+Patch50: 0050-nft-Don-t-assume-NFTNL_RULE_USERDATA-holds-a-comment.patch
+Patch51: 0051-nft-Introduce-UDATA_TYPE_EBTABLES_POLICY.patch
+Patch52: 0052-ebtables-nft-Support-user-defined-chain-policies.patch
+URL: http://www.netfilter.org/
+# pf.os: ISC license
+# iptables-apply: Artistic Licence 2.0
+License: GPLv2 and Artistic 2.0 and ISC
+
+# libnetfilter_conntrack is needed for xt_connlabel
+BuildRequires: pkgconfig(libnetfilter_conntrack)
+# libnfnetlink-devel is requires for nfnl_osf
+BuildRequires: pkgconfig(libnfnetlink)
+BuildRequires: libselinux-devel
+BuildRequires: kernel-headers
+BuildRequires: systemd
+# libmnl, libnftnl, bison, flex for nftables
+BuildRequires: bison
+BuildRequires: flex
+BuildRequires: gcc
+BuildRequires: pkgconfig(libmnl) >= 1.0
+BuildRequires: pkgconfig(libnftnl) >= 1.1.1-4
+# libpcap-devel for nfbpf_compile
+BuildRequires: libpcap-devel
+BuildRequires:  autoconf
+BuildRequires:  automake
+BuildRequires:  libtool
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+%if 0%{?fedora} > 24
+Conflicts: setup < 2.10.4-1
+%endif
+
+%description
+The iptables utility controls the network packet filtering code in the
+Linux kernel. If you need to set up firewalls and/or IP masquerading,
+you should either install nftables or this package.
+
+Note: This package contains the nftables-based variants of iptables and
+ip6tables, which are drop-in replacements of the legacy tools.
+
+%package libs
+Summary: iptables libraries
+Group: System Environment/Base
+
+%description libs
+iptables libraries.
+
+Please remember that libip*tc libraries do neither have a stable API nor a real so version.
+
+For more information about this, please have a look at
+
+  http://www.netfilter.org/documentation/FAQ/netfilter-faq-4.html#ss4.5
+
+
+%package devel
+Summary: Development package for iptables
+Group: System Environment/Base
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: iptables-libs = %{version}-%{release}
+Requires: pkgconfig
+
+%description devel
+iptables development headers and libraries.
+
+The iptc libraries are marked as not public by upstream. The interface is not
+stable and may change with every new version. It is therefore unsupported.
+
+%package services
+Summary: iptables and ip6tables services for iptables
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+# obsolete old main package
+Obsoletes: %{name} < 1.4.16.1
+# obsolete ipv6 sub package
+Obsoletes: %{name}-ipv6 < 1.4.11.1
+
+%description services
+iptables services for IPv4 and IPv6
+
+This package provides the services iptables and ip6tables that have been split
+out of the base package since they are not active by default anymore.
+
+%package utils
+Summary: iptables and ip6tables services for iptables
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+
+%description utils
+Utils for iptables.
+
+Currently only provides nfnl_osf with the pf.os database.
+
+%package arptables
+Summary: User space tool to set up tables of ARP rules in kernel
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+Obsoletes: arptables
+Provides: arptables
+
+%description arptables
+The arptables tool is used to set up and maintain
+the tables of ARP rules in the Linux kernel. These rules inspect
+the ARP frames which they see. arptables is analogous to the iptables
+user space tool, but is less complicated.
+
+Note: This package contains the nftables-based variant of arptables, a drop-in
+replacement of the legacy tool.
+
+%package ebtables
+Summary: Ethernet Bridge frame table administration tool
+Group: System Environment/Base
+Requires: %{name} = %{version}-%{release}
+Obsoletes: ebtables
+Provides: ebtables
+
+%description ebtables
+Ethernet bridge tables is a firewalling tool to transparently filter network
+traffic passing a bridge. The filtering possibilities are limited to link
+layer filtering and some basic filtering on higher network layers.
+
+This tool is the userspace control for the bridge and ebtables kernel
+components (built by default in RHEL kernels).
+
+The ebtables tool can be used together with the other Linux filtering tools,
+like iptables. There are no known incompatibility issues.
+
+Note: This package contains the nftables-based variant of ebtables, a drop-in
+replacement of the legacy tool.
+
+%prep
+%autosetup -p1
+
+%build
+./autogen.sh
+CFLAGS="$RPM_OPT_FLAGS -fno-strict-aliasing " \
+%configure --enable-devel --enable-bpf-compiler --with-kernel=/usr --with-kbuild=/usr --with-ksource=/usr
+
+# do not use rpath
+sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool
+sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool
+
+rm -f include/linux/types.h
+
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR=%{buildroot} 
+# remove la file(s)
+rm -f %{buildroot}/%{_libdir}/*.la
+
+# install ip*tables.h header files
+install -m 644 include/ip*tables.h %{buildroot}%{_includedir}/
+install -d -m 755 %{buildroot}%{_includedir}/iptables
+install -m 644 include/iptables/internal.h %{buildroot}%{_includedir}/iptables/
+
+# install ipulog header file
+install -d -m 755 %{buildroot}%{_includedir}/libipulog/
+install -m 644 include/libipulog/*.h %{buildroot}%{_includedir}/libipulog/
+
+# install init scripts and configuration files
+install -d -m 755 %{buildroot}%{script_path}
+install -c -m 755 %{SOURCE1} %{buildroot}%{script_path}/iptables.init
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE1} > ip6tables.init
+install -c -m 755 ip6tables.init %{buildroot}%{script_path}/ip6tables.init
+install -d -m 755 %{buildroot}%{_sysconfdir}/sysconfig
+install -c -m 600 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/iptables-config
+sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE2} > ip6tables-config
+install -c -m 600 ip6tables-config %{buildroot}%{_sysconfdir}/sysconfig/ip6tables-config
+install -c -m 600 %{SOURCE4} %{buildroot}%{_sysconfdir}/sysconfig/iptables
+install -c -m 600 %{SOURCE5} %{buildroot}%{_sysconfdir}/sysconfig/ip6tables
+
+# install systemd service files
+install -d -m 755 %{buildroot}/%{_unitdir}
+install -c -m 644 %{SOURCE3} %{buildroot}/%{_unitdir}
+sed -e 's;iptables;ip6tables;g' -e 's;IPv4;IPv6;g' -e 's;/usr/libexec/ip6tables;/usr/libexec/iptables;g' < %{SOURCE3} > ip6tables.service
+install -c -m 644 ip6tables.service %{buildroot}/%{_unitdir}
+
+# install legacy actions for service command
+install -d %{buildroot}/%{legacy_actions}/iptables
+install -d %{buildroot}/%{legacy_actions}/ip6tables
+
+cat << EOF > %{buildroot}/%{legacy_actions}/iptables/save
+#!/bin/bash
+exec %{script_path}/iptables.init save
+EOF
+chmod 755 %{buildroot}/%{legacy_actions}/iptables/save
+sed -e 's;iptables.init;ip6tables.init;g' -e 's;IPTABLES;IP6TABLES;g' < %{buildroot}/%{legacy_actions}/iptables/save > ip6tabes.save-legacy
+install -c -m 755 ip6tabes.save-legacy %{buildroot}/%{legacy_actions}/ip6tables/save
+
+cat << EOF > %{buildroot}/%{legacy_actions}/iptables/panic
+#!/bin/bash
+exec %{script_path}/iptables.init panic
+EOF
+chmod 755 %{buildroot}/%{legacy_actions}/iptables/panic
+sed -e 's;iptables.init;ip6tables.init;g' -e 's;IPTABLES;IP6TABLES;g' < %{buildroot}/%{legacy_actions}/iptables/panic > ip6tabes.panic-legacy
+install -c -m 755 ip6tabes.panic-legacy %{buildroot}/%{legacy_actions}/ip6tables/panic
+
+# install iptables-apply with man page
+install -m 755 iptables/iptables-apply %{buildroot}%{_sbindir}/
+install -m 644 iptables/iptables-apply.8 %{buildroot}%{_mandir}/man8/
+
+%if 0%{?fedora} > 24
+# Remove /etc/ethertypes (now part of setup)
+rm -f %{buildroot}%{_sysconfdir}/ethertypes
+%endif
+
+# drop all legacy tools
+rm -f %{buildroot}%{_sbindir}/*legacy*
+rm -f %{buildroot}%{_bindir}/iptables-xml
+rm -f %{buildroot}%{_mandir}/man1/iptables-xml*
+rm -f %{buildroot}%{_mandir}/man8/xtables-legacy*
+
+# rename nft versions to standard name
+pfx=%{buildroot}%{_sbindir}/iptables
+for pfx in %{buildroot}%{_sbindir}/{iptables,ip6tables,arptables,ebtables}; do
+	mv $pfx-nft $pfx
+	mv $pfx-nft-restore $pfx-restore
+	mv $pfx-nft-save $pfx-save
+done
+
+# extra sources for arptables
+install -p -D -m 644 %{SOURCE6} %{buildroot}%{_unitdir}/arptables.service
+mkdir -p %{buildroot}%{_libexecdir}/
+install -p -D -m 755 %{SOURCE7} %{buildroot}%{_libexecdir}/
+mkdir -p %{buildroot}%{_sysconfdir}/sysconfig
+echo '# Configure prior to use' > %{buildroot}%{_sysconfdir}/sysconfig/arptables
+install -m0644 %{SOURCE12} %{buildroot}%{_mandir}/man8/arptables.8
+
+# extra sources for ebtables
+install -p %{SOURCE9} %{buildroot}%{_unitdir}/
+install -m0755 %{SOURCE8} %{buildroot}%{_libexecdir}/ebtables
+install -m0600 %{SOURCE10} %{buildroot}%{_sysconfdir}/sysconfig/ebtables-config
+touch %{buildroot}%{_sysconfdir}/sysconfig/ebtables
+install -m0644 %{SOURCE11} %{buildroot}%{_mandir}/man8/ebtables.8
+
+
+%if 0%{?rhel}
+%pre
+for p in %{_sysconfdir}/alternatives/{iptables,ip6tables}.*; do
+    if [ -h "$p" ]; then
+        ipt=$(readlink "$p")
+        echo "Removing alternatives for ${p##*/} with path $ipt"
+        %{_sbindir}/alternatives --remove "${p##*/}" "$ipt"
+    fi
+done
+%endif
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%post services
+%systemd_post iptables.service ip6tables.service
+
+%preun services
+%systemd_preun iptables.service ip6tables.service
+
+%postun services
+/sbin/ldconfig
+%systemd_postun iptables.service ip6tables.service
+
+%post arptables
+%systemd_post arptables.service
+
+%preun arptables
+%systemd_preun arptables.service
+
+%postun arptables
+%systemd_postun arptables.service
+
+%post ebtables
+%systemd_post ebtables.service
+
+%preun ebtables
+%systemd_preun ebtables.service
+
+%postun ebtables
+%systemd_postun_with_restart ebtables.service
+
+%files
+%{!?_licensedir:%global license %%doc}
+%license COPYING
+%doc INCOMPATIBILITIES
+%config(noreplace) %{_sysconfdir}/sysconfig/iptables-config
+%config(noreplace) %{_sysconfdir}/sysconfig/ip6tables-config
+%if 0%{?fedora} <= 24
+%{_sysconfdir}/ethertypes
+%endif
+%{_sbindir}/iptables
+%{_sbindir}/iptables-apply
+%{_sbindir}/iptables-restore
+%{_sbindir}/iptables-restore-translate
+%{_sbindir}/iptables-save
+%{_sbindir}/iptables-translate
+%{_sbindir}/ip6tables
+%{_sbindir}/ip6tables-restore
+%{_sbindir}/ip6tables-restore-translate
+%{_sbindir}/ip6tables-save
+%{_sbindir}/ip6tables-translate
+%{_sbindir}/xtables-monitor
+%{_sbindir}/xtables-nft-multi
+%{_mandir}/man8/iptables*
+%{_mandir}/man8/ip6tables*
+%{_mandir}/man8/nfnl_osf*
+%{_mandir}/man8/xtables-monitor*
+%{_mandir}/man8/xtables-nft*
+%{_mandir}/man8/xtables-translate*
+%dir %{_libdir}/xtables
+%{_libdir}/xtables/libarpt*
+%{_libdir}/xtables/libebt*
+%{_libdir}/xtables/libipt*
+%{_libdir}/xtables/libip6t*
+%{_libdir}/xtables/libxt*
+
+%files libs
+%{_libdir}/libip*tc.so.*
+%{_libdir}/libxtables.so.*
+
+%files devel
+%dir %{_includedir}/iptables
+%{_includedir}/iptables/*.h
+%{_includedir}/*.h
+%dir %{_includedir}/libiptc
+%{_includedir}/libiptc/*.h
+%dir %{_includedir}/libipulog
+%{_includedir}/libipulog/*.h
+%{_libdir}/libip*tc.so
+%{_libdir}/libxtables.so
+%{_libdir}/pkgconfig/libiptc.pc
+%{_libdir}/pkgconfig/libip4tc.pc
+%{_libdir}/pkgconfig/libip6tc.pc
+%{_libdir}/pkgconfig/xtables.pc
+
+%files services
+%dir %{script_path}
+%{script_path}/iptables.init
+%{script_path}/ip6tables.init
+%config(noreplace) %{_sysconfdir}/sysconfig/iptables
+%config(noreplace) %{_sysconfdir}/sysconfig/ip6tables
+%{_unitdir}/iptables.service
+%{_unitdir}/ip6tables.service
+%dir %{legacy_actions}/iptables
+%{legacy_actions}/iptables/save
+%{legacy_actions}/iptables/panic
+%dir %{legacy_actions}/ip6tables
+%{legacy_actions}/ip6tables/save
+%{legacy_actions}/ip6tables/panic
+
+%files utils
+%{_sbindir}/nfnl_osf
+%{_sbindir}/nfbpf_compile
+%dir %{_datadir}/xtables
+%{_datadir}/xtables/pf.os
+
+%files arptables
+%{_sbindir}/arptables*
+%{_libexecdir}/arptables-helper
+%{_unitdir}/arptables.service
+%config(noreplace) %{_sysconfdir}/sysconfig/arptables
+%doc %{_mandir}/man8/arptables.8*
+
+%files ebtables
+%{_sbindir}/ebtables*
+%{_libexecdir}/ebtables
+%{_unitdir}/ebtables.service
+%config(noreplace) %{_sysconfdir}/sysconfig/ebtables-config
+%ghost %{_sysconfdir}/sysconfig/ebtables
+%doc %{_mandir}/man8/ebtables.8*
+
+%changelog
+* Fri Feb 08 2019 Phil Sutter - 1.8.2-9
+- ebtables-nft: Support user-defined chain policies
+
+* Thu Feb 07 2019 Phil Sutter - 1.8.2-8
+- arptables.8: Document --set-counters option
+
+* Thu Feb 07 2019 Phil Sutter - 1.8.2-7
+- arptables: Support --set-counters option
+
+* Fri Feb 01 2019 Phil Sutter - 1.8.2-6
+- Improve performance with large rulesets
+- Fix for changes in arptables output
+- Fix for inserting rules at wrong position
+- Fix segfault when comparing rules with standard target
+- Fix ebtables output for negated values
+- Document missing arptables FORWARD chain
+
+* Tue Dec 18 2018 Phil Sutter - 1.8.2-5
+- Drop change to test snippet not included in tarball from Patch4
+
+* Tue Dec 18 2018 Phil Sutter - 1.8.2-4
+- Fix iptables init script for nftables-backend
+- Drop references to unsupported broute table from ebtables man page
+- xtables: Don't use native nftables comments
+
+* Thu Dec 06 2018 Phil Sutter - 1.8.2-3
+- Drop change to test snippet not included in tarball from Patch3
+
+* Thu Dec 06 2018 Phil Sutter - 1.8.2-2
+- Point out that nftables-variants are installed in package description
+- Fix for deleting arptables rules by referencing them
+
+* Thu Dec 06 2018 Phil Sutter - 1.8.2-1
+- Rebase onto upstream version 1.8.2
+
+* Thu Oct 25 2018 Phil Sutter - 1.8.1-2
+- Add upstream fixes to 1.8.1 release
+
+* Thu Oct 25 2018 Phil Sutter - 1.8.1-1
+- Rebase onto upstream version 1.8.1
+
+* Thu Sep 27 2018 Phil Sutter - 1.8.0-11
+- Fix for covscan warnings in init scripts
+
+* Wed Sep 26 2018 Phil Sutter - 1.8.0-10
+- Fix short name of Artistic Licence
+
+* Wed Sep 26 2018 Phil Sutter - 1.8.0-9
+- Add further fixes for issues identified by covscan
+- Fix for bogus "is incompatible" warnings
+- Fix layout in License tag
+- Replace "Fedora" with "RHEL" in description
+- Make devel sub-package depend on libs sub-package
+
+* Mon Sep 17 2018 Phil Sutter - 1.8.0-8
+- Fix issues identified by covscan
+- xtables-restore: Fix flushing referenced custom chains
+- xtables: Accept --wait in iptables-nft-restore
+
+* Mon Sep 03 2018 Phil Sutter - 1.8.0-7
+- xtables: Align return codes with legacy iptables
+- xtables: Drop use of IP6T_F_PROTO
+
+* Wed Aug 29 2018 Phil Sutter - 1.8.0-6
+- xtables: Fix for deleting rules with comment
+
+* Fri Aug 24 2018 Phil Sutter - 1.8.0-5
+- xtables: Use meta l4proto for -p match
+- ebtables: Fix for listing of non-existent chains
+- xtables: Fix for no output in iptables-nft -S
+
+* Sat Aug 18 2018 Phil Sutter - 1.8.0-4
+- xtables: Fix for segfault in iptables-nft
+- ebtables: Fix entries count in chain listing
+- Use %%autosetup macro in %%prep
+
+* Fri Aug 17 2018 Phil Sutter - 1.8.0-3
+- xtables: Make 'iptables -S nonexisting' return non-zero
+
+* Fri Aug 10 2018 Phil Sutter - 1.8.0-2
+- Rebase onto upstream master commit 514de4801b731db4712
+- Add arptables and ebtables sub-packages
+
+* Wed Jul 11 2018 Phil Sutter - 1.8.0-1
+- New upstream version 1.8.0
+- Drop compat sub-package
+- Use nft tool versions, drop legacy ones
+
+* Thu Mar 01 2018 Phil Sutter <psutter@redhat.com> - 1.6.2-2
+- Kill module unloading support
+- Support /etc/sysctl.d
+- Don't restart services after package update
+- Add support for --wait options to restore commands
+
+* Wed Feb 21 2018 Michael Cronenworth <mike@cchtml.com> - 1.6.2-1
+- New upstream version 1.6.2
+  http://www.netfilter.org/projects/iptables/files/changes-iptables-1.6.2.txt
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.6.1-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Sun Oct 22 2017 Kevin Fenzi <kevin@scrye.com> - 1.6.1-5
+- Rebuild for new libnftnl
+
+* Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.6.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.6.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.6.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Thu Feb 02 2017 Thomas Woerner <twoerner@redhat.com> - 1.6.1-1
+- New upstream version 1.6.1 with enhanced translation to nft support and
+  several fixes (RHBZ#1417323)
+  http://netfilter.org/projects/iptables/files/changes-iptables-1.6.1.txt
+- Enable parallel build again
+
+* Thu Feb 02 2017 Petr Šabata <contyk@redhat.com> - 1.6.0-4
+- Disabling parallel build to avoid build issues with xtables
+- See http://patchwork.alpinelinux.org/patch/1787/ for reference
+- This should be fixed in 1.6.1; parallel build can be restored after the
+  update
+
+* Mon Dec 19 2016 Thomas Woerner <twoerner@redhat.com> - 1.6.0-3
+- Dropped bad provides for iptables in services sub package (RHBZ#1327786)
+
+* Fri Jul 22 2016 Thomas Woerner <twoerner@redhat.com> - 1.6.0-2
+- /etc/ethertypes has been moved into the setup package for F-25+.
+  (RHBZ#1329256)
+
+* Wed Apr 13 2016 Thomas Woerner <twoerner@redhat.com> - 1.6.0-1
+- New upstream version 1.6.0 with nft-compat support and lots of fixes (RHBZ#1292990)
+  Upstream changelog:
+  http://netfilter.org/projects/iptables/files/changes-iptables-1.6.0.txt
+- New libs sub package containing libxtables and unstable libip*tc libraries (RHBZ#1323161)
+- Using scripts form RHEL-7 (RHBZ#1240366)
+- New compat sub package for nftables compatibility
+- Install iptables-apply (RHBZ#912047)
+- Fixed module uninstall (RHBZ#1324101)
+- Incorporated changes by Petr Pisar
+- Enabled bpf compiler (RHBZ#1170227) Thanks to Yanko Kaneti for the patch
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.4.21-16
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.21-15
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Mon Dec 01 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-14
+- add dhcpv6-client to /etc/sysconfig/ip6tables (RHBZ#1169036)
+
+* Mon Nov 03 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-13
+- iptables.init: use /run/lock/subsys/ instead of /var/lock/subsys/ (RHBZ#1159573)
+
+* Mon Sep 29 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-12
+- ip[6]tables.init: change shebang from /bin/sh to /bin/bash (RHBZ#1147272)
+
+* Sat Aug 16 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.21-11
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Sat Jul 12 2014 Tom Callaway <spot@fedoraproject.org> - 1.4.21-10
+- fix license handling
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.21-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Wed Mar 12 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-8
+- add missing reload and panic actions
+- BuildRequires: pkgconfig(x) instead of x-devel
+- no need to specify file mode bits twice (in %%install and %%files)
+
+* Sun Jan 19 2014 Ville Skyttä <ville.skytta@iki.fi> - 1.4.21-7
+- Don't order services after syslog.target.
+
+* Wed Jan 15 2014 Thomas Woerner <twoerner@redhat.com> 1.4.21-6
+- Enable connlabel support again, needs libnetfilter_conntrack
+
+* Wed Jan 15 2014 Thomas Woerner <twoerner@redhat.com> 1.4.21-6
+- fixed update from RHEL-6 to RHEL-7 (RHBZ#1043901)
+
+* Tue Jan 14 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-5
+- chmod /etc/sysconfig/ip[6]tables 755 -> 600
+
+* Fri Jan 10 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-4
+- drop virtual provide for xtables.so.9
+- add default /etc/sysconfig/ip[6]tables (RHBZ#1034494)
+
+* Thu Jan 09 2014 Jiri Popelka <jpopelka@redhat.com> - 1.4.21-3
+- no need to support the pre-systemd things
+- use systemd macros (#850166)
+- remove scriptlets for migrating to a systemd unit from a SysV initscripts
+- ./configure -> %%configure
+- spec clean up
+- fix self-obsoletion
+
+* Thu Jan  9 2014 Thomas Woerner <twoerner@redhat.com> 1.4.21-2
+- fixed system hang at shutdown if root device is network based (RHBZ#1007934)
+  Thanks to Rodrigo A B Freire for the patch
+
+* Thu Jan  9 2014 Thomas Woerner <twoerner@redhat.com> 1.4.21-1
+- no connlabel.conf upstream anymore
+- new version 1.4.21
+  - doc: clarify DEBUG usage macro
+  - iptables: use autoconf to process .in man pages
+  - extensions: libipt_ULOG: man page should mention NFLOG as replacement
+  - extensions: libxt_connlabel: use libnetfilter_conntrack
+  - Introduce a new revision for the set match with the counters support
+  - libxt_CT: Add the "NOTRACK" alias
+  - libip6t_mh: Correct command to list named mh types in manpage
+  - extensions: libxt_DNAT, libxt_REDIRECT, libxt_NETMAP, libxt_SNAT, libxt_MASQUERADE, libxt_LOG: rename IPv4 manpage and tell about IPv6 support
+  - extensions: libxt_LED: fix parsing of delay
+  - ip{6}tables-restore: fix breakage due to new locking approach
+  - libxt_recent: restore minimum value for --seconds
+  - iptables-xml: fix parameter parsing (similar to 2165f38)
+  - extensions: add copyright statements
+  - xtables: improve get_modprobe handling
+  - ip[6]tables: Add locking to prevent concurrent instances
+  - iptables: Fix connlabel.conf install location
+  - ip6tables: don't print out /128
+  - libip6t_LOG: target output is different to libipt_LOG
+  - build: additional include path required after UAPI changes
+  - iptables: iptables-xml: Fix various parsing bugs
+  - libxt_recent: restore reap functionality to recent module
+  - build: fail in configure on missing dependency with --enable-bpf-compiler
+  - extensions: libxt_NFQUEUE: add --queue-cpu-fanout parameter
+  - extensions: libxt_set, libxt_SET: check the set family too
+  - ip6tables: Use consistent exit code for EAGAIN
+  - iptables: libxt_hashlimit.man: correct address
+  - iptables: libxt_conntrack.man extraneous commas
+  - iptables: libip(6)t_REJECT.man default icmp types
+  - iptables: iptables-xm1.1 correct man section
+  - iptables: libxt_recent.{c,man} dead URL
+  - iptables: libxt_string.man add examples
+  - extensions: libxt_LOG: use generic syslog reference in manpage
+  - iptables: extensions/GNUMakefile.in use CPPFLAGS
+  - iptables: correctly reference generated file
+  - ip[6]tables: fix incorrect alignment in commands_v_options
+  - build: add software version to manpage first line at configure stage
+  - extensions: libxt_cluster: add note on arptables-jf
+  - utils: nfsynproxy: fix error while compiling the BPF filter
+  - extensions: add SYNPROXY extension
+  - utils: add nfsynproxy tool
+  - iptables: state match incompatibilty across versions
+  - libxtables: xtables_ipmask_to_numeric incorrect with non-CIDR masks
+  - iptables: improve chain name validation
+  - iptables: spurious error in load_extension
+  - xtables: trivial spelling fix
+
+* Sun Dec 22 2013 Ville Skyttä <ville.skytta@iki.fi> - 1.4.19.1-2
+- Drop INSTALL from docs, escape macros in %%changelog.
+
+* Wed Jul 31 2013 Thomas Woerner <twoerner@redhat.com> 1.4.19.1-1
+- new version 1.4.19.1
+  - libxt_NFQUEUE: fix bypass option documentation
+  - extensions: add connlabel match
+  - extensions: add connlabel match
+  - ip[6]tables: show --protocol instead of --proto in usage
+  - libxt_recent: Fix missing space in manpage for --mask option
+  - extensions: libxt_multiport: Update manpage to list valid protocols
+  - utils: nfnl_osf: use the right nfnetlink lib
+  - libip6t_NETMAP: Use xtables_ip6mask_to_cidr and get rid of libip6tc dependency
+  - Revert "build: resolve link failure for ip6t_NETMAP"
+  - libxt_osf: fix missing --ttl and --log in save output
+  - libxt_osf: fix bad location for location in --genre
+  - libip6t_SNPT: add manpage
+  - libip6t_DNPT: add manpage
+  - utils: updates .gitignore to include nfbpf_compile
+  - extensions: libxt_bpf: clarify --bytecode argument
+  - libxtables: fix parsing of dotted network mask format
+  - build: bump version to 1.4.19
+  - libxt_conntrack: fix state match alias state parsing
+  - extensions: add libxt_bpf extension
+  - utils: nfbpf_compile
+  - doc: mention SNAT in INPUT chain since kernel 2.6.36
+- fixed changelog date weekdays where needed
+
+* Mon Mar  4 2013 Thomas Woerner <twoerner@redhat.com> 1.4.18-1
+- new version 1.4.18 
+  - lots of documentation changes
+  - Introduce match/target aliases
+  - Add the "state" alias to the "conntrack" match
+  - iptables: remove unused leftover definitions
+  - libxtables: add xtables_rule_matches_free
+  - libxtables: add xtables_print_num
+  - extensions: libip6t_DNPT: fix wording in DNPT target
+  - extension: libip6t_DNAT: allow port DNAT without address
+  - extensions: libip6t_DNAT: set IPv6 DNAT --to-destination
+  - extensions: S/DNPT: add missing save function
+- changes of 1.4.17:
+  - libxt_time: add support to ignore day transition
+  - Convert the NAT targets to use the kernel supplied nf_nat.h header
+  - extensions: add IPv6 MASQUERADE extension
+  - extensions: add IPv6 SNAT extension
+  - extensions: add IPv6 DNAT target
+  - extensions: add IPv6 REDIRECT extension
+  - extensions: add IPv6 NETMAP extension
+  - extensions: add NPT extension
+  - extensions: libxt_statistic: Fix save output
+
+* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.16.2-7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Wed Jan 16 2013 Ville Skyttä <ville.skytta@iki.fi> - 1.4.16.2-6
+- Own unowned -services libexec dirs (#894464, Michael Scherer).
+- Fix -services unit file permissions (#732936, Michal Schmidt).
+
+* Thu Nov  8 2012 Thomas Woerner <twoerner@redhat.com> 1.4.16.2-5
+- fixed path of ip6tables.init in ip6tables.service
+
+* Fri Nov  2 2012 Thomas Woerner <twoerner@redhat.com> 1.4.16.2-4
+- fixed missing services for update of pre F-18 installations (rhbz#867960)
+  - provide and obsolete old main package in services sub package
+  - provide and obsolete old ipv6 sub package (pre F-17) in services sub package
+
+* Sun Oct 14 2012 Dan Horák <dan[at]dany.cz> 1.4.16.2-3
+- fix the compat provides for all 64-bit arches
+
+* Fri Oct 12 2012 Thomas Woerner <twoerner@redhat.com> 1.4.16.2-2
+- new sub package services providing the systemd services (RHBZ#862922)
+- new sub package utils: provides nfnl_osf and the pf.os database
+- using %%{_libexecdir}/iptables as script path for the original init scripts
+- added service iptables save funcitonality using the new way provided by 
+  initscripts 9.37.1 (RHBZ#748134)
+- added virtual provide for libxtables.so.7
+
+* Mon Oct  8 2012 Thomas Woerner <twoerner@redhat.com> 1.4.16.2-1
+- new version 1.4.16.2
+  - build: support for automake-1.12
+  - build: separate AC variable replacements from xtables.h
+  - build: have `make clean` remove dep files too
+  - doc: grammatical updates to libxt_SET
+  - doc: clean up interpunction in state list for xt_conntrack
+  - doc: deduplicate extension descriptions into a new manpage
+  - doc: trim "state" manpage and reference conntrack instead
+  - doc: have NOTRACK manpage point to CT instead
+  - doc: mention iptables-apply in the SEE ALSO sections
+  - extensions: libxt_addrtype: fix type in help message
+  - include: add missing linux/netfilter_ipv4/ip_queue.h
+  - iptables: fix wrong error messages
+  - iptables: support for match aliases
+  - iptables: support for target aliases
+  - iptables-restore: warn about -t in rule lines
+  - ip[6]tables-restore: cleanup to reduce one level of indentation
+  - libip6t_frag: match any frag id by default
+  - libxtables: consolidate preference logic
+  - libxt_devgroup: consolidate devgroup specification parsing
+  - libxt_devgroup: guard against negative numbers
+  - libxt_LED: guard against negative numbers
+  - libxt_NOTRACK: replace as an alias to CT --notrack
+  - libxt_state: replace as an alias to xt_conntrack
+  - libxt_tcp: print space before, not after "flags:"
+  - libxt_u32: do bounds checking for @'s operands
+  - libxt_*limit: avoid division by zero
+  - Merge branch 'master' of git://git.inai.de/iptables
+  - Merge remote-tracking branch 'nf/stable'
+  - New set match revision with --return-nomatch flag support
+- dropped fixrestore patch, upstream
+
+* Wed Aug  1 2012 Thomas Woerner <twoerner@redhat.com> 1.4.15-1
+- new version 1.4.15
+  - extensions: add HMARK target
+  - iptables-restore: fix parameter parsing (shows up with gcc-4.7)
+  - iptables-restore: move code to add_param_to_argv, cleanup (fix gcc-4.7)
+  - libxtables: add xtables_ip[6]mask_to_cidr
+  - libxt_devgroup: add man page snippet
+  - libxt_hashlimit: add support for byte-based operation
+  - libxt_recent: add --mask netmask
+  - libxt_recent: remove unused variable
+  - libxt_HMARK: correct a number of errors introduced by Pablo's rework
+  - libxt_HMARK: fix ct case example
+  - libxt_HMARK: fix output of iptables -L
+  - Revert "iptables-restore: move code to add_param_to_argv, cleanup (fix gcc-4.7)"
+
+* Wed Jul 18 2012 Thomas Woerner <twoerner@redhat.com> 1.4.14-3
+- added fixrestore patch submitted to upstream by fryasu (nfbz#774) 
+  (RHBZ#825796)
+
+* Wed Jul 18 2012 Thomas Woerner <twoerner@redhat.com> 1.4.14-2
+- disabled libipq, removed upstream, not provided by kernel anymore
+
+* Wed Jul 18 2012 Thomas Woerner <twoerner@redhat.com> 1.4.14-1
+- new version 1.4.14
+  - extensions: add IPv6 capable ECN match extension
+  - extensions: add nfacct match
+  - extensions: add rpfilter module
+  - extensions: libxt_rateest: output all options in save hook
+  - iptables: missing free() in function cache_add_entry()
+  - iptables: missing free() in function delete_entry()
+  - libiptc: fix retry path in TC_INIT
+  - libiptc: Returns the position the entry was inserted
+  - libipt_ULOG: fix --ulog-cprange
+  - libxt_CT: add --timeout option
+  - ip(6)tables-restore: make sure argv is NULL terminated
+  - Revert "libiptc: Returns the position the entry was inserted"
+  - src: mark newly opened fds as FD_CLOEXEC (close on exec)
+  - tests: add rateest match rules
+- dropped patch5 (cloexec), merged upstream
+
+* Mon Apr 23 2012 Thomas Woerner <twoerner@redhat.com> 1.4.12.2-5
+- reenable iptables default services
+
+* Wed Feb 29 2012 Harald Hoyer <harald@redhat.com> 1.4.12.2-4
+- install everything in /usr
+  https://fedoraproject.org/wiki/Features/UsrMove
+
+* Thu Feb 16 2012 Thomas Woerner <twoerner@redhat.com> 1.4.12.2-3
+- fixed auto enable check for Fedora > 16 and added rhel > 6 check
+
+* Wed Feb 15 2012 Thomas Woerner <twoerner@redhat.com> 1.4.12.2-2
+- disabled autostart and auto enable for iptables.service and ip6tables.service
+  for Fedora > 16
+
+* Mon Jan 16 2012 Thomas Woerner <twoerner@redhat.com> 1.4.12.2-1
+- new version 1.4.12.2 with new pkgconfig/libip4tc.pc and pkgconfig/libip6tc.pc
+  - build: make check stage not fail when building statically
+  - build: restore build order of modules
+  - build: scan for unreferenced symbols
+  - build: sort file list before build
+  - doc: clarification on the meaning of -p 0
+  - doc: document iptables-restore's -T option
+  - doc: fix undesired newline in ip6tables-restore(8)
+  - ip6tables-restore: implement missing -T option
+  - iptables: move kernel version find routing into libxtables
+  - libiptc: provide separate pkgconfig files
+  - libipt_SAME: set PROTO_RANDOM on all ranges
+  - libxtables: Fix file descriptor leak in xtables_lmap_init on error
+  - libxt_connbytes: fix handling of --connbytes FROM
+  - libxt_CONNSECMARK: fix spacing in output
+  - libxt_conntrack: improve error message on parsing violation
+  - libxt_NFQUEUE: fix --queue-bypass ipt-save output
+  - libxt_RATEEST: link with -lm
+  - libxt_statistic: link with -lm
+  - Merge branch 'stable'
+  - Merge branch 'stable' of git://dev.medozas.de/iptables
+  - nfnl_osf: add missing libnfnetlink_CFLAGS to compile process
+  - xtoptions: fill in fallback value for nvals
+  - xtoptions: simplify xtables_parse_interface
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.12.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Mon Dec 12 2011 Thomas Woerner <twoerner@redhat.com> 1.4.12.1-1
+- new version 1.4.12.1 with new pkgconfig/libipq.pc
+  - build: abort autogen on subcommand failure
+  - build: strengthen check for overlong lladdr components
+  - build: workaround broken linux-headers on RHEL-5
+  - doc: clarify libxt_connlimit defaults
+  - doc: fix typo in libxt_TRACE
+  - extensions: use multi-target registration
+  - libip6t_dst: restore setting IP6T_OPTS_LEN flag
+  - libip6t_frag: restore inversion support
+  - libip6t_hbh: restore setting IP6T_OPTS_LEN flag
+  - libipq: add pkgconfig file
+  - libipt_ttl: document that negation is available
+  - libxt_conntrack: fix --ctproto 0 output
+  - libxt_conntrack: remove one misleading comment
+  - libxt_dccp: fix deprecated intrapositional ordering of !
+  - libxt_dccp: fix random output of ! on --dccp-option
+  - libxt_dccp: provide man pages options in short help too
+  - libxt_dccp: restore missing XTOPT_INVERT tags for options
+  - libxt_dccp: spell out option name on save
+  - libxt_dscp: restore inversion support
+  - libxt_hashlimit: default htable-expire must be in milliseconds
+  - libxt_hashlimit: observe new default gc-expire time when saving
+  - libxt_hashlimit: remove inversion from hashlimit rev 0
+  - libxt_owner: restore inversion support
+  - libxt_physdev: restore inversion support
+  - libxt_policy: remove superfluous inversion
+  - libxt_set: put differing variable names in directly
+  - libxt_set: update man page about kernel support on the feature
+  - libxt_string: define _GNU_SOURCE for strnlen
+  - libxt_string: escape the escaping char too
+  - libxt_string: fix space around arguments
+  - libxt_string: replace hex codes by char equivalents
+  - libxt_string: simplify hex output routine
+  - libxt_tcp: always print the mask parts
+  - libxt_TCPMSS: restore build with IPv6-less libcs
+  - libxt_TOS: update linux kernel version list for backported fix
+  - libxt_u32: fix missing allowance for inversion
+  - src: remove unused IPTABLES_MULTI define
+  - tests: add negation tests for libxt_statistic
+  - xtoptions: flag use of XTOPT_POINTER without XTOPT_PUT
+- removed include/linux/types.h before build to be able to compile
+
+* Tue Jul 26 2011 Thomas Woerner <twoerner@redhat.com> 1.4.12-2
+- dropped temporary provide again
+
+* Tue Jul 26 2011 Thomas Woerner <twoerner@redhat.com> 1.4.12-1.1
+- added temporary provides for libxtables.so.6 to be able to rebuild iproute,
+  which is part of the standard build environment
+
+* Mon Jul 25 2011 Thomas Woerner <twoerner@redhat.com> 1.4.12-1
+- new version 1.4.12 with support of all new features of kernel 3.0
+  - build: attempt to fix building under Linux 2.4
+  - build: bump soversion for recent data structure change
+  - build: install modules in arch-dependent location
+  - doc: fix group range in libxt_NFLOG's man
+  - doc: fix version string in ip6tables.8
+  - doc: include matches/targets in manpage again
+  - doc: mention multiple verbosity flags
+  - doc: the -m option cannot be inverted
+  - extensions: support for per-extension instance global variable space
+  - iptables-apply: select default rule file depending on call name
+  - iptables: consolidate target/match init call
+  - iptables: Coverity: DEADCODE
+  - iptables: Coverity: NEGATIVE_RETURNS
+  - iptables: Coverity: RESOURCE_LEAK
+  - iptables: Coverity: REVERSE_INULL
+  - iptables: Coverity: VARARGS
+  - iptables: restore negation for -f
+  - libip6t_HL: fix option names from ttl -> hl
+  - libipt_LOG: fix ignoring all but last flags
+  - libxtables: ignore whitespace in the multiaddress argument parser
+  - libxtables: properly reject empty hostnames
+  - libxtables: set clone's initial data to NULL
+  - libxt_conntrack: move more data into the xt_option_entry
+  - libxt_conntrack: restore network-byte order for v1,v2
+  - libxt_hashlimit: use a more obvious expiry value by default
+  - libxt_rateest: abolish global variables
+  - libxt_RATEEST: abolish global variables
+  - libxt_RATEEST: fix userspacesize field
+  - libxt_RATEEST: use guided option parser
+  - libxt_state: fix regression about inversion of main option
+  - option: remove last traces of intrapositional negation
+- complete changelog:
+  http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.12.txt
+
+* Thu Jul 21 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11.1-4
+- merged ipv6 sub package into main package
+- renamed init scripts to /usr/libexec/ip*tables.init
+
+* Fri Jul 15 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11.1-3
+- added support for native systemd file (rhbz#694738)
+  - new iptables.service file
+  - additional requires
+  - moved sysv init scripts to /usr/libexec
+  - added new post, preun and postun scripts and triggers
+
+* Tue Jul 12 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11.1-2
+- dropped temporary provide again
+- enabled smp build
+
+* Tue Jul 12 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11.1-1.1
+-  added temporary provides for libxtables.so.5 to be able to rebuild iproute,
+   which is part of the standard build environment
+
+* Mon Jul 11 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11.1-1
+- new version 1.4.11.1, bug and doc fix release for 1.4.11
+
+* Tue Jun  7 2011 Thomas Woerner <twoerner@redhat.com> 1.4.11-1
+- new version 1.4.11 with all new features of 2.6.37-39 (not usable)
+  - lots of changes and bugfixes for base and extensions
+  - complete changelog:
+    http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.11.txt
+
+* Wed Feb 09 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.10-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Mon Jan 10 2011 Thomas Woerner <twoerner@redhat.com> 1.4.10-1
+- new version 1.4.10 with all new features of 2.6.36
+  - all: consistent syntax use in struct option
+  - build: fix static linking
+  - doc: let man(1) autoalign the text in xt_cpu
+  - doc: remove extra empty line from xt_cpu
+  - doc: minimal spelling updates to xt_cpu
+  - doc: consistent use of markup
+  - extensions: libxt_quota: don't ignore the quota value on deletion
+  - extensions: REDIRECT: add random help
+  - extensions: add xt_cpu match
+  - extensions: add idletimer xt target extension
+  - extensions: libxt_IDLETIMER: use xtables_param_act when checking options
+  - extensions: libxt_CHECKSUM extension
+  - extensions: libipt_LOG/libip6t_LOG: support macdecode option
+  - extensions: fix compilation of the new CHECKSUM target
+  - extensions: libxt_ipvs: user-space lib for netfilter matcher xt_ipvs
+  - iptables-xml: resolve compiler warnings
+  - iptables: limit chain name length to be consistent with targets
+  - libiptc: add Libs.private to pkgconfig files
+  - libiptc: build with -Wl,--no-as-needed
+  - xtables: remove unnecessary cast
+- dropped xt_CHECKSUM, added upstream
+
+* Tue Oct 12 2010 Thomas Woerner <twoerner@redhat.com> 1.4.9-2
+- added xt_CHECKSUM patch from Michael S. Tsirkin (rhbz#612587)
+
+* Wed Aug  4 2010 Thomas Woerner <twoerner@redhat.com> 1.4.9-1
+- new version 1.4.9 with all new features of 2.6.35
+  - doc: xt_hashlimit: fix a typo
+  - doc: xt_LED: nroff formatting requirements
+  - doc: xt_string: correct copy-and-pasting in manpage
+  - extensions: add the LED target
+  - extensions: libxt_quota.c: Support option negation
+  - extensions: libxt_rateest: fix bps options for iptables-save
+  - extensions: libxt_rateest: fix typo in the man page
+  - extensions: REDIRECT: add random help
+  - includes: sync header files from Linux 2.6.35-rc1
+  - libxt_conntrack: do print netmask
+  - libxt_hashlimit: always print burst value
+  - libxt_set: new revision added
+  - utils: add missing include flags to Makefile
+  - xtables: another try at chain name length checking
+  - xtables: remove xtables_set_revision function
+  - xt_quota: also document negation
+  - xt_sctp: Trace DATA chunk that supports SACK-IMMEDIATELY extension
+  - xt_sctp: support FORWARD_TSN chunk type
+
+* Fri Jul  2 2010 Thomas Woerner <twoerner@redhat.com> 1.4.8-1
+- new version 1.4.8 all new features of 2.6.34 (rhbz#)
+  - extensions: REDIRECT: fix --to-ports parser
+  - iptables: add noreturn attribute to exit_tryhelp()
+  - extensions: MASQUERADE: fix --to-ports parser
+  - libxt_comment: avoid use of IPv4-specific examples
+  - libxt_CT: add a manpage
+  - iptables: correctly check for too-long chain/target/match names
+  - doc: libxt_MARK: no longer restricted to mangle table
+  - doc: remove claim that TCPMSS is limited to mangle
+  - libxt_recent: add a missing space in output
+  - doc: add manpage for libxt_osf
+  - libxt_osf: import nfnl_osf program
+  - extensions: add support for xt_TEE
+  - CT: fix --ctevents parsing
+  - extensions: add CT extension
+  - libxt_CT: print conntrack zone in ->print/->save
+  - xtables: fix compilation when debugging is enabled
+  - libxt_conntrack: document --ctstate UNTRACKED
+  - iprange: fix xt_iprange v0 parsing
+
+* Wed Mar 24 2010 Thomas Woerner <twoerner@redhat.com> 1.4.7-2
+- added default values for IPTABLES_STATUS_VERBOSE and
+  IPTABLES_STATUS_LINENUMBERS in init script
+- added missing lsb keywords Required-Start and Required-Stop to init script
+
+* Fri Mar  5 2010 Thomas Woerner <twoerner@redhat.com> 1.4.7-1
+- new version 1.4.7 with support for all new features of 2.6.33 (rhbz#570767)
+  - libip4tc: Add static qualifier to dump_entry()
+  - libipq: build as shared library
+  - recent: reorder cases in code (cosmetic cleanup)
+  - several man page and documentation fixes
+  - policy: fix error message showing wrong option
+  - includes: header updates
+  - Lift restrictions on interface names
+- fixed license and moved iptables-xml into base package according to review
+
+* Wed Jan 27 2010 Thomas Woerner <twoerner@redhat.com> 1.4.6-2
+- moved libip*tc and libxtables libs to /lib[64], added symlinks for .so libs
+  to /usr/lib[64] for compatibility (rhbz#558796)
+
+* Wed Jan 13 2010 Thomas Woerner <twoerner@redhat.com> 1.4.6-1
+- new version 1.4.6 with support for all new features of 2.6.32
+  - several man page fixes
+  - Support for nommu arches
+  - realm: remove static initializations
+  - libiptc: remove unused functions
+  - libiptc: avoid strict-aliasing warnings
+  - iprange: do accept non-ranges for xt_iprange v1
+  - iprange: warn on reverse range
+  - iprange: roll address parsing into a loop
+  - iprange: do accept non-ranges for xt_iprange v1 (log)
+  - iprange: warn on reverse range (log)
+  - libiptc: fix wrong maptype of base chain counters on restore
+  - iptables: fix undersized deletion mask creation
+  - style: reduce indent in xtables_check_inverse
+  - libxtables: hand argv to xtables_check_inverse
+  - iptables/extensions: make bundled options work again
+  - CONNMARK: print mark rules with mask 0xffffffff as set instead of xset
+  - iptables: take masks into consideration for replace command
+  - doc: explain experienced --hitcount limit
+  - doc: name resolution clarification
+  - iptables: expose option to zero packet/byte counters for a specific rule
+  - build: restore --disable-ipv6 functionality on system w/o v6 headers
+  - MARK: print mark rules with mask 0xffffffff as --set-mark instead of --set-xmark
+  - DNAT: fix incorrect check during parsing
+  - extensions: add osf extension
+  - conntrack: fix --expires parsing
+
+* Thu Dec 17 2009 Thomas Woerner <twoerner@redhat.com> 1.4.5-2
+- dropped nf_ext_init remains from cloexec patch
+
+* Thu Sep 17 2009 Thomas Woerner <twoerner@redhat.com> 1.4.5-1
+- new version 1.4.5 with support for all new features of 2.6.31
+  - libxt_NFQUEUE: add new v1 version with queue-balance option
+  - xt_conntrack: revision 2 for enlarged state_mask member
+  - libxt_helper: fix invalid passed option to check_inverse
+  - libiptc: split v4 and v6
+  - extensions: collapse registration structures
+  - iptables: allow for parse-less extensions
+  - iptables: allow for help-less extensions
+  - extensions: remove empty help and parse functions
+  - xtables: add multi-registration functions
+  - extensions: collapse data variables to use multi-reg calls
+  - xtables: warn of missing version identifier in extensions
+  - multi binary: allow subcommand via argv[1]
+  - iptables: accept multiple IP address specifications for -s, -d
+  - several build fixes
+  - several man page fixes
+- fixed two leaked file descriptors on sockets (rhbz#521397)
+
+* Mon Aug 24 2009 Thomas Woerner <twoerner@redhat.com> 1.4.4-1
+- new version 1.4.4 with support for all new features of 2.6.30
+  - several man page fixes
+  - iptables: replace open-coded sizeof by ARRAY_SIZE
+  - libip6t_policy: remove redundant functions
+  - policy: use direct xt_policy_info instead of ipt/ip6t
+  - policy: merge ipv6 and ipv4 variant
+  - extensions: add `cluster' match support
+  - extensions: add const qualifiers in print/save functions
+  - extensions: use NFPROTO_UNSPEC for .family field
+  - extensions: remove redundant casts
+  - iptables: close open file descriptors
+  - fix segfault if incorrect protocol name is used
+  - replace open-coded sizeof by ARRAY_SIZE
+  - do not include v4-only modules in ip6tables manpage
+  - use direct xt_policy_info instead of ipt/ip6t
+  - xtables: fix segfault if incorrect protocol name is used
+  - libxt_connlimit: initialize v6_mask
+  - SNAT/DNAT: add support for persistent multi-range NAT mappings
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.3.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Wed Apr 15 2009 Thomas Woerner <twoerner@redhat.com> 1.4.3.2-1
+- new version 1.4.3.2
+- also install iptables/internal.h, needed for iptables.h and ip6tables.h
+
+* Mon Mar 30 2009 Thomas Woerner <twoerner@redhat.com> 1.4.3.1-1
+- new version 1.4.3.1
+  - libiptc is now shared
+  - supports all new features of the 2.6.29 kernel
+- dropped typo_latter patch
+
+* Thu Mar  5 2009 Thomas Woerner <twoerner@redhat.com> 1.4.2-3
+- still more review fixes (rhbz#225906)
+  - consistent macro usage
+  - use sed instead of perl for rpath removal
+  - use standard RPM CFLAGS, but also -fno-strict-aliasing (needed for libiptc*)
+
+* Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Fri Feb 20 2009 Thomas Woerner <twoerner@redhat.com> 1.4.2-1
+- new version 1.4.2
+- removed TOS value mask patch (upstream)
+- more review fixes (rhbz#225906)
+- install all header files (rhbz#462207)
+- dropped nf_ext_init (rhbz#472548)
+
+* Tue Jul 22 2008 Thomas Woerner <twoerner@redhat.com> 1.4.1.1-2
+- fixed TOS value mask problem (rhbz#456244) (upstream patch)
+- two more cloexec fixes
+
+* Tue Jul  1 2008 Thomas Woerner <twoerner@redhat.com> 1.4.1.1-1
+- upstream bug fix release 1.4.1.1
+- dropped extra patch for 1.4.1 - not needed anymore
+
+* Tue Jun 10 2008 Thomas Woerner <twoerner@redhat.com> 1.4.1-1
+- new version 1.4.1 with new build environment
+- additional ipv6 network mask patch from Jan Engelhardt
+- spec file cleanup
+- removed old patches
+
+* Fri Jun  6 2008 Tom "spot" Callaway <tcallawa@redhat.com> 1.4.0-5
+- use normal kernel headers, not linux/compiler.h
+- change BuildRequires: kernel-devel to kernel-headers
+- We need to do this to be able to build for both sparcv9 and sparc64 
+  (there is no kernel-devel.sparcv9)
+
+* Thu Mar 20 2008 Thomas Woerner <twoerner@redhat.com> 1.4.0-4
+- use O_CLOEXEC for all opened files in all applications (rhbz#438189)
+
+* Mon Mar  3 2008 Thomas Woerner <twoerner@redhat.com> 1.4.0-3
+- use the kernel headers from the build tree for iptables for now to be able to 
+  compile this package, but this makes the package more kernel dependant
+- use s6_addr32 instead of in6_u.u6_addr32
+
+* Wed Feb 20 2008 Fedora Release Engineering <rel-eng@fedoraproject.org> - 1.4.0-2
+- Autorebuild for GCC 4.3
+
+* Mon Feb 11 2008 Thomas Woerner <twoerner@redhat.com> 1.4.0-1
+- new version 1.4.0
+- fixed condrestart (rhbz#428148)
+- report the module in rmmod_r if there is an error
+- use nf_ext_init instead of my_init for extension constructors
+
+* Mon Nov  5 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-6
+- fixed leaked file descriptor before fork/exec (rhbz#312191)
+- blacklisting is not working, use "install X /bin/(true|false)" test instead
+- return private exit code 150 for disabled ipv6 support
+- use script name for output messages
+
+* Tue Oct 16 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-5
+- fixed error code for stopping a already stopped firewall (rhbz#321751)
+- moved blacklist test into start
+
+* Wed Sep 26 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-4.1
+- do not start ip6tables if ipv6 is blacklisted (rhbz#236888)
+- use simpler fix for (rhbz#295611)
+  Thanks to Linus Torvalds for the patch.
+
+* Mon Sep 24 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-4
+- fixed IPv6 reject type (rhbz#295181)
+- fixed init script: start, stop and status
+- support netfilter compiled into kernel in init script (rhbz#295611)
+- dropped inversion for limit modules from man pages (rhbz#220780)
+- fixed typo in ip6tables man page (rhbz#236185)
+
+* Wed Sep 19 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-3
+- do not depend on local_fs in lsb header - this delayes start after network
+- fixed exit code for initscript usage
+
+* Mon Sep 17 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-2.1
+- do not use lock file for condrestart test
+
+* Thu Aug 23 2007 Thomas Woerner <twoerner@redhat.com> 1.3.8-2
+- fixed initscript for LSB conformance (rhbz#246953, rhbz#242459)
+- provide iptc interface again, but unsupported (rhbz#216733)
+- compile all extension, which are supported by the kernel-headers package
+- review fixes (rhbz#225906)
+
+* Tue Jul 31 2007 Thomas Woerner <twoerner@redhat.com>
+- reverted ipv6 fix, because it disables the ipv6 at all (rhbz#236888)
+
+* Fri Jul 13 2007 Steve Conklin <sconklin@redhat.com> - 1.3.8-1
+- New version 1.3.8
+
+* Mon Apr 23 2007 Jeremy Katz <katzj@redhat.com> - 1.3.7-2
+- fix error when ipv6 support isn't loaded in the kernel (#236888)
+
+* Wed Jan 10 2007 Thomas Woerner <twoerner@redhat.com> 1.3.7-1.1
+- fixed installation of secmark modules
+
+* Tue Jan  9 2007 Thomas Woerner <twoerner@redhat.com> 1.3.7-1
+- new verison 1.3.7
+- iptc is not a public interface and therefore not installed anymore
+- dropped upstream secmark patch
+
+* Tue Sep 19 2006 Thomas Woerner <twoerner@redhat.com> 1.3.5-2
+- added secmark iptables patches (#201573)
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 1.3.5-1.2.1
+- rebuild
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 1.3.5-1.2
+- bump again for double-long bug on ppc(64)
+
+* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 1.3.5-1.1
+- rebuilt for new gcc4.1 snapshot and glibc changes
+
+* Thu Feb  2 2006 Thomas Woerner <twoerner@redhat.com> 1.3.5-1
+- new version 1.3.5
+- fixed init script to set policy for raw tables, too (#179094)
+
+* Tue Jan 24 2006 Thomas Woerner <twoerner@redhat.com> 1.3.4-3
+- added important iptables header files to devel package
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Fri Nov 25 2005 Thomas Woerner <twoerner@redhat.com> 1.3.4-2
+- fix for plugin problem: link with "gcc -shared" instead of "ld -shared" and 
+  replace "_init" with "__attribute((constructor)) my_init"
+
+* Fri Nov 25 2005 Thomas Woerner <twoerner@redhat.com> 1.3.4-1.1
+- rebuild due to unresolved symbols in shared libraries
+
+* Fri Nov 18 2005 Thomas Woerner <twoerner@redhat.com> 1.3.4-1
+- new version 1.3.4
+- dropped free_opts patch (upstream fixed)
+- made libipq PIC (#158623)
+- additional configuration options for iptables startup script (#172929)
+  Thanks to Jan Gruenwald for the patch
+- spec file cleanup (dropped linux_header define and usage)
+
+* Mon Jul 18 2005 Thomas Woerner <twoerner@redhat.com> 1.3.2-1
+- new version 1.3.2 with additional patch for the misplaced free_opts call
+  from Marcus Sundberg
+
+* Wed May 11 2005 Thomas Woerner <twoerner@redhat.com> 1.3.1-1
+- new version 1.3.1
+
+* Fri Mar 18 2005 Thomas Woerner <twoerner@redhat.com> 1.3.0-2
+- Remove unnecessary explicit kernel dep (#146142)
+- Fixed out of bounds accesses (#131848): Thanks to Steve Grubb
+  for the patch
+- Adapted iptables-config to reference to modprobe.conf (#150143)
+- Remove misleading message (#140154): Thanks to Ulrich Drepper
+  for the patch
+
+* Mon Feb 21 2005 Thomas Woerner <twoerner@redhat.com> 1.3.0-1
+- new version 1.3.0
+
+* Thu Nov 11 2004 Thomas Woerner <twoerner@redhat.com> 1.2.11-3.2
+- fixed autoload problem in iptables and ip6tables (CAN-2004-0986)
+
+* Fri Sep 17 2004 Thomas Woerner <twoerner@redhat.com> 1.2.11-3.1
+- changed default behaviour for IPTABLES_STATUS_NUMERIC to "yes" (#129731)
+- modified config file to match this change and un-commented variables with
+  default values
+
+* Thu Sep 16 2004 Thomas Woerner <twoerner@redhat.com> 1.2.11-3
+- applied second part of cleanup patch from (#131848): thanks to Steve Grubb
+  for the patch
+
+* Wed Aug 25 2004 Thomas Woerner <twoerner@redhat.com> 1.2.11-2
+- fixed free bug in iptables (#128322)
+
+* Tue Jun 22 2004 Thomas Woerner <twoerner@redhat.com> 1.2.11-1
+- new version 1.2.11
+
+* Thu Jun 17 2004 Thomas Woerner <twoerner@redhat.com> 1.2.10-1
+- new version 1.2.10
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Tue Mar 02 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Thu Feb 26 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-2.3
+- fixed iptables-restore -c fault if there are no counters (#116421)
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Sun Jan  25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.9-1.2
+- Close File descriptors to prevent SELinux error message
+
+* Wed Jan  7 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-1.1
+- rebuild
+
+* Wed Dec 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.9-1
+- vew version 1.2.9
+- new config options in ipXtables-config:
+  IPTABLES_MODULES_UNLOAD
+- more documentation in ipXtables-config
+- fix for netlink security issue in libipq (devel package)
+- print fix for libipt_icmp (#109546)
+
+* Thu Oct 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-13
+- marked all messages in iptables init script for translation (#107462)
+- enabled devel package (#105884, #106101)
+- bumped build for fedora for libipt_recent.so (#106002)
+
+* Tue Sep 23 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-12.1
+- fixed lost udp port range in ip6tables-save (#104484)
+- fixed non numeric multiport port output in ipXtables-savs
+
+* Mon Sep 22 2003 Florian La Roche <Florian.LaRoche@redhat.de> 1.2.8-11
+- do not link against -lnsl
+
+* Wed Sep 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-10
+- made variables in rmmod_r local
+
+* Tue Jul 22 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-9
+- fixed permission for init script
+
+* Sat Jul 19 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-8
+- fixed save when iptables file is missing and iptables-config permissions
+
+* Tue Jul  8 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-7
+- fixes for ip6tables: module unloading, setting policy only for existing 
+  tables
+
+* Thu Jul  3 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-6
+- IPTABLES_SAVE_COUNTER defaults to no, now
+- install config file in /etc/sysconfig
+- exchange unload of ip_tables and ip_conntrack
+- fixed start function
+
+* Wed Jul  2 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-5
+- new config option IPTABLES_SAVE_ON_RESTART
+- init script: new status, save and restart
+- fixes #44905, #65389, #80785, #82860, #91040, #91560 and #91374
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-4
+- new config option IPTABLES_STATUS_NUMERIC
+- cleared IPTABLES_MODULES in iptables-config
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-3
+- new init scripts
+
+* Sat Jun 28 2003 Florian La Roche <Florian.LaRoche@redhat.de>
+- remove check for very old kernel versions in init scripts
+- sync up both init scripts and remove some further ugly things
+- add some docu into rpm
+
+* Thu Jun 26  2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-2
+- rebuild
+
+* Mon Jun 16 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-1
+- update to 1.2.8
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Mon Jan 13 2003 Bill Nottingham <notting@redhat.com> 1.2.7a-1
+- update to 1.2.7a
+- add a plethora of bugfixes courtesy Michael Schwendt <mschewndt@yahoo.com>
+
+* Fri Dec 13 2002 Elliot Lee <sopwith@redhat.com> 1.2.6a-3
+- Fix multilib
+
+* Wed Aug 07 2002 Karsten Hopp <karsten@redhat.de>
+- fixed iptables and ip6tables initscript output, based on #70511
+- check return status of all iptables calls, not just the last one
+  in a 'for' loop.
+
+* Mon Jul 29 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.6a-1
+- 1.2.6a (bugfix release, #69747)
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Mar  4 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-3
+- Add some fixes from CVS, fixing bug #60465
+
+* Tue Feb 12 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-2
+- Merge ip6tables improvements from Ian Prowell <iprowell@prowell.org>
+  #59402
+- Update URL (#59354)
+- Use /sbin/chkconfig rather than chkconfig in %%postun script
+
+* Fri Jan 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-1
+- 1.2.5
+
+* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Nov  5 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-2
+- Fix %%preun script
+
+* Tue Oct 30 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-1
+- Update to 1.2.4 (various fixes, including security fixes; among others:
+  #42990, #50500, #53325, #54280)
+- Fix init script (#31133)
+
+* Mon Sep  3 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.3-1
+- 1.2.3 (5 security fixes, some other fixes)
+- Fix updating (#53032)
+
+* Mon Aug 27 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-4
+- Fix #50990
+- Add some fixes from current CVS; should fix #52620
+
+* Mon Jul 16 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-3
+- Add some fixes from the current CVS tree; fixes #49154 and some IPv6
+  issues
+
+* Tue Jun 26 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-2
+- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt
+  <mschwendt@yahoo.com>
+
+* Tue May  8 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-1
+- 1.2.2
+
+* Wed Mar 21 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2.1a, fixes #28412, #31136, #31460, #31133
+
+* Thu Mar  1 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Yet another initscript fix (#30173)
+- Fix the fixes; they fixed some issues but broke more important
+  stuff :/ (#30176)
+
+* Tue Feb 27 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Fix up initscript (#27962)
+- Add fixes from CVS to iptables-{restore,save}, fixing #28412
+
+* Fri Feb 09 2001 Karsten Hopp <karsten@redhat.de>
+- create /etc/sysconfig/iptables mode 600 (same problem as #24245)
+
+* Mon Feb 05 2001 Karsten Hopp <karsten@redhat.de>
+- fix bugzilla #25986 (initscript not marked as config file)
+- fix bugzilla #25962 (iptables-restore)
+- mv chkconfig --del from postun to preun
+
+* Thu Feb  1 2001 Trond Eivind Glomsrød <teg@redhat.com>
+- Fix check for ipchains
+
+* Mon Jan 29 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Some fixes to init scripts
+
+* Wed Jan 24 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add some fixes from CVS, fixes among other things Bug #24732
+
+* Wed Jan 17 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add missing man pages, fix up init script (Bug #17676)
+
+* Mon Jan 15 2001 Bill Nottingham <notting@redhat.com>
+- add init script
+
+* Mon Jan 15 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2
+- fix up ipv6 split
+- add init script
+- Move the plugins from /usr/lib/iptables to /lib/iptables.
+  This needs to work before /usr is mounted...
+- Use -O1 on alpha (compiler bug)
+
+* Sat Jan  6 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.2
+- Add IPv6 support (in separate package)
+
+* Thu Aug 17 2000 Bill Nottingham <notting@redhat.com>
+- build everywhere
+
+* Tue Jul 25 2000 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.1
+
+* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Tue Jun 27 2000 Preston Brown <pbrown@redhat.com>
+- move iptables to /sbin.
+- excludearch alpha for now, not building there because of compiler bug(?)
+
+* Fri Jun  9 2000 Bill Nottingham <notting@redhat.com>
+- don't obsolete ipchains either
+- update to 1.1.0
+
+* Sun Jun  4 2000 Bill Nottingham <notting@redhat.com>
+- remove explicit kernel requirement
+
+* Tue May  2 2000 Bernhard Rosenkränzer <bero@redhat.com>
+- initial package