Blame SOURCES/0052-ebtables-nft-Support-user-defined-chain-policies.patch

8cce6c
From 008ecae50b63744d9f51fcbe794ac896163c4639 Mon Sep 17 00:00:00 2001
8cce6c
From: Phil Sutter <phil@nwl.cc>
8cce6c
Date: Thu, 7 Feb 2019 22:08:55 +0100
8cce6c
Subject: [PATCH] ebtables-nft: Support user-defined chain policies
8cce6c
8cce6c
Legacy ebtables supports policies for user-defined chains - and what's
8cce6c
worse, they default to ACCEPT unlike anywhere else. So lack of support
8cce6c
for this braindead feature in ebtables-nft is actually a change of
8cce6c
behaviour which very likely affects all ebtables users out there.
8cce6c
8cce6c
The solution implemented here uses an implicit (and transparent) last
8cce6c
rule in all user-defined ebtables-nft chains with policy other than
8cce6c
RETURN. This rule is identified by an nft comment
8cce6c
"XTABLES_EB_INTERNAL_POLICY_RULE" (since commit ccf154d7420c0 ("xtables:
8cce6c
Don't use native nftables comments") nft comments are not used
8cce6c
otherwise).
8cce6c
8cce6c
To minimize interference with existing code, this policy rule is removed
8cce6c
from chains during cache population and the policy is saved in
8cce6c
NFTNL_CHAIN_POLICY attribute. When committing changes to the kernel,
8cce6c
nft_commit() traverses through the list of chains and (re-)creates
8cce6c
policy rules if required.
8cce6c
8cce6c
In ebtables-nft-restore, table flushes are problematic. To avoid weird
8cce6c
kernel error responses, introduce a custom 'table_flush' callback which
8cce6c
removes any pending policy rule add/remove jobs prior to creating the
8cce6c
NFT_COMPAT_TABLE_FLUSH one.
8cce6c
8cce6c
I've hidden all this mess behind checks for h->family, so hopefully
8cce6c
impact on {ip,ip6,arp}tables-nft should be negligible.
8cce6c
8cce6c
Signed-off-by: Phil Sutter <phil@nwl.cc>
8cce6c
Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
8cce6c
Signed-off-by: Florian Westphal <fw@strlen.de>
8cce6c
(cherry picked from commit aff1162b3e4b7ef805425a40306044c7d7dddc67)
8cce6c
Signed-off-by: Phil Sutter <psutter@redhat.com>
8cce6c
---
8cce6c
 iptables/nft-bridge.c                         |   2 +-
8cce6c
 iptables/nft.c                                | 228 +++++++++++++++++-
8cce6c
 iptables/nft.h                                |   4 +
8cce6c
 .../ebtables/0002-ebtables-save-restore_0     |   7 +
8cce6c
 iptables/xtables-eb.c                         |  20 +-
8cce6c
 iptables/xtables-restore.c                    |  23 +-
8cce6c
 6 files changed, 265 insertions(+), 19 deletions(-)
8cce6c
8cce6c
diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c
8cce6c
index 2b79ca951cd92..a51792ef03ae1 100644
8cce6c
--- a/iptables/nft-bridge.c
8cce6c
+++ b/iptables/nft-bridge.c
8cce6c
@@ -358,7 +358,7 @@ static void nft_bridge_print_header(unsigned int format, const char *chain,
8cce6c
 				    bool basechain, uint32_t refs, uint32_t entries)
8cce6c
 {
8cce6c
 	printf("Bridge chain: %s, entries: %u, policy: %s\n",
8cce6c
-	       chain, entries, basechain ? pol : "RETURN");
8cce6c
+	       chain, entries, pol ?: "RETURN");
8cce6c
 }
8cce6c
 
