Skip to content

Commit 84d7fce

Browse files
committed
netfilter: nf_tables: export rule-set generation ID
This patch exposes the ruleset generation ID in three ways: 1) The new command NFT_MSG_GETGEN that exposes the 32-bits ruleset generation ID. This ID is incremented in every commit and it should be large enough to avoid wraparound problems. 2) The less significant 16-bits of the generation ID are exposed through the nfgenmsg->res_id header field. This allows us to quickly catch if the ruleset has change between two consecutive list dumps from different object lists (in this specific case I think the risk of wraparound is unlikely). 3) Userspace subscribers may receive notifications of new rule-set generation after every commit. This also provides an alternative way to monitor the generation ID. If the events are lost, the userspace process hits a overrun error, so it knows that it is working with a stale ruleset anyway. Patrick spotted that rule-set transformations in userspace may take quite some time. In that case, it annotates the 32-bits generation ID before fetching the rule-set, then: 1) it compares it to what we obtain after the transformation to make sure it is not working with a stale rule-set and no wraparound has ocurred. 2) it subscribes to ruleset notifications, so it can watch for new generation ID. This is complementary to the NLM_F_DUMP_INTR approach, which allows us to detect an interference in the middle one single list dumping. There is no way to explicitly check that an interference has occurred between two list dumps from the kernel, since it doesn't know how many lists the userspace client is actually going to dump. Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent fc04733 commit 84d7fce

File tree

2 files changed

+130
-26
lines changed

2 files changed

+130
-26
lines changed

