Skip to content

Commit 958bee1

Browse files
committed
netfilter: nf_tables: use new transaction infrastructure to handle sets
This patch reworks the nf_tables API so set updates are included in the same batch that contains rule updates. This speeds up rule-set updates since we skip a dialog of four messages between kernel and user-space (two on each direction), from: 1) create the set and send netlink message to the kernel 2) process the response from the kernel that contains the allocated name. 3) add the set elements and send netlink message to the kernel. 4) process the response from the kernel (to check for errors). To: 1) add the set to the batch. 2) add the set elements to the batch. 3) add the rule that points to the set. 4) send batch to the kernel. This also introduces an internal set ID (NFTA_SET_ID) that is unique in the batch so set elements and rules can refer to new sets. Backward compatibility has been only retained in userspace, this means that new nft versions can talk to the kernel both in the new and the old fashion. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent b380e5c commit 958bee1

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ static inline void *nft_set_priv(const struct nft_set *set)
268268

269269
struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
270270
const struct nlattr *nla);
271+
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
272+
const struct nlattr *nla);
271273

272274
/**
273275
* struct nft_set_binding - nf_tables set binding
@@ -408,6 +410,16 @@ struct nft_trans_rule {
408410
#define nft_trans_rule(trans) \
409411
(((struct nft_trans_rule *)trans->data)->rule)
410412

413+
struct nft_trans_set {
414+
struct nft_set *set;
415+
u32 set_id;
416+
};
417+
418+
#define nft_trans_set(trans) \
419+
(((struct nft_trans_set *)trans->data)->set)
420+
#define nft_trans_set_id(trans) \
421+
(((struct nft_trans_set *)trans->data)->set_id)
422+
411423
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
412424
{
413425
return (struct nft_expr *)&rule->data[0];

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ enum nft_set_desc_attributes {
246246
* @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
247247
* @NFTA_SET_POLICY: selection policy (NLA_U32)
248248
* @NFTA_SET_DESC: set description (NLA_NESTED)
249+
* @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
249250
*/
250251
enum nft_set_attributes {
251252
NFTA_SET_UNSPEC,
@@ -258,6 +259,7 @@ enum nft_set_attributes {
258259
NFTA_SET_DATA_LEN,
259260
NFTA_SET_POLICY,
260261
NFTA_SET_DESC,
262+
NFTA_SET_ID,
261263
__NFTA_SET_MAX
262264
};
263265
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
@@ -293,12 +295,14 @@ enum nft_set_elem_attributes {
293295
* @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
294296
* @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
295297
* @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
298+
* @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
296299
*/
297300
enum nft_set_elem_list_attributes {
298301
NFTA_SET_ELEM_LIST_UNSPEC,
299302
NFTA_SET_ELEM_LIST_TABLE,
300303
NFTA_SET_ELEM_LIST_SET,
301304
NFTA_SET_ELEM_LIST_ELEMENTS,
305+
NFTA_SET_ELEM_LIST_SET_ID,
302306
__NFTA_SET_ELEM_LIST_MAX
303307
};
304308
#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
@@ -484,12 +488,14 @@ enum nft_cmp_attributes {
484488
* @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING)
485489
* @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers)
486490
* @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers)
491+
* @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
487492
*/
488493
enum nft_lookup_attributes {
489494
NFTA_LOOKUP_UNSPEC,
490495
NFTA_LOOKUP_SET,
491496
NFTA_LOOKUP_SREG,
492497
NFTA_LOOKUP_DREG,
498+
NFTA_LOOKUP_SET_ID,
493499
__NFTA_LOOKUP_MAX
494500
};
495501
#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1)

net/netfilter/nf_tables_api.c

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,6 +1934,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
19341934
[NFTA_SET_DATA_LEN] = { .type = NLA_U32 },
19351935
[NFTA_SET_POLICY] = { .type = NLA_U32 },
19361936
[NFTA_SET_DESC] = { .type = NLA_NESTED },
1937+
[NFTA_SET_ID] = { .type = NLA_U32 },
19371938
};
19381939

19391940
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@@ -1984,6 +1985,20 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
19841985
return ERR_PTR(-ENOENT);
19851986
}
19861987

1988+
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
1989+
const struct nlattr *nla)
1990+
{
1991+
struct nft_trans *trans;
1992+
u32 id = ntohl(nla_get_be32(nla));
1993+
1994+
list_for_each_entry(trans, &net->nft.commit_list, list) {
1995+
if (trans->msg_type == NFT_MSG_NEWSET &&
1996+
id == nft_trans_set_id(trans))
1997+
return nft_trans_set(trans);
1998+
}
1999+
return ERR_PTR(-ENOENT);
2000+
}
2001+
19872002
static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
19882003
const char *name)
19892004
{
@@ -2259,6 +2274,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
22592274
return ret;
22602275
}
22612276

