>From 5723a577c90810435937173f256dbccd015e3b34 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 28 Mar 2013 17:50:04 +0100 Subject: [PATCH] netfilter: nf_tables: add explicit begin operation for transactions This patch removes the NFT_RULE_F_COMMIT flag and it adds an explicit begin operation to start transactions, as suggested by Tomasz. You hit -EBUSY if another process started a transaction before you try to perform an incremental (ie. one single rule) or transactional update. Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 7 +--- net/netfilter/nf_tables_api.c | 59 +++++++++++++++++------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 76b215f..1461a42 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -37,6 +37,7 @@ enum nf_tables_msg_types { NFT_MSG_NEWSETELEM, NFT_MSG_GETSETELEM, NFT_MSG_DELSETELEM, + NFT_MSG_BEGIN, NFT_MSG_COMMIT, NFT_MSG_ABORT, NFT_MSG_MAX, @@ -88,18 +89,12 @@ enum nft_chain_attributes { }; #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) -enum { - NFT_RULE_F_COMMIT = (1 << 0), - NFT_RULE_F_MASK = NFT_RULE_F_COMMIT, -}; - enum nft_rule_attributes { NFTA_RULE_UNSPEC, NFTA_RULE_TABLE, NFTA_RULE_CHAIN, NFTA_RULE_HANDLE, NFTA_RULE_EXPRESSIONS, - NFTA_RULE_FLAGS, NFTA_RULE_COMPAT, __NFTA_RULE_MAX }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 57d28cb..57cfc74 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1264,7 +1264,6 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { .len = NFT_CHAIN_MAXNAMELEN - 1 }, [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, - [NFTA_RULE_FLAGS] = { .type = NLA_U32 }, [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, }; @@ -1523,12 +1522,6 @@ static int nf_tables_dirty_add(struct nft_rule *rule, const struct nft_ctx *ctx) { struct nft_rule_update *rupd; - /* Another socket owns the dirty list? */ - if (!ctx->net->nft.pid_owner) - ctx->net->nft.pid_owner = ctx->nlh->nlmsg_pid; - else if (ctx->net->nft.pid_owner != ctx->nlh->nlmsg_pid) - return -EBUSY; - rupd = kmalloc(sizeof(struct nft_rule_update), GFP_KERNEL); if (rupd == NULL) return -ENOMEM; @@ -1558,9 +1551,12 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, unsigned int size, i, n; int err, rem; bool create; - u32 flags = 0; u64 handle; + /* A transaction is happening, tell this process that it should retry */ + if (net->nft.pid_owner && net->nft.pid_owner != nlh->nlmsg_pid) + return -EBUSY; + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); @@ -1617,14 +1613,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, if (rule == NULL) goto err1; - if (nla[NFTA_RULE_FLAGS]) { - flags = ntohl(nla_get_be32(nla[NFTA_RULE_FLAGS])); - if (flags & ~NFT_RULE_F_MASK) - return -EINVAL; - - if (flags & NFT_RULE_F_COMMIT) - nft_rule_activate_next(net, rule); - } + if (net->nft.pid_owner) + nft_rule_activate_next(net, rule); rule->handle = handle; rule->dlen = size; @@ -1639,7 +1629,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, } if (nlh->nlmsg_flags & NLM_F_REPLACE) { - if (flags & NFT_RULE_F_COMMIT) { + if (net->nft.pid_owner) { nft_rule_disactivate_next(net, old_rule); list_add_tail_rcu(&rule->list, &chain->rules); } else { @@ -1651,7 +1641,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, else list_add_rcu(&rule->list, &chain->rules); - if (flags & NFT_RULE_F_COMMIT) { + if (net->nft.pid_owner) { err = nf_tables_dirty_add(rule, &ctx); if (err < 0) { list_del_rcu(&rule->list); @@ -1680,7 +1670,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule, u32 flags) { int ret = 0; - if (flags & NFT_RULE_F_COMMIT) { + if (ctx->net->nft.pid_owner) { /* Will be deleted already in the next generation */ if (!nft_rule_is_active_next(ctx->net, rule)) return -EBUSY; @@ -1712,6 +1702,10 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, struct nft_ctx ctx; u32 flags = 0; + /* A transaction is happening, tell this process that it should retry */ + if (net->nft.pid_owner && net->nft.pid_owner != nlh->nlmsg_pid) + return -EBUSY; + afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) return PTR_ERR(afi); @@ -1726,13 +1720,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); - if (nla[NFTA_RULE_FLAGS]) { - flags = ntohl(nla_get_be32(nla[NFTA_RULE_FLAGS])); - - if (flags & ~NFT_RULE_F_MASK) - return -EINVAL; - } - if (nla[NFTA_RULE_HANDLE]) { rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); if (IS_ERR(rule)) @@ -1750,6 +1737,21 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, return 0; } +static int nf_tables_begin(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + struct net *net = sock_net(skb->sk); + + /* Check if another process is performing a transaction */ + if (!net->nft.pid_owner) + net->nft.pid_owner = nlh->nlmsg_pid; + else if (net->nft.pid_owner != nlh->nlmsg_pid) + return -EBUSY; + + return 0; +} + static int nf_tables_commit(struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[]) @@ -2840,6 +2842,11 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { .attr_count = NFTA_SET_ELEM_LIST_MAX, .policy = nft_set_elem_list_policy, }, + [NFT_MSG_BEGIN] = { + .call = nf_tables_begin, + .attr_count = NFTA_TABLE_MAX, + .policy = nft_rule_policy, + }, [NFT_MSG_COMMIT] = { .call = nf_tables_commit, .attr_count = NFTA_TABLE_MAX, -- 1.7.10.4