8cce6c
 static void print_matches_and_watchers(const struct iptables_command_state *cs,
8cce6c
diff --git a/iptables/nft.c b/iptables/nft.c
8cce6c
index 6129afdbad281..4fdc789d99928 100644
8cce6c
--- a/iptables/nft.c
8cce6c
+++ b/iptables/nft.c
8cce6c
@@ -55,6 +55,7 @@
8cce6c
 #include "nft.h"
8cce6c
 #include "xshared.h" /* proto_to_name */
8cce6c
 #include "nft-shared.h"
8cce6c
+#include "nft-bridge.h" /* EBT_NOPROTO */
8cce6c
 #include "xtables-config-parser.h"
8cce6c
 
8cce6c
 static void *nft_fn;
8cce6c
@@ -1325,6 +1326,87 @@ retry:
8cce6c
 	return ret;
8cce6c
 }
8cce6c
 
8cce6c
+static bool nft_rule_is_policy_rule(struct nftnl_rule *r)
8cce6c
+{
8cce6c
+	const struct nftnl_udata *tb[UDATA_TYPE_MAX + 1] = {};
8cce6c
+	const void *data;
8cce6c
+	uint32_t len;
8cce6c
+
8cce6c
+	if (!nftnl_rule_is_set(r, NFTNL_RULE_USERDATA))
8cce6c
+		return false;
8cce6c
+
8cce6c
+	data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len;;
8cce6c
+	if (nftnl_udata_parse(data, len, parse_udata_cb, tb) < 0)
8cce6c
+		return NULL;
8cce6c
+
8cce6c
+	if (!tb[UDATA_TYPE_EBTABLES_POLICY] ||
8cce6c
+	    nftnl_udata_get_u32(tb[UDATA_TYPE_EBTABLES_POLICY]) != 1)
8cce6c
+		return false;
8cce6c
+
8cce6c
+	return true;
8cce6c
+}
8cce6c
+
8cce6c
+static struct nftnl_rule *nft_chain_last_rule(struct nftnl_chain *c)
8cce6c
+{
8cce6c
+	struct nftnl_rule *r = NULL, *last;
8cce6c
+	struct nftnl_rule_iter *iter;
8cce6c
+
8cce6c
+	iter = nftnl_rule_iter_create(c);
8cce6c
+	if (!iter)
8cce6c
+		return NULL;
8cce6c
+
8cce6c
+	do {
8cce6c
+		last = r;
8cce6c
+		r = nftnl_rule_iter_next(iter);
8cce6c
+	} while (r);
8cce6c
+	nftnl_rule_iter_destroy(iter);
8cce6c
+
8cce6c
+	return last;
8cce6c
+}
8cce6c
+
8cce6c
+static void nft_bridge_chain_postprocess(struct nft_handle *h,
8cce6c
+					 struct nftnl_chain *c)
8cce6c
+{
8cce6c
+	struct nftnl_rule *last = nft_chain_last_rule(c);
8cce6c
+	struct nftnl_expr_iter *iter;
8cce6c
+	struct nftnl_expr *expr;
8cce6c
+	int verdict;
8cce6c
+
8cce6c
+	if (!last || !nft_rule_is_policy_rule(last))
8cce6c
+		return;
8cce6c
+
8cce6c
+	iter = nftnl_expr_iter_create(last);
8cce6c
+	if (!iter)
8cce6c
+		return;
8cce6c
+
8cce6c
+	expr = nftnl_expr_iter_next(iter);
8cce6c
+	if (!expr ||
8cce6c
+	    strcmp("counter", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)))
8cce6c
+		goto out_iter;
8cce6c
+
8cce6c
+	expr = nftnl_expr_iter_next(iter);
8cce6c
+	if (!expr ||
8cce6c
+	    strcmp("immediate", nftnl_expr_get_str(expr, NFTNL_EXPR_NAME)) ||
8cce6c
+	    !nftnl_expr_is_set(expr, NFTNL_EXPR_IMM_VERDICT))
8cce6c
+		goto out_iter;
8cce6c
+
8cce6c
+	verdict = nftnl_expr_get_u32(expr, NFTNL_EXPR_IMM_VERDICT);
8cce6c
+	switch (verdict) {
8cce6c
+	case NF_ACCEPT:
8cce6c
+	case NF_DROP:
8cce6c
+		break;
8cce6c
+	default:
8cce6c
+		goto out_iter;
8cce6c
+	}
8cce6c
+
8cce6c
+	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, verdict);
8cce6c
+	if (batch_rule_add(h, NFT_COMPAT_RULE_DELETE, last) < 0)
8cce6c
+		fprintf(stderr, "Failed to delete old policy rule\n");
8cce6c
+	nftnl_chain_rule_del(last);
8cce6c
+out_iter:
8cce6c
+	nftnl_expr_iter_destroy(iter);
8cce6c
+}
8cce6c
+
8cce6c
 static int nftnl_rule_list_cb(const struct nlmsghdr *nlh, void *data)
