Skip to content

Commit 63b48c7

Browse files
committed
netfilter: nf_tables_offload: undo updates if transaction fails
The nft_flow_rule_offload_commit() function might fail after several successful commands, thus, leaving the hardware filtering policy in inconsistent state. This patch adds nft_flow_rule_offload_abort() function which undoes the updates that have been already processed if one command in this transaction fails. Hence, the hardware ruleset is left as it was before this aborted transaction. The deletion path needs to create the flow_rule object too, in case that an existing rule needs to be re-added from the abort path. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 23403cd commit 63b48c7

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,24 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
361361

362362
static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
363363
{
364+
struct nft_flow_rule *flow;
364365
struct nft_trans *trans;
365366
int err;
366367

367368
trans = nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule);
368369
if (trans == NULL)
369370
return -ENOMEM;
370371

372+
if (ctx->chain->flags & NFT_CHAIN_HW_OFFLOAD) {
373+
flow = nft_flow_rule_create(ctx->net, rule);
374+
if (IS_ERR(flow)) {
375+
nft_trans_destroy(trans);
376+
return PTR_ERR(flow);
377+
}
378+
379+
nft_trans_flow_rule(trans) = flow;
380+
}
381+
371382
err = nf_tables_delrule_deactivate(ctx, rule);
372383
if (err < 0) {
373384
nft_trans_destroy(trans);

net/netfilter/nf_tables_offload.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,55 @@ static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
389389
return nft_flow_block_chain(basechain, NULL, cmd);
390390
}
391391

392+
static void nft_flow_rule_offload_abort(struct net *net,
393+
struct nft_trans *trans)
394+
{
395+
int err = 0;
396+
397+
list_for_each_entry_continue_reverse(trans, &net->nft.commit_list, list) {
398+
if (trans->ctx.family != NFPROTO_NETDEV)
399+
continue;
400+
401+
switch (trans->msg_type) {
402+
case NFT_MSG_NEWCHAIN:
403+
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
404+
nft_trans_chain_update(trans))
405+
continue;
406+
407+
err = nft_flow_offload_chain(trans->ctx.chain, NULL,
408+
FLOW_BLOCK_UNBIND);
409+
break;
410+
case NFT_MSG_DELCHAIN:
411+
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
412+
continue;
413+
414+
err = nft_flow_offload_chain(trans->ctx.chain, NULL,
415+
FLOW_BLOCK_BIND);
416+
break;
417+
case NFT_MSG_NEWRULE:
418+
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
419+
continue;
420+
421+
err = nft_flow_offload_rule(trans->ctx.chain,
422+
nft_trans_rule(trans),
423+
NULL, FLOW_CLS_DESTROY);
424+
break;
425+
case NFT_MSG_DELRULE:
426+
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
427+
continue;
428+
429+
err = nft_flow_offload_rule(trans->ctx.chain,
430+
nft_trans_rule(trans),
431+
nft_trans_flow_rule(trans),
432+
FLOW_CLS_REPLACE);
433+
break;
434+
}
435+
436+
if (WARN_ON_ONCE(err))
437+
break;
438+
}
439+
}
440+
392441
int nft_flow_rule_offload_commit(struct net *net)
393442
{
394443
struct nft_trans *trans;
@@ -441,8 +490,10 @@ int nft_flow_rule_offload_commit(struct net *net)
441490
break;
442491
}
443492

444-
if (err)
493+
if (err) {
494+
nft_flow_rule_offload_abort(net, trans);
445495
break;
496+
}
446497
}
447498

448499
list_for_each_entry(trans, &net->nft.commit_list, list) {
@@ -451,6 +502,7 @@ int nft_flow_rule_offload_commit(struct net *net)
451502

452503
switch (trans->msg_type) {
453504
case NFT_MSG_NEWRULE:
505+
case NFT_MSG_DELRULE:
454506
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
455507
continue;
456508

0 commit comments

Comments
 (0)