2277+
#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */
2278+
22622279
static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
22632280
const struct nlmsghdr *nlh,
22642281
const struct nlattr * const nla[])
@@ -2288,6 +2305,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
22882305
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
22892306
if (IS_ERR(set))
22902307
return PTR_ERR(set);
2308+
if (set->flags & NFT_SET_INACTIVE)
2309+
return -ENOENT;
22912310

22922311
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
22932312
if (skb2 == NULL)
@@ -2321,6 +2340,26 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
23212340
return 0;
23222341
}
23232342

2343+
static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
2344+
struct nft_set *set)
2345+
{
2346+
struct nft_trans *trans;
2347+
2348+
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
2349+
if (trans == NULL)
2350+
return -ENOMEM;
2351+
2352+
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
2353+
nft_trans_set_id(trans) =
2354+
ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
2355+
set->flags |= NFT_SET_INACTIVE;
2356+
}
2357+
nft_trans_set(trans) = set;
2358+
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
2359+
2360+
return 0;
2361+
}
2362+
23242363
static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
23252364
const struct nlmsghdr *nlh,
23262365
const struct nlattr * const nla[])
@@ -2341,7 +2380,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
23412380

23422381
if (nla[NFTA_SET_TABLE] == NULL ||
23432382
nla[NFTA_SET_NAME] == NULL ||
2344-
nla[NFTA_SET_KEY_LEN] == NULL)
2383+
nla[NFTA_SET_KEY_LEN] == NULL ||
2384+
nla[NFTA_SET_ID] == NULL)
23452385
return -EINVAL;
23462386

23472387
memset(&desc, 0, sizeof(desc));
@@ -2458,8 +2498,11 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
24582498
if (err < 0)
24592499
goto err2;
24602500

2501+
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
2502+
if (err < 0)
2503+
goto err2;
2504+
24612505
list_add_tail(&set->list, &table->sets);
2462-
nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
24632506
return 0;
24642507

24652508
err2:
@@ -2469,16 +2512,20 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
24692512
return err;
24702513
}
24712514

2472-
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2515+
static void nft_set_destroy(struct nft_set *set)
24732516
{
2474-
list_del(&set->list);
2475-
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
2476-
24772517
set->ops->destroy(set);
24782518
module_put(set->ops->owner);
24792519
kfree(set);
24802520
}
24812521

2522+
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2523+
{
2524+
list_del(&set->list);
2525+
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
2526+
nft_set_destroy(set);
2527+
}
2528+
24822529
static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
24832530
const struct nlmsghdr *nlh,
24842531
const struct nlattr * const nla[])
@@ -2500,10 +2547,16 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
25002547
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
25012548
if (IS_ERR(set))
25022549
return PTR_ERR(set);
2550+
if (set->flags & NFT_SET_INACTIVE)
2551+
return -ENOENT;
25032552
if (!list_empty(&set->bindings))
25042553
return -EBUSY;
25052554

2506-
nf_tables_set_destroy(&ctx, set);
2555+
err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
2556+
if (err < 0)
2557+
return err;
2558+
2559+
list_del(&set->list);
25072560
return 0;
25082561
}
25092562

@@ -2563,7 +2616,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
25632616
{
25642617
list_del(&binding->list);
25652618

2566-
if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
2619+
if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
2620+
!(set->flags & NFT_SET_INACTIVE))
25672621
nf_tables_set_destroy(ctx, set);
25682622
}
25692623

@@ -2581,6 +2635,7 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
25812635
[NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING },
25822636
[NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING },
25832637
[NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED },
2638+
[NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
25842639
};
25852640

25862641
static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
@@ -2680,6 +2735,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
26802735
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
26812736
if (IS_ERR(set))
26822737
return PTR_ERR(set);
2738+
if (set->flags & NFT_SET_INACTIVE)
2739+
return -ENOENT;
26832740

26842741
event = NFT_MSG_NEWSETELEM;
26852742
event |= NFNL_SUBSYS_NFTABLES << 8;
@@ -2743,6 +2800,8 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
27432800
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
27442801
if (IS_ERR(set))
27452802
return PTR_ERR(set);
2803+
if (set->flags & NFT_SET_INACTIVE)
2804+
return -ENOENT;
27462805