8cce6c
 {
8cce6c
 	struct nftnl_chain *c = data;
8cce6c
@@ -1378,6 +1460,10 @@ retry:
8cce6c
 	}
8cce6c
 
8cce6c
 	nftnl_rule_free(rule);
8cce6c
+
8cce6c
+	if (h->family == NFPROTO_BRIDGE)
8cce6c
+		nft_bridge_chain_postprocess(h, c);
8cce6c
+
8cce6c
 	return 0;
8cce6c
 }
8cce6c
 
8cce6c
@@ -1443,6 +1529,15 @@ int nft_chain_save(struct nft_handle *h, struct nftnl_chain_list *list)
8cce6c
 			if (nftnl_chain_get(c, NFTNL_CHAIN_POLICY))
8cce6c
 				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
8cce6c
 			policy = policy_name[pol];
8cce6c
+		} else if (h->family == NFPROTO_BRIDGE) {
8cce6c
+			if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY)) {
8cce6c
+				uint32_t pol;
8cce6c
+
8cce6c
+				pol = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
8cce6c
+				policy = policy_name[pol];
8cce6c
+			} else {
8cce6c
+				policy = "RETURN";
8cce6c
+			}
8cce6c
 		}
8cce6c
 
8cce6c
 		if (ops->save_chain)
8cce6c
@@ -1637,6 +1732,8 @@ int nft_chain_user_add(struct nft_handle *h, const char *chain, const char *tabl
8cce6c
 
8cce6c
 	nftnl_chain_set(c, NFTNL_CHAIN_TABLE, (char *)table);
8cce6c
 	nftnl_chain_set(c, NFTNL_CHAIN_NAME, (char *)chain);
8cce6c
+	if (h->family == NFPROTO_BRIDGE)
8cce6c
+		nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, NF_ACCEPT);
8cce6c
 
8cce6c
 	ret = batch_chain_add(h, NFT_COMPAT_CHAIN_USER_ADD, c);
8cce6c
 
8cce6c
@@ -2278,7 +2375,6 @@ static void __nft_print_header(struct nft_handle *h,
8cce6c
 			       struct nftnl_chain *c, unsigned int format)
8cce6c
 {
8cce6c
 	const char *chain_name = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME);
8cce6c
-	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
8cce6c
 	bool basechain = !!nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM);
8cce6c
 	uint32_t refs = nftnl_chain_get_u32(c, NFTNL_CHAIN_USE);
8cce6c
 	uint32_t entries = nft_rule_count(h, c);
8cce6c
@@ -2286,8 +2382,12 @@ static void __nft_print_header(struct nft_handle *h,
8cce6c
 		.pcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_PACKETS),
8cce6c
 		.bcnt = nftnl_chain_get_u64(c, NFTNL_CHAIN_BYTES),
8cce6c
 	};
8cce6c
+	const char *pname = NULL;
8cce6c
 
