Skip to content

Commit 55dd6f9

Browse files
committed
netfilter: nf_tables: use new transaction infrastructure to handle table
This patch speeds up rule-set updates and it also provides a way to revert updates and leave things in consistent state in case that the batch needs to be aborted. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent e1aaca9 commit 55dd6f9

File tree

2 files changed

+136
-19
lines changed

2 files changed

+136
-19
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ struct nft_trans_chain {
436436
#define nft_trans_chain_policy(trans) \
437437
(((struct nft_trans_chain *)trans->data)->policy)
438438

439+
struct nft_trans_table {
440+
bool update;
441+
bool enable;
442+
};
443+
444+
#define nft_trans_table_update(trans) \
445+
(((struct nft_trans_table *)trans->data)->update)
446+
#define nft_trans_table_enable(trans) \
447+
(((struct nft_trans_table *)trans->data)->enable)
448+
439449
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
440450
{
441451
return (struct nft_expr *)&rule->data[0];

net/netfilter/nf_tables_api.c

Lines changed: 126 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
307307
return skb->len;
308308
}
309309

310+
/* Internal table flags */
311+
#define NFT_TABLE_INACTIVE (1 << 15)
312+
310313
static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
311314
const struct nlmsghdr *nlh,
312315
const struct nlattr * const nla[])
@@ -333,6 +336,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
333336
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
334337
if (IS_ERR(table))
335338
return PTR_ERR(table);
339+
if (table->flags & NFT_TABLE_INACTIVE)
340+
return -ENOENT;
336341

337342
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
338343
if (!skb2)
@@ -395,9 +400,9 @@ static void nf_tables_table_disable(const struct nft_af_info *afi,
395400

396401
static int nf_tables_updtable(struct nft_ctx *ctx)
397402
{
398-
const struct nfgenmsg *nfmsg = nlmsg_data(ctx->nlh);
399-
int family = nfmsg->nfgen_family, ret = 0;
403+
struct nft_trans *trans;
400404
u32 flags;
405+
int ret = 0;
401406

402407
if (!ctx->nla[NFTA_TABLE_FLAGS])
403408
return 0;
@@ -406,25 +411,48 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
406411
if (flags & ~NFT_TABLE_F_DORMANT)
407412
return -EINVAL;
408413

414+
trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
415+
sizeof(struct nft_trans_table));
416+
if (trans == NULL)
417+
return -ENOMEM;
418+
409419
if ((flags & NFT_TABLE_F_DORMANT) &&
410420
!(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
411-
nf_tables_table_disable(ctx->afi, ctx->table);
412-
ctx->table->flags |= NFT_TABLE_F_DORMANT;
421+
nft_trans_table_enable(trans) = false;
413422
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
414423
ctx->table->flags & NFT_TABLE_F_DORMANT) {
415424
ret = nf_tables_table_enable(ctx->afi, ctx->table);
416-
if (ret >= 0)
425+
if (ret >= 0) {
417426
ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
427+
nft_trans_table_enable(trans) = true;
428+
}
418429
}
419430
if (ret < 0)
420431
goto err;
421432

422-
nf_tables_table_notify(ctx->skb, ctx->nlh, ctx->table,
423-
NFT_MSG_NEWTABLE, family);
433+
nft_trans_table_update(trans) = true;
434+
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
435+
return 0;
424436
err:
437+
nft_trans_destroy(trans);
425438
return ret;
426439
}
427440

441+
static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
442+
{
443+
struct nft_trans *trans;
444+
445+
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
446+
if (trans == NULL)
447+
return -ENOMEM;
448+
449+
if (msg_type == NFT_MSG_NEWTABLE)
450+
ctx->table->flags |= NFT_TABLE_INACTIVE;
451+
452+
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
453+
return 0;
454+
}
455+
428456
static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
429457
const struct nlmsghdr *nlh,
430458
const struct nlattr * const nla[])
@@ -437,6 +465,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
437465
int family = nfmsg->nfgen_family;
438466
u32 flags = 0;
439467
struct nft_ctx ctx;
468+
int err;
440469

441470
afi = nf_tables_afinfo_lookup(net, family, true);
442471
if (IS_ERR(afi))
@@ -451,6 +480,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
451480
}
452481