include/uapi/linux/netfilter/nf_tables.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ enum nft_verdicts {
5151
* @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes)
5252
* @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes)
5353
* @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
54+
* @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes)
55+
* @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes)
5456
*/
5557
enum nf_tables_msg_types {
5658
NFT_MSG_NEWTABLE,
@@ -68,6 +70,8 @@ enum nf_tables_msg_types {
6870
NFT_MSG_NEWSETELEM,
6971
NFT_MSG_GETSETELEM,
7072
NFT_MSG_DELSETELEM,
73+
NFT_MSG_NEWGEN,
74+
NFT_MSG_GETGEN,
7175
NFT_MSG_MAX,
7276
};
7377

@@ -812,4 +816,16 @@ enum nft_masq_attributes {
812816
};
813817
#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1)
814818

819+
/**
820+
* enum nft_gen_attributes - nf_tables ruleset generation attributes
821+
*
822+
* @NFTA_GEN_ID: Ruleset generation ID (NLA_U32)
823+
*/
824+
enum nft_gen_attributes {
825+
NFTA_GEN_UNSPEC,
826+
NFTA_GEN_ID,
827+
__NFTA_GEN_MAX
828+
};
829+
#define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1)
830+
815831
#endif /* _LINUX_NF_TABLES_H */

net/netfilter/nf_tables_api.c

Lines changed: 114 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,9 @@ static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
405405
[NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
406406
};
407407

408-
static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
409-
int event, u32 flags, int family,
410-
const struct nft_table *table)
408+
static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
409+
u32 portid, u32 seq, int event, u32 flags,
410+
int family, const struct nft_table *table)
411411
{
412412
struct nlmsghdr *nlh;
413413
struct nfgenmsg *nfmsg;
@@ -420,7 +420,7 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
420420
nfmsg = nlmsg_data(nlh);
421421
nfmsg->nfgen_family = family;
422422
nfmsg->version = NFNETLINK_V0;
423-
nfmsg->res_id = 0;
423+
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
424424

425425
if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
426426
nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
@@ -448,8 +448,8 @@ static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
448448
if (skb == NULL)
449449
goto err;
450450

451-
err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
452-
ctx->afi->family, ctx->table);
451+
err = nf_tables_fill_table_info(skb, ctx->net, ctx->portid, ctx->seq,
452+
event, 0, ctx->afi->family, ctx->table);
453453
if (err < 0) {
454454
kfree_skb(skb);
455455
goto err;
@@ -488,7 +488,7 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
488488
if (idx > s_idx)
489489
memset(&cb->args[1], 0,
490490
sizeof(cb->args) - sizeof(cb->args[0]));
491-
if (nf_tables_fill_table_info(skb,
491+
if (nf_tables_fill_table_info(skb, net,
492492
NETLINK_CB(cb->skb).portid,
493493
cb->nlh->nlmsg_seq,
494494
NFT_MSG_NEWTABLE,
@@ -540,7 +540,7 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
540540
if (!skb2)
541541
return -ENOMEM;
542542

543-
err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
543+
err = nf_tables_fill_table_info(skb2, net, NETLINK_CB(skb).portid,
544544
nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
545545
family, table);
546546
if (err < 0)
@@ -914,9 +914,9 @@ static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
914914
return -ENOSPC;
915915
}
916916

917-
static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
918-
int event, u32 flags, int family,
919-
const struct nft_table *table,
917+
static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
918+
u32 portid, u32 seq, int event, u32 flags,
919+
int family, const struct nft_table *table,
920920
const struct nft_chain *chain)
921921
{
922922
struct nlmsghdr *nlh;
@@ -930,7 +930,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
930930
nfmsg = nlmsg_data(nlh);
931931
nfmsg->nfgen_family = family;
932932
nfmsg->version = NFNETLINK_V0;
933-
nfmsg->res_id = 0;
933+
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
934934

935935
if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
936936
goto nla_put_failure;
@@ -988,8 +988,8 @@ static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
988988
if (skb == NULL)
989989
goto err;
990990

991-
err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
992-
ctx->afi->family, ctx->table,
991+
err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
992+
event, 0, ctx->afi->family, ctx->table,
993993
ctx->chain);
994994
if (err < 0) {
995995
kfree_skb(skb);
@@ -1031,7 +1031,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
10311031
if (idx > s_idx)
10321032
memset(&cb->args[1], 0,
10331033
sizeof(cb->args) - sizeof(cb->args[0]));
1034-
if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
1034+
if (nf_tables_fill_chain_info(skb, net,
1035+
NETLINK_CB(cb->skb).portid,
10351036
cb->nlh->nlmsg_seq,
10361037
NFT_MSG_NEWCHAIN,
10371038
NLM_F_MULTI,
@@ -1090,7 +1091,7 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
10901091
if (!skb2)
10911092
return -ENOMEM;
10921093

1093-
err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
1094+
err = nf_tables_fill_chain_info(skb2, net, NETLINK_CB(skb).portid,
10941095
nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
10951096
family, table, chain);
10961097
if (err < 0)
@@ -1647,8 +1648,9 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
16471648
.len = NFT_USERDATA_MAXLEN },
16481649
};
16491650

1650-
static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
1651-
int event, u32 flags, int family,
1651+
static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
1652+
u32 portid, u32 seq, int event,
1653+
u32 flags, int family,
16521654
const struct nft_table *table,
16531655
const struct nft_chain *chain,
16541656
const struct nft_rule *rule)
@@ -1668,7 +1670,7 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
16681670
nfmsg = nlmsg_data(nlh);
16691671
nfmsg->nfgen_family = family;
16701672
nfmsg->version = NFNETLINK_V0;
1671-
nfmsg->res_id = 0;
1673+
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
16721674

16731675
if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
16741676
goto nla_put_failure;
@@ -1724,8 +1726,8 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx,
17241726
if (skb == NULL)
17251727
goto err;
17261728

1727-
err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
1728-
ctx->afi->family, ctx->table,
1729+
err = nf_tables_fill_rule_info(skb, ctx->net, ctx->portid, ctx->seq,
1730+
event, 0, ctx->afi->family, ctx->table,
17291731
ctx->chain, rule);
17301732
if (err < 0) {
17311733
kfree_skb(skb);
@@ -1771,7 +1773,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
17711773
if (idx > s_idx)
17721774
memset(&cb->args[1], 0,
17731775
sizeof(cb->args) - sizeof(cb->args[0]));
1774-
if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
1776+
if (nf_tables_fill_rule_info(skb, net, NETLINK_CB(cb->skb).portid,
17751777
cb->nlh->nlmsg_seq,
17761778
NFT_MSG_NEWRULE,
17771779
NLM_F_MULTI | NLM_F_APPEND,
@@ -1837,7 +1839,7 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
18371839
if (!skb2)
18381840
return -ENOMEM;
18391841

1840-
err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
1842+
err = nf_tables_fill_rule_info(skb2, net, NETLINK_CB(skb).portid,
18411843
nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
18421844
family, table, chain, rule);
18431845
if (err < 0)
@@ -2321,7 +2323,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
23212323
nfmsg = nlmsg_data(nlh);
23222324
nfmsg->nfgen_family = ctx->afi->family;
23232325
nfmsg->version = NFNETLINK_V0;
2324-
nfmsg->res_id = 0;
2326+
nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff);
23252327

23262328
if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
23272329
goto nla_put_failure;
@@ -2925,7 +2927,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
29252927
nfmsg = nlmsg_data(nlh);
29262928
nfmsg->nfgen_family = ctx.afi->family;
29272929
nfmsg->version = NFNETLINK_V0;
2928-
nfmsg->res_id = 0;
2930+
nfmsg->res_id = htons(ctx.net->nft.base_seq & 0xffff);
29292931

29302932
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
29312933
goto nla_put_failure;
@@ -3006,7 +3008,7 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
30063008
nfmsg = nlmsg_data(nlh);
30073009
nfmsg->nfgen_family = ctx->afi->family;
30083010
nfmsg->version = NFNETLINK_V0;
3009-
nfmsg->res_id = 0;
3011+
nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff);
30103012

30113013
if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
30123014
goto nla_put_failure;
@@ -3293,6 +3295,87 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
32933295
return err;
32943296
}
32953297

3298+
static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
3299+
u32 portid, u32 seq)
3300+
{
3301+
struct nlmsghdr *nlh;
3302+
struct nfgenmsg *nfmsg;
3303+
int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWGEN;
3304+
3305+
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0);
3306+
if (nlh == NULL)
3307+
goto nla_put_failure;
3308+
3309+
nfmsg = nlmsg_data(nlh);
3310+
nfmsg->nfgen_family = AF_UNSPEC;
3311+
nfmsg->version = NFNETLINK_V0;
3312+
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
3313+
3314+
if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)))
3315+
goto nla_put_failure;
3316+
3317+
return nlmsg_end(skb, nlh);
3318+
3319+
nla_put_failure:
3320+
nlmsg_trim(skb, nlh);
3321+
return -EMSGSIZE;
3322+
}
3323+
3324+
static int nf_tables_gen_notify(struct net *net, struct sk_buff *skb, int event)
3325+
{
3326+
struct nlmsghdr *nlh = nlmsg_hdr(skb);
3327+
struct sk_buff *skb2;
3328+
int err;
3329+
3330+
if (nlmsg_report(nlh) &&
3331+
!nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
3332+
return 0;
3333+
3334+
err = -ENOBUFS;
3335+
skb2 = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
3336+
if (skb2 == NULL)
3337+
goto err;
3338+
3339+
err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid,
3340+
nlh->nlmsg_seq);
3341+
if (err < 0) {
3342+
kfree_skb(skb2);
3343+
goto err;
3344+
}
3345+
3346+
err = nfnetlink_send(skb2, net, NETLINK_CB(skb).portid,
3347+
NFNLGRP_NFTABLES, nlmsg_report(nlh), GFP_KERNEL);
3348+
err:
3349+
if (err < 0) {
3350+
nfnetlink_set_err(net, NETLINK_CB(skb).portid, NFNLGRP_NFTABLES,
3351+
err);
3352+
}
3353+
return err;
3354+
}
3355+
3356+
static int nf_tables_getgen(struct sock *nlsk, struct sk_buff *skb,
3357+
const struct nlmsghdr *nlh,
3358+
const struct nlattr * const nla[])
3359+
{
3360+
struct net *net = sock_net(skb->sk);
3361+
struct sk_buff *skb2;
3362+
int err;
3363+
3364+
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
3365+
if (skb2 == NULL)
3366+
return -ENOMEM;
3367+
3368+
err = nf_tables_fill_gen_info(skb2, net, NETLINK_CB(skb).portid,
3369+
nlh->nlmsg_seq);
3370+
if (err < 0)
3371+
goto err;
3372+
3373+
return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
3374+
err:
3375+
kfree_skb(skb2);
3376+
return err;
3377+
}
3378+
32963379
static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
32973380
[NFT_MSG_NEWTABLE] = {
32983381
.call_batch = nf_tables_newtable,
@@ -3369,6 +3452,9 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
33693452
.attr_count = NFTA_SET_ELEM_LIST_MAX,
33703453
.policy = nft_set_elem_list_policy,
33713454
},
3455+
[NFT_MSG_GETGEN] = {
3456+
.call = nf_tables_getgen,
3457+
},
33723458
};
33733459

33743460
static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3526,6 +3612,8 @@ static int nf_tables_commit(struct sk_buff *skb)
35263612
call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
35273613
}
35283614

3615+
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
3616+
35293617
return 0;
35303618
}
35313619

0 commit comments

Comments
 (0)