8cce6c
-	ops->print_header(format, chain_name, policy_name[policy],
8cce6c
+	if (nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
8cce6c
+		pname = policy_name[nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY)];
8cce6c
+
8cce6c
+	ops->print_header(format, chain_name, pname,
8cce6c
 			&ctrs, basechain, refs - entries, entries);
8cce6c
 }
8cce6c
 
8cce6c
@@ -2671,8 +2771,111 @@ static int nft_action(struct nft_handle *h, int action)
8cce6c
 	return ret == 0 ? 1 : 0;
8cce6c
 }
8cce6c
 
8cce6c
+static int ebt_add_policy_rule(struct nftnl_chain *c, void *data)
8cce6c
+{
8cce6c
+	uint32_t policy = nftnl_chain_get_u32(c, NFTNL_CHAIN_POLICY);
8cce6c
+	struct iptables_command_state cs = {
8cce6c
+		.eb.bitmask = EBT_NOPROTO,
8cce6c
+	};
8cce6c
+	struct nftnl_udata_buf *udata;
8cce6c
+	struct nft_handle *h = data;
8cce6c
+	struct nftnl_rule *r;
8cce6c
+	const char *pname;
8cce6c
+
8cce6c
+	if (nftnl_chain_get(c, NFTNL_CHAIN_HOOKNUM))
8cce6c
+		return 0; /* ignore base chains */
8cce6c
+
8cce6c
+	if (!nftnl_chain_is_set(c, NFTNL_CHAIN_POLICY))
8cce6c
+		return 0;
8cce6c
+
8cce6c
+	nftnl_chain_unset(c, NFTNL_CHAIN_POLICY);
8cce6c
+
8cce6c
+	switch (policy) {
8cce6c
+	case NFT_RETURN:
8cce6c
+		return 0; /* return policy is default for nft chains */
8cce6c
+	case NF_ACCEPT:
8cce6c
+		pname = "ACCEPT";
8cce6c
+		break;
8cce6c
+	case NF_DROP:
8cce6c
+		pname = "DROP";
8cce6c
+		break;
8cce6c
+	default:
8cce6c
+		return -1;
8cce6c
+	}
8cce6c
+
8cce6c
+	command_jump(&cs, pname);
8cce6c
+
8cce6c
+	r = nft_rule_new(h, nftnl_chain_get_str(c, NFTNL_CHAIN_NAME),
8cce6c
+			 nftnl_chain_get_str(c, NFTNL_CHAIN_TABLE), &cs);
8cce6c
+	if (!r)
8cce6c
+		return -1;
8cce6c
+
8cce6c
+	udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN);
8cce6c
+	if (!udata)
8cce6c
+		return -1;
8cce6c
+
8cce6c
+	if (!nftnl_udata_put_u32(udata, UDATA_TYPE_EBTABLES_POLICY, 1))
8cce6c
+		return -1;
8cce6c
+
8cce6c
+	nftnl_rule_set_data(r, NFTNL_RULE_USERDATA,
8cce6c
+			    nftnl_udata_buf_data(udata),
8cce6c
+			    nftnl_udata_buf_len(udata));
8cce6c
+	nftnl_udata_buf_free(udata);
8cce6c
+
8cce6c
+	if (batch_rule_add(h, NFT_COMPAT_RULE_APPEND, r) < 0) {
8cce6c
+		nftnl_rule_free(r);
8cce6c
+		return -1;
8cce6c
+	}
8cce6c
+
8cce6c
+	return 0;
8cce6c
+}
8cce6c
+
8cce6c
+int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
8cce6c
+			      const char *chain, const char *policy)
8cce6c
+{
8cce6c
+	struct nftnl_chain *c = nft_chain_find(h, table, chain);
8cce6c
+	int pval;
8cce6c
+
8cce6c
+	if (!c)
8cce6c
+		return 0;
8cce6c
+
8cce6c
+	if (!strcmp(policy, "DROP"))
8cce6c
+		pval = NF_DROP;
8cce6c
+	else if (!strcmp(policy, "ACCEPT"))
8cce6c
+		pval = NF_ACCEPT;
8cce6c
+	else if (!strcmp(policy, "RETURN"))
8cce6c
+		pval = NFT_RETURN;
8cce6c
+	else
8cce6c
+		return 0;
8cce6c
+
8cce6c
+	nftnl_chain_set_u32(c, NFTNL_CHAIN_POLICY, pval);
8cce6c
+	return 1;
8cce6c
+}
8cce6c
+
8cce6c
+static void nft_bridge_commit_prepare(struct nft_handle *h)
8cce6c
+{
8cce6c
+	const struct builtin_table *t;
8cce6c
+	struct nftnl_chain_list *list;
8cce6c
+	int i;
8cce6c
+
8cce6c
+	for (i = 0; i < NFT_TABLE_MAX; i++) {
8cce6c
+		t = &h->tables[i];
8cce6c
+
8cce6c
+		if (!t->name)
8cce6c
+			continue;
8cce6c
+
8cce6c
+		list = h->table[t->type].chain_cache;
8cce6c
+		if (!list)
8cce6c
+			continue;
8cce6c
+
8cce6c
+		nftnl_chain_list_foreach(list, ebt_add_policy_rule, h);
8cce6c
+	}
8cce6c
+}
8cce6c
+
8cce6c
 int nft_commit(struct nft_handle *h)