453482
if (table != NULL) {
483+
if (table->flags & NFT_TABLE_INACTIVE)
484+
return -ENOENT;
454485
if (nlh->nlmsg_flags & NLM_F_EXCL)
455486
return -EEXIST;
456487
if (nlh->nlmsg_flags & NLM_F_REPLACE)
@@ -480,8 +511,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
480511
INIT_LIST_HEAD(&table->sets);
481512
table->flags = flags;
482513

514+
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
515+
err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
516+
if (err < 0) {
517+
kfree(table);
518+
module_put(afi->owner);
519+
return err;
520+
}
483521
list_add_tail(&table->list, &afi->tables);
484-
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
485522
return 0;
486523
}
487524

@@ -493,7 +530,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
493530
struct nft_af_info *afi;
494531
struct nft_table *table;
495532
struct net *net = sock_net(skb->sk);
496-
int family = nfmsg->nfgen_family;
533+
int family = nfmsg->nfgen_family, err;
534+
struct nft_ctx ctx;
497535

498536
afi = nf_tables_afinfo_lookup(net, family, false);
499537
if (IS_ERR(afi))
@@ -502,17 +540,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
502540
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
503541
if (IS_ERR(table))
504542
return PTR_ERR(table);
543+
if (table->flags & NFT_TABLE_INACTIVE)
544+
return -ENOENT;
505545

506546
if (!list_empty(&table->chains) || !list_empty(&table->sets))
507547
return -EBUSY;
508548

549+
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
550+
err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
551+
if (err < 0)
552+
return err;
553+
509554
list_del(&table->list);
510-
nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
511-
kfree(table);
512-
module_put(afi->owner);
513555
return 0;
514556
}
515557

558+
static void nf_tables_table_destroy(struct nft_ctx *ctx)
559+
{
560+
kfree(ctx->table);
561+
module_put(ctx->afi->owner);
562+
}
563+
516564
int nft_register_chain_type(const struct nf_chain_type *ctype)
517565
{
518566
int err = 0;
@@ -776,6 +824,8 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
776824
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
777825
if (IS_ERR(table))
778826
return PTR_ERR(table);
827+
if (table->flags & NFT_TABLE_INACTIVE)
828+
return -ENOENT;
779829

780830
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
781831
if (IS_ERR(chain))
@@ -1120,6 +1170,8 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
11201170
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
11211171
if (IS_ERR(table))
11221172
return PTR_ERR(table);
1173+
if (table->flags & NFT_TABLE_INACTIVE)
1174+
return -ENOENT;
11231175

11241176
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
11251177
if (IS_ERR(chain))
@@ -1573,6 +1625,8 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
15731625
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
15741626
if (IS_ERR(table))
15751627
return PTR_ERR(table);
1628+
if (table->flags & NFT_TABLE_INACTIVE)
1629+
return -ENOENT;
15761630

15771631
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
15781632
if (IS_ERR(chain))
@@ -1838,6 +1892,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
18381892
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
18391893
if (IS_ERR(table))
18401894
return PTR_ERR(table);
1895+
if (table->flags & NFT_TABLE_INACTIVE)
1896+
return -ENOENT;
18411897

18421898
if (nla[NFTA_RULE_CHAIN]) {
18431899
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -2004,6 +2060,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
20042060
table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
20052061
if (IS_ERR(table))
20062062
return PTR_ERR(table);
2063+
if (table->flags & NFT_TABLE_INACTIVE)
2064+
return -ENOENT;
20072065
}
20082066

20092067
nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
@@ -2681,7 +2739,8 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
26812739
static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
26822740
const struct sk_buff *skb,
26832741
const struct nlmsghdr *nlh,
2684-
const struct nlattr * const nla[])
2742+
const struct nlattr * const nla[],
2743+
bool trans)
26852744
{
26862745
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
26872746
struct nft_af_info *afi;
@@ -2695,6 +2754,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
26952754
table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
26962755
if (IS_ERR(table))
26972756
return PTR_ERR(table);
2757+
if (!trans && (table->flags & NFT_TABLE_INACTIVE))
2758+
return -ENOENT;
26982759

26992760
nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
27002761
return 0;
@@ -2768,7 +2829,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
27682829
if (err < 0)
27692830
return err;
27702831

2771-
err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla);
2832+
err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
2833+
false);
27722834
if (err < 0)
27732835
return err;
27742836

@@ -2833,7 +2895,7 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
28332895
struct nft_ctx ctx;
28342896
int err;
28352897

2836-
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
2898+
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
28372899
if (err < 0)
28382900
return err;
28392901