27472806
if (nlh->nlmsg_flags & NLM_F_DUMP) {
27482807
struct netlink_dump_control c = {
@@ -2928,6 +2987,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
29282987
const struct nlmsghdr *nlh,
29292988
const struct nlattr * const nla[])
29302989
{
2990+
struct net *net = sock_net(skb->sk);
29312991
const struct nlattr *attr;
29322992
struct nft_set *set;
29332993
struct nft_ctx ctx;
@@ -2938,8 +2998,15 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
29382998
return err;
29392999

29403000
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2941-
if (IS_ERR(set))
2942-
return PTR_ERR(set);
3001+
if (IS_ERR(set)) {
3002+
if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
3003+
set = nf_tables_set_lookup_byid(net,
3004+
nla[NFTA_SET_ELEM_LIST_SET_ID]);
3005+
}
3006+
if (IS_ERR(set))
3007+
return PTR_ERR(set);
3008+
}
3009+
29433010
if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
29443011
return -EBUSY;
29453012

@@ -3069,7 +3136,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
30693136
.policy = nft_rule_policy,
30703137
},
30713138
[NFT_MSG_NEWSET] = {
3072-
.call = nf_tables_newset,
3139+
.call_batch = nf_tables_newset,
30733140
.attr_count = NFTA_SET_MAX,
30743141
.policy = nft_set_policy,
30753142
},
@@ -3079,12 +3146,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
30793146
.policy = nft_set_policy,
30803147
},
30813148
[NFT_MSG_DELSET] = {
3082-
.call = nf_tables_delset,
3149+
.call_batch = nf_tables_delset,
30833150
.attr_count = NFTA_SET_MAX,
30843151
.policy = nft_set_policy,
30853152
},
30863153
[NFT_MSG_NEWSETELEM] = {
3087-
.call = nf_tables_newsetelem,
3154+
.call_batch = nf_tables_newsetelem,
30883155
.attr_count = NFTA_SET_ELEM_LIST_MAX,
30893156
.policy = nft_set_elem_list_policy,
30903157
},
@@ -3094,7 +3161,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
30943161
.policy = nft_set_elem_list_policy,
30953162
},
30963163
[NFT_MSG_DELSETELEM] = {
3097-
.call = nf_tables_delsetelem,
3164+
.call_batch = nf_tables_delsetelem,
30983165
.attr_count = NFTA_SET_ELEM_LIST_MAX,
30993166
.policy = nft_set_elem_list_policy,
31003167
},
@@ -3136,6 +3203,16 @@ static int nf_tables_commit(struct sk_buff *skb)
31363203
nft_trans_rule(trans), NFT_MSG_DELRULE, 0,
31373204
trans->ctx.afi->family);
31383205
break;
3206+
case NFT_MSG_NEWSET:
3207+
nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
3208+
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
3209+
NFT_MSG_NEWSET);
3210+
nft_trans_destroy(trans);
3211+
break;
3212+
case NFT_MSG_DELSET:
3213+
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
3214+
NFT_MSG_DELSET);
3215+
break;
31393216
}
31403217
}
31413218

@@ -3148,9 +3225,12 @@ static int nf_tables_commit(struct sk_buff *skb)
31483225
case NFT_MSG_DELRULE:
31493226
nf_tables_rule_destroy(&trans->ctx,
31503227
nft_trans_rule(trans));
3151-
nft_trans_destroy(trans);
3228+
break;
3229+
case NFT_MSG_DELSET:
3230+
nft_set_destroy(nft_trans_set(trans));
31523231
break;
31533232
}
3233+
nft_trans_destroy(trans);
31543234
}
31553235

31563236
return 0;
@@ -3170,6 +3250,14 @@ static int nf_tables_abort(struct sk_buff *skb)
31703250
nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
31713251
nft_trans_destroy(trans);
31723252
break;
3253+
case NFT_MSG_NEWSET:
3254+
list_del(&nft_trans_set(trans)->list);
3255+
break;
3256+
case NFT_MSG_DELSET:
3257+
list_add_tail(&nft_trans_set(trans)->list,
3258+
&trans->ctx.table->sets);
3259+
nft_trans_destroy(trans);
3260+
break;
31733261
}
31743262
}
31753263

@@ -3181,9 +3269,12 @@ static int nf_tables_abort(struct sk_buff *skb)
31813269
case NFT_MSG_NEWRULE:
31823270
nf_tables_rule_destroy(&trans->ctx,
31833271
nft_trans_rule(trans));
3184-
nft_trans_destroy(trans);
3272+
break;
3273+
case NFT_MSG_NEWSET:
3274+
nft_set_destroy(nft_trans_set(trans));
31853275
break;
31863276
}
3277+
nft_trans_destroy(trans);
31873278
}
31883279

31893280
return 0;

net/netfilter/nft_lookup.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
5656
return -EINVAL;
5757

5858
set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
59-
if (IS_ERR(set))
60-
return PTR_ERR(set);
59+
if (IS_ERR(set)) {
60+
if (tb[NFTA_LOOKUP_SET_ID]) {
61+
set = nf_tables_set_lookup_byid(ctx->net,
62+
tb[NFTA_LOOKUP_SET_ID]);
63+
}
64+
if (IS_ERR(set))
65+
return PTR_ERR(set);
66+
}
6167

6268
priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG]));
6369
err = nft_validate_input_register(priv->sreg);

0 commit comments

Comments
 (0)