8cce6c
 {
8cce6c
+	if (h->family == NFPROTO_BRIDGE)
8cce6c
+		nft_bridge_commit_prepare(h);
8cce6c
 	return nft_action(h, NFT_COMPAT_COMMIT);
8cce6c
 }
8cce6c
 
8cce6c
@@ -2681,6 +2884,27 @@ int nft_abort(struct nft_handle *h)
8cce6c
 	return nft_action(h, NFT_COMPAT_ABORT);
8cce6c
 }
8cce6c
 
8cce6c
+int nft_abort_policy_rule(struct nft_handle *h, const char *table)
8cce6c
+{
8cce6c
+	struct obj_update *n, *tmp;
8cce6c
+
8cce6c
+	list_for_each_entry_safe(n, tmp, &h->obj_list, head) {
8cce6c
+		if (n->type != NFT_COMPAT_RULE_APPEND &&
8cce6c
+		    n->type != NFT_COMPAT_RULE_DELETE)
8cce6c
+			continue;
8cce6c
+
8cce6c
+		if (strcmp(table,
8cce6c
+			   nftnl_rule_get_str(n->rule, NFTNL_RULE_TABLE)))
8cce6c
+			continue;
8cce6c
+
8cce6c
+		if (!nft_rule_is_policy_rule(n->rule))
8cce6c
+			continue;
8cce6c
+
8cce6c
+		batch_obj_del(h, n);
8cce6c
+	}
8cce6c
+	return 0;
8cce6c
+}
8cce6c
+
8cce6c
 int nft_compatible_revision(const char *name, uint8_t rev, int opt)
