aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2014-09-04 08:30:22 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2014-09-19 05:14:43 -0400
commit84d7fce693884897c6196cc98228a2ad56ae2a9a (patch)
treed29751d76c35bc6efb5042ba24486be823173dcb
parentfc04733a1a71af26bf30830571b71f5f2a354a06 (diff)
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 <pablo@netfilter.org>
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h16
-rw-r--r--net/netfilter/nf_tables_api.c140
2 files changed, 130 insertions, 26 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 66d66dd3ff79..b72ccfeaf865 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -51,6 +51,8 @@ enum nft_verdicts {
51 * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes) 51 * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes)
52 * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes) 52 * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes)
53 * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) 53 * @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)
54 */ 56 */
55enum nf_tables_msg_types { 57enum nf_tables_msg_types {
56 NFT_MSG_NEWTABLE, 58 NFT_MSG_NEWTABLE,
@@ -68,6 +70,8 @@ enum nf_tables_msg_types {
68 NFT_MSG_NEWSETELEM, 70 NFT_MSG_NEWSETELEM,
69 NFT_MSG_GETSETELEM, 71 NFT_MSG_GETSETELEM,
70 NFT_MSG_DELSETELEM, 72 NFT_MSG_DELSETELEM,
73 NFT_MSG_NEWGEN,
74 NFT_MSG_GETGEN,
71 NFT_MSG_MAX, 75 NFT_MSG_MAX,
72}; 76};
73 77
@@ -812,4 +816,16 @@ enum nft_masq_attributes {
812}; 816};
813#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1) 817#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1)
814 818
819/**
820 * enum nft_gen_attributes - nf_tables ruleset generation attributes
821 *
822 * @NFTA_GEN_ID: Ruleset generation ID (NLA_U32)
823 */
824enum 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
815#endif /* _LINUX_NF_TABLES_H */ 831#endif /* _LINUX_NF_TABLES_H */
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 82374601577e..a476b9962155 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -405,9 +405,9 @@ static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
405 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, 405 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
406}; 406};
407 407
408static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, 408static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
409 int event, u32 flags, int family, 409 u32 portid, u32 seq, int event, u32 flags,
410 const struct nft_table *table) 410 int family, const struct nft_table *table)
411{ 411{
412 struct nlmsghdr *nlh; 412 struct nlmsghdr *nlh;
413 struct nfgenmsg *nfmsg; 413 struct nfgenmsg *nfmsg;
@@ -420,7 +420,7 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
420 nfmsg = nlmsg_data(nlh); 420 nfmsg = nlmsg_data(nlh);
421 nfmsg->nfgen_family = family; 421 nfmsg->nfgen_family = family;
422 nfmsg->version = NFNETLINK_V0; 422 nfmsg->version = NFNETLINK_V0;
423 nfmsg->res_id = 0; 423 nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
424 424
425 if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || 425 if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
426 nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) || 426 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)
448 if (skb == NULL) 448 if (skb == NULL)
449 goto err; 449 goto err;
450 450
451 err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0, 451 err = nf_tables_fill_table_info(skb, ctx->net, ctx->portid, ctx->seq,
452 ctx->afi->family, ctx->table); 452 event, 0, ctx->afi->family, ctx->table);
453 if (err < 0) { 453 if (err < 0) {
454 kfree_skb(skb); 454 kfree_skb(skb);
455 goto err; 455 goto err;
@@ -488,7 +488,7 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
488 if (idx > s_idx) 488 if (idx > s_idx)
489 memset(&cb->args[1], 0, 489 memset(&cb->args[1], 0,
490 sizeof(cb->args) - sizeof(cb->args[0])); 490 sizeof(cb->args) - sizeof(cb->args[0]));
491 if (nf_tables_fill_table_info(skb, 491 if (nf_tables_fill_table_info(skb, net,
492 NETLINK_CB(cb->skb).portid, 492 NETLINK_CB(cb->skb).portid,
493 cb->nlh->nlmsg_seq, 493 cb->nlh->nlmsg_seq,
494 NFT_MSG_NEWTABLE, 494 NFT_MSG_NEWTABLE,
@@ -540,7 +540,7 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
540 if (!skb2) 540 if (!skb2)
541 return -ENOMEM; 541 return -ENOMEM;
542 542
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,
544 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0, 544 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
545 family, table); 545 family, table);
546 if (err < 0) 546 if (err < 0)
@@ -914,9 +914,9 @@ nla_put_failure:
914 return -ENOSPC; 914 return -ENOSPC;
915} 915}
916 916
917static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, 917static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
918 int event, u32 flags, int family, 918 u32 portid, u32 seq, int event, u32 flags,
919 const struct nft_table *table, 919 int family, const struct nft_table *table,
920 const struct nft_chain *chain) 920 const struct nft_chain *chain)
921{ 921{
922 struct nlmsghdr *nlh; 922 struct nlmsghdr *nlh;
@@ -930,7 +930,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
930 nfmsg = nlmsg_data(nlh); 930 nfmsg = nlmsg_data(nlh);
931 nfmsg->nfgen_family = family; 931 nfmsg->nfgen_family = family;
932 nfmsg->version = NFNETLINK_V0; 932 nfmsg->version = NFNETLINK_V0;
933 nfmsg->res_id = 0; 933 nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
934 934
935 if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) 935 if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
936 goto nla_put_failure; 936 goto nla_put_failure;
@@ -988,8 +988,8 @@ static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
988 if (skb == NULL) 988 if (skb == NULL)
989 goto err; 989 goto err;
990 990
991 err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0, 991 err = nf_tables_fill_chain_info(skb, ctx->net, ctx->portid, ctx->seq,
992 ctx->afi->family, ctx->table, 992 event, 0, ctx->afi->family, ctx->table,
993 ctx->chain); 993 ctx->chain);
994 if (err < 0) { 994 if (err < 0) {
995 kfree_skb(skb); 995 kfree_skb(skb);
@@ -1031,7 +1031,8 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
1031 if (idx > s_idx) 1031 if (idx > s_idx)
1032 memset(&cb->args[1], 0, 1032 memset(&cb->args[1], 0,
1033 sizeof(cb->args) - sizeof(cb->args[0])); 1033 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,
1035 cb->nlh->nlmsg_seq, 1036 cb->nlh->nlmsg_seq,
1036 NFT_MSG_NEWCHAIN, 1037 NFT_MSG_NEWCHAIN,
1037 NLM_F_MULTI, 1038 NLM_F_MULTI,
@@ -1090,7 +1091,7 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
1090 if (!skb2) 1091 if (!skb2)
1091 return -ENOMEM; 1092 return -ENOMEM;
1092 1093
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,
1094 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0, 1095 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
1095 family, table, chain); 1096 family, table, chain);
1096 if (err < 0) 1097 if (err < 0)
@@ -1647,8 +1648,9 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
1647 .len = NFT_USERDATA_MAXLEN }, 1648 .len = NFT_USERDATA_MAXLEN },
1648}; 1649};
1649 1650
1650static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, 1651static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
1651 int event, u32 flags, int family, 1652 u32 portid, u32 seq, int event,
1653 u32 flags, int family,
1652 const struct nft_table *table, 1654 const struct nft_table *table,
1653 const struct nft_chain *chain, 1655 const struct nft_chain *chain,
1654 const struct nft_rule *rule) 1656 const struct nft_rule *rule)
@@ -1668,7 +1670,7 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
1668 nfmsg = nlmsg_data(nlh); 1670 nfmsg = nlmsg_data(nlh);
1669 nfmsg->nfgen_family = family; 1671 nfmsg->nfgen_family = family;
1670 nfmsg->version = NFNETLINK_V0; 1672 nfmsg->version = NFNETLINK_V0;
1671 nfmsg->res_id = 0; 1673 nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
1672 1674
1673 if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) 1675 if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
1674 goto nla_put_failure; 1676 goto nla_put_failure;
@@ -1724,8 +1726,8 @@ static int nf_tables_rule_notify(const struct nft_ctx *ctx,
1724 if (skb == NULL) 1726 if (skb == NULL)
1725 goto err; 1727 goto err;
1726 1728
1727 err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0, 1729 err = nf_tables_fill_rule_info(skb, ctx->net, ctx->portid, ctx->seq,
1728 ctx->afi->family, ctx->table, 1730 event, 0, ctx->afi->family, ctx->table,
1729 ctx->chain, rule); 1731 ctx->chain, rule);
1730 if (err < 0) { 1732 if (err < 0) {
1731 kfree_skb(skb); 1733 kfree_skb(skb);
@@ -1771,7 +1773,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
1771 if (idx > s_idx) 1773 if (idx > s_idx)
1772 memset(&cb->args[1], 0, 1774 memset(&cb->args[1], 0,
1773 sizeof(cb->args) - sizeof(cb->args[0])); 1775 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,
1775 cb->nlh->nlmsg_seq, 1777 cb->nlh->nlmsg_seq,
1776 NFT_MSG_NEWRULE, 1778 NFT_MSG_NEWRULE,
1777 NLM_F_MULTI | NLM_F_APPEND, 1779 NLM_F_MULTI | NLM_F_APPEND,
@@ -1837,7 +1839,7 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
1837 if (!skb2) 1839 if (!skb2)
1838 return -ENOMEM; 1840 return -ENOMEM;
1839 1841
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,
1841 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, 1843 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
1842 family, table, chain, rule); 1844 family, table, chain, rule);
1843 if (err < 0) 1845 if (err < 0)
@@ -2321,7 +2323,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
2321 nfmsg = nlmsg_data(nlh); 2323 nfmsg = nlmsg_data(nlh);
2322 nfmsg->nfgen_family = ctx->afi->family; 2324 nfmsg->nfgen_family = ctx->afi->family;
2323 nfmsg->version = NFNETLINK_V0; 2325 nfmsg->version = NFNETLINK_V0;
2324 nfmsg->res_id = 0; 2326 nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff);
2325 2327
2326 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) 2328 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2327 goto nla_put_failure; 2329 goto nla_put_failure;
@@ -2925,7 +2927,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
2925 nfmsg = nlmsg_data(nlh); 2927 nfmsg = nlmsg_data(nlh);
2926 nfmsg->nfgen_family = ctx.afi->family; 2928 nfmsg->nfgen_family = ctx.afi->family;
2927 nfmsg->version = NFNETLINK_V0; 2929 nfmsg->version = NFNETLINK_V0;
2928 nfmsg->res_id = 0; 2930 nfmsg->res_id = htons(ctx.net->nft.base_seq & 0xffff);
2929 2931
2930 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name)) 2932 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
2931 goto nla_put_failure; 2933 goto nla_put_failure;
@@ -3006,7 +3008,7 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
3006 nfmsg = nlmsg_data(nlh); 3008 nfmsg = nlmsg_data(nlh);
3007 nfmsg->nfgen_family = ctx->afi->family; 3009 nfmsg->nfgen_family = ctx->afi->family;
3008 nfmsg->version = NFNETLINK_V0; 3010 nfmsg->version = NFNETLINK_V0;
3009 nfmsg->res_id = 0; 3011 nfmsg->res_id = htons(ctx->net->nft.base_seq & 0xffff);
3010 3012
3011 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) 3013 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
3012 goto nla_put_failure; 3014 goto nla_put_failure;
@@ -3293,6 +3295,87 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
3293 return err; 3295 return err;
3294} 3296}
3295 3297
3298static 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
3319nla_put_failure:
3320 nlmsg_trim(skb, nlh);
3321 return -EMSGSIZE;
3322}
3323
3324static 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);
3348err:
3349 if (err < 0) {
3350 nfnetlink_set_err(net, NETLINK_CB(skb).portid, NFNLGRP_NFTABLES,
3351 err);
3352 }
3353 return err;
3354}
3355
3356static 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);
3374err:
3375 kfree_skb(skb2);
3376 return err;
3377}
3378
3296static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { 3379static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
3297 [NFT_MSG_NEWTABLE] = { 3380 [NFT_MSG_NEWTABLE] = {
3298 .call_batch = nf_tables_newtable, 3381 .call_batch = nf_tables_newtable,
@@ -3369,6 +3452,9 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
3369 .attr_count = NFTA_SET_ELEM_LIST_MAX, 3452 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3370 .policy = nft_set_elem_list_policy, 3453 .policy = nft_set_elem_list_policy,
3371 }, 3454 },
3455 [NFT_MSG_GETGEN] = {
3456 .call = nf_tables_getgen,
3457 },
3372}; 3458};
3373 3459
3374static void nft_chain_commit_update(struct nft_trans *trans) 3460static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3526,6 +3612,8 @@ static int nf_tables_commit(struct sk_buff *skb)
3526 call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); 3612 call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
3527 } 3613 }
3528 3614
3615 nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
3616
3529 return 0; 3617 return 0;
3530} 3618}
3531 3619