@@ -3033,7 +3095,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
30333095
struct nft_ctx ctx;
30343096
int rem, err;
30353097

3036-
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
3098+
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
30373099
if (err < 0)
30383100
return err;
30393101

@@ -3111,7 +3173,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
31113173
struct nft_ctx ctx;
31123174
int rem, err;
31133175

3114-
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla);
3176+
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
31153177
if (err < 0)
31163178
return err;
31173179

@@ -3131,7 +3193,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
31313193

31323194
static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
31333195
[NFT_MSG_NEWTABLE] = {
3134-
.call = nf_tables_newtable,
3196+
.call_batch = nf_tables_newtable,
31353197
.attr_count = NFTA_TABLE_MAX,
31363198
.policy = nft_table_policy,
31373199
},
@@ -3141,7 +3203,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
31413203
.policy = nft_table_policy,
31423204
},
31433205
[NFT_MSG_DELTABLE] = {
3144-
.call = nf_tables_deltable,
3206+
.call_batch = nf_tables_deltable,
31453207
.attr_count = NFTA_TABLE_MAX,
31463208
.policy = nft_table_policy,
31473209
},
@@ -3246,6 +3308,28 @@ static int nf_tables_commit(struct sk_buff *skb)
32463308

32473309
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
32483310
switch (trans->msg_type) {
3311+
case NFT_MSG_NEWTABLE:
3312+
if (nft_trans_table_update(trans)) {
3313+
if (!nft_trans_table_enable(trans)) {
3314+
nf_tables_table_disable(trans->ctx.afi,
3315+
trans->ctx.table);
3316+
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3317+
}
3318+
} else {
3319+
trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
3320+
}
3321+
nf_tables_table_notify(trans->ctx.skb, trans->ctx.nlh,
3322+
trans->ctx.table,
3323+
NFT_MSG_NEWTABLE,
3324+
trans->ctx.afi->family);
3325+
nft_trans_destroy(trans);
3326+
break;
3327+
case NFT_MSG_DELTABLE:
3328+
nf_tables_table_notify(trans->ctx.skb, trans->ctx.nlh,
3329+
trans->ctx.table,
3330+
NFT_MSG_DELTABLE,
3331+
trans->ctx.afi->family);
3332+
break;
32493333
case NFT_MSG_NEWCHAIN:
32503334
if (nft_trans_chain_update(trans))
32513335
nft_chain_commit_update(trans);
@@ -3310,6 +3394,9 @@ static int nf_tables_commit(struct sk_buff *skb)
33103394
/* Now we can safely release unused old rules */
33113395
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
33123396
switch (trans->msg_type) {
3397+
case NFT_MSG_DELTABLE:
3398+
nf_tables_table_destroy(&trans->ctx);
3399+
break;
33133400
case NFT_MSG_DELCHAIN:
33143401
nf_tables_chain_destroy(trans->ctx.chain);
33153402
break;
@@ -3334,6 +3421,23 @@ static int nf_tables_abort(struct sk_buff *skb)
33343421

33353422
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
33363423
switch (trans->msg_type) {
3424+
case NFT_MSG_NEWTABLE:
3425+
if (nft_trans_table_update(trans)) {
3426+
if (nft_trans_table_enable(trans)) {
3427+
nf_tables_table_disable(trans->ctx.afi,
3428+
trans->ctx.table);
3429+
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3430+
}
3431+
nft_trans_destroy(trans);
3432+
} else {
3433+
list_del(&trans->ctx.table->list);
3434+
}
3435+
break;
3436+
case NFT_MSG_DELTABLE:
3437+
list_add_tail(&trans->ctx.table->list,
3438+
&trans->ctx.afi->tables);
3439+
nft_trans_destroy(trans);
3440+
break;
33373441
case NFT_MSG_NEWCHAIN:
33383442
if (nft_trans_chain_update(trans)) {
33393443
if (nft_trans_chain_stats(trans))
@@ -3377,6 +3481,9 @@ static int nf_tables_abort(struct sk_buff *skb)
33773481

33783482
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
33793483
switch (trans->msg_type) {
3484+
case NFT_MSG_NEWTABLE:
3485+
nf_tables_table_destroy(&trans->ctx);
3486+
break;
33803487
case NFT_MSG_NEWCHAIN:
33813488
nf_tables_chain_destroy(trans->ctx.chain);
33823489
break;

0 commit comments

Comments
 (0)