8cce6c
 {
8cce6c
 	struct mnl_socket *nl;
8cce6c
diff --git a/iptables/nft.h b/iptables/nft.h
8cce6c
index 0726923a63dd4..56dc207608855 100644
8cce6c
--- a/iptables/nft.h
8cce6c
+++ b/iptables/nft.h
8cce6c
@@ -137,6 +137,7 @@ uint32_t nft_invflags2cmp(uint32_t invflags, uint32_t flag);
8cce6c
  */
8cce6c
 int nft_commit(struct nft_handle *h);
8cce6c
 int nft_abort(struct nft_handle *h);
8cce6c
+int nft_abort_policy_rule(struct nft_handle *h, const char *table);
8cce6c
 
8cce6c
 /*
8cce6c
  * revision compatibility.
8cce6c
@@ -203,4 +204,7 @@ void nft_rule_to_arpt_entry(struct nftnl_rule *r, struct arpt_entry *fw);
8cce6c
 
8cce6c
 bool nft_is_table_compatible(struct nft_handle *h, const char *name);
8cce6c
 
8cce6c
+int ebt_set_user_chain_policy(struct nft_handle *h, const char *table,
8cce6c
+			      const char *chain, const char *policy);
8cce6c
+
8cce6c
 #endif
8cce6c
diff --git a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0 b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
8cce6c
index b23c1ee18c8ae..080ba49a4974d 100755
8cce6c
--- a/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
8cce6c
+++ b/iptables/tests/shell/testcases/ebtables/0002-ebtables-save-restore_0
8cce6c
@@ -50,6 +50,9 @@ $XT_MULTI ebtables -A foo --pkttype-type multicast --limit 100 -j ACCEPT
8cce6c
 
8cce6c
 $XT_MULTI ebtables -A FORWARD -j foo
8cce6c
 
8cce6c
+$XT_MULTI ebtables -N bar
8cce6c
+$XT_MULTI ebtables -P bar RETURN
8cce6c
+
8cce6c
 $XT_MULTI ebtables -t nat -A PREROUTING --redirect-target ACCEPT
8cce6c
 #$XT_MULTI ebtables -t nat -A PREROUTING --to-src fe:ed:ba:be:00:01
8cce6c
 
8cce6c
@@ -59,6 +62,8 @@ $XT_MULTI ebtables -t nat -P OUTPUT DROP
8cce6c
 $XT_MULTI ebtables -t nat -A POSTROUTING -j ACCEPT
8cce6c
 #$XT_MULTI ebtables -t nat -A POSTROUTING --to-dst fe:ed:ba:be:00:01 --dnat-target ACCEPT
8cce6c
 
8cce6c
+$XT_MULTI ebtables -t nat -N nat_foo -P DROP
8cce6c
+
8cce6c
 # compare against stored ebtables dump
8cce6c
 
8cce6c
 DUMP='*filter
8cce6c
@@ -66,6 +71,7 @@ DUMP='*filter
8cce6c
 :FORWARD DROP
8cce6c
 :OUTPUT ACCEPT
8cce6c
 :foo ACCEPT
8cce6c
+:bar RETURN
8cce6c
 -A INPUT -p IPv4 -i lo -j ACCEPT
8cce6c
 -A FORWARD -j foo
8cce6c
 -A OUTPUT -s Broadcast -j DROP
8cce6c
@@ -98,6 +104,7 @@ DUMP='*filter
8cce6c
 :PREROUTING ACCEPT
8cce6c
 :OUTPUT DROP
8cce6c
 :POSTROUTING ACCEPT
8cce6c
+:nat_foo DROP
8cce6c
 -A PREROUTING -j redirect 
8cce6c
 -A OUTPUT -j ACCEPT
8cce6c
 -A POSTROUTING -j ACCEPT
8cce6c
diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c
8cce6c
index 21344843a365a..d0f0026e9c538 100644
8cce6c
--- a/iptables/xtables-eb.c
8cce6c
+++ b/iptables/xtables-eb.c
8cce6c
@@ -811,7 +811,6 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table,
8cce6c
 		case 'E': /* Rename chain */
8cce6c
 		case 'X': /* Delete chain */
8cce6c
 			/* We allow -N chainname -P policy */
8cce6c
-			/* XXX: Not in ebtables-compat */
8cce6c
 			if (command == 'N' && c == 'P') {
8cce6c
 				command = c;
8cce6c
 				optind--; /* No table specified */
8cce6c
@@ -1236,17 +1235,16 @@ print_zero:
8cce6c
 
8cce6c
 	if (command == 'P') {
8cce6c
 		if (selected_chain < 0) {
8cce6c
-			xtables_error(PARAMETER_PROBLEM,
8cce6c
-				      "Policy %s not allowed for user defined chains",
8cce6c
-				      policy);
8cce6c
-		}
8cce6c
-		if (strcmp(policy, "RETURN") == 0) {
8cce6c
-			xtables_error(PARAMETER_PROBLEM,
8cce6c
-				      "Policy RETURN only allowed for user defined chains");
8cce6c
+			ret = ebt_set_user_chain_policy(h, *table, chain, policy);
8cce6c
+		} else {
8cce6c
+			if (strcmp(policy, "RETURN") == 0) {
8cce6c
+				xtables_error(PARAMETER_PROBLEM,
8cce6c
+					      "Policy RETURN only allowed for user defined chains");
8cce6c
+			}
8cce6c
+			ret = nft_chain_set(h, *table, chain, policy, NULL);
8cce6c
+			if (ret < 0)
8cce6c
+				xtables_error(PARAMETER_PROBLEM, "Wrong policy");
8cce6c
 		}
8cce6c
-		ret = nft_chain_set(h, *table, chain, policy, NULL);
8cce6c
-		if (ret < 0)
8cce6c
-			xtables_error(PARAMETER_PROBLEM, "Wrong policy");
8cce6c
 	} else if (command == 'L') {
8cce6c
 		ret = list_rules(h, chain, *table, rule_nr,
8cce6c
 				 0,
8cce6c
diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c
8cce6c
index 4e00ed86be06d..6e6daffc9a1df 100644
8cce6c
--- a/iptables/xtables-restore.c
8cce6c
+++ b/iptables/xtables-restore.c
8cce6c
@@ -226,14 +226,20 @@ void xtables_restore_parse(struct nft_handle *h,
8cce6c
 							     curtable->name, chain);
8cce6c
 			} else if (cb->chain_user_add &&
8cce6c
 				   cb->chain_user_add(h, chain,
8cce6c
-						      curtable->name) < 0) {
8cce6c
-				if (errno == EEXIST)
8cce6c
-					continue;
8cce6c
-
8cce6c
+						      curtable->name) < 0 &&
8cce6c
+				   errno != EEXIST) {
8cce6c
 				xtables_error(PARAMETER_PROBLEM,
8cce6c
 					      "cannot create chain "
8cce6c
 					      "'%s' (%s)\n", chain,
8cce6c
 					      strerror(errno));
8cce6c
+			} else if (h->family == NFPROTO_BRIDGE &&
8cce6c
+				   !ebt_set_user_chain_policy(h, curtable->name,
8cce6c
+							      chain, policy)) {
8cce6c
+				xtables_error(OTHER_PROBLEM,
8cce6c
+					      "Can't set policy `%s'"
8cce6c
+					      " on `%s' line %u: %s\n",
8cce6c
+					      policy, chain, line,
8cce6c
+					      ops->strerror(errno));
8cce6c
 			}
8cce6c
 			ret = 1;
8cce6c
 		} else if (in_table) {
8cce6c
@@ -462,11 +468,18 @@ int xtables_ip6_restore_main(int argc, char *argv[])
8cce6c
 				    argc, argv);
8cce6c
 }
8cce6c
 
8cce6c
+static int ebt_table_flush(struct nft_handle *h, const char *table)
8cce6c
+{
8cce6c
+	/* drop any pending policy rule add/removal jobs */
8cce6c
+	nft_abort_policy_rule(h, table);
8cce6c
+	return nft_table_flush(h, table);
8cce6c
+}
8cce6c
+
8cce6c
 struct nft_xt_restore_cb ebt_restore_cb = {
8cce6c
 	.chain_list	= get_chain_list,
8cce6c
 	.commit		= nft_commit,
8cce6c
 	.table_new	= nft_table_new,
8cce6c
-	.table_flush	= nft_table_flush,
8cce6c
+	.table_flush	= ebt_table_flush,
8cce6c
 	.chain_user_flush = nft_chain_user_flush,
8cce6c
 	.do_command	= do_commandeb,
8cce6c
 	.chain_set	= nft_chain_set,
8cce6c
-- 
8cce6c
2.20.1
8cce6c