Hi Phil, On Thu, Feb 14, 2019 at 06:21:04PM +0100, Phil Sutter wrote: > Hi Pablo, > > Originating from a problem with ebtables-nft user-defined chain > policies, I made up the following use-case: > > | # iptables-nft -A FORWARD -j ACCEPT > | # iptables-nft-restore --noflush <<EOF > | *filter > | -D FORWARD -j ACCEPT > | -F > | COMMIT > | EOF > | iptables-restore v1.8.2 (nf_tables): > | line 3: RULE_FLUSH failed (No such file or directory): rule in chain FORWARD > > In case anyone reading this is not aware of it: In nftables, flushing a > chain works by sending NFT_MSG_DELRULE message with just table and chain > defined, no rule handle or position. > > The problem is that delete command in batch removes the rule, flush > command then tries to delete the same rule again. Kernel returns -ENOENT > in nf_tables_delrule_deactivate(). > > The above use-case works with legacy iptables. > > Question is if I have to work around this in userspace or if we should > make nf_tables_delrule_deactivate() return 0 even if given rule is not > active? Downside is that second option would cause double deletion of > same rule within a single batch to succeed. >From the flush path, we can skip requests for deletion of rules that have been already deleted, ie. we add no transaction since there is already one in place.
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 5a92f23f179f..efc79422a8c7 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -288,11 +288,14 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type, return trans; } -static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule) +static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule, bool flush) { struct nft_trans *trans; int err; + if (flush && !nft_is_active_next(ctx->net, rule)) + return 0; + trans = nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule); if (trans == NULL) return -ENOMEM; @@ -313,7 +316,7 @@ static int nft_delrule_by_chain(struct nft_ctx *ctx) int err; list_for_each_entry(rule, &ctx->chain->rules, list) { - err = nft_delrule(ctx, rule); + err = nft_delrule(ctx, rule, true); if (err < 0) return err; } @@ -1906,7 +1909,7 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk, continue; use--; - err = nft_delrule(&ctx, rule); + err = nft_delrule(&ctx, rule, true); if (err < 0) return err; } @@ -2707,7 +2710,7 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, err = -ENOMEM; goto err2; } - err = nft_delrule(&ctx, old_rule); + err = nft_delrule(&ctx, old_rule, false); if (err < 0) { nft_trans_destroy(trans); goto err2; @@ -2804,7 +2807,7 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk, return PTR_ERR(rule); } - err = nft_delrule(&ctx, rule); + err = nft_delrule(&ctx, rule, false); } else if (nla[NFTA_RULE_ID]) { rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]); if (IS_ERR(rule)) { @@ -2812,7 +2815,7 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk, return PTR_ERR(rule); } - err = nft_delrule(&ctx, rule); + err = nft_delrule(&ctx, rule, false); } else { err = nft_delrule_by_chain(&ctx); }