netfilter: nf_tables: generalise transaction infrastructure
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 3 Apr 2014 23:24:07 +0000 (01:24 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 19 May 2014 10:06:10 +0000 (12:06 +0200)
This patch generalises the existing rule transaction infrastructure
so it can be used to handle set, table and chain object transactions
as well. The transaction provides a data area that stores private
information depending on the transaction type.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c

index 91505231a105402f7adce26451b291096642e712..246dbd48825f8b259dde157aa8343d80ac664df3 100644 (file)
@@ -387,18 +387,25 @@ struct nft_rule {
 };
 
 /**
- *     struct nft_rule_trans - nf_tables rule update in transaction
+ *     struct nft_trans - nf_tables object update in transaction
  *
  *     @list: used internally
- *     @ctx: rule context
- *     @rule: rule that needs to be updated
+ *     @ctx: transaction context
+ *     @data: internal information related to the transaction
  */
-struct nft_rule_trans {
+struct nft_trans {
        struct list_head                list;
        struct nft_ctx                  ctx;
+       char                            data[0];
+};
+
+struct nft_trans_rule {
        struct nft_rule                 *rule;
 };
 
+#define nft_trans_rule(trans)  \
+       (((struct nft_trans_rule *)trans->data)->rule)
+
 static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
 {
        return (struct nft_expr *)&rule->data[0];
index 3643bbc720bc121c62420e745175867a5efab186..424a6f3cb6c559c3ddaf8761ca9264368bc4864f 100644 (file)
@@ -105,6 +105,25 @@ static void nft_ctx_init(struct nft_ctx *ctx,
        ctx->nla   = nla;
 }
 
+static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, u32 size)
+{
+       struct nft_trans *trans;
+
+       trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
+       if (trans == NULL)
+               return NULL;
+
+       trans->ctx      = *ctx;
+
+       return trans;
+}
+
+static void nft_trans_destroy(struct nft_trans *trans)
+{
+       list_del(&trans->list);
+       kfree(trans);
+}
+
 /*
  * Tables
  */
@@ -1557,26 +1576,25 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
-#define NFT_RULE_MAXEXPRS      128
-
-static struct nft_expr_info *info;
-
-static struct nft_rule_trans *
-nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
+static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx,
+                                           struct nft_rule *rule)
 {
-       struct nft_rule_trans *rupd;
+       struct nft_trans *trans;
 
-       rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL);
-       if (rupd == NULL)
-              return NULL;
+       trans = nft_trans_alloc(ctx, sizeof(struct nft_trans_rule));
+       if (trans == NULL)
+               return NULL;
 
-       rupd->ctx = *ctx;
-       rupd->rule = rule;
-       list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
+       nft_trans_rule(trans) = rule;
+       list_add_tail(&trans->list, &ctx->net->nft.commit_list);
 
-       return rupd;
+       return trans;
 }
 
+#define NFT_RULE_MAXEXPRS      128
+
+static struct nft_expr_info *info;
+
 static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                             const struct nlmsghdr *nlh,
                             const struct nlattr * const nla[])
@@ -1587,7 +1605,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
        struct nft_table *table;
        struct nft_chain *chain;
        struct nft_rule *rule, *old_rule = NULL;
-       struct nft_rule_trans *repl = NULL;
+       struct nft_trans *trans = NULL;
        struct nft_expr *expr;
        struct nft_ctx ctx;
        struct nlattr *tmp;
@@ -1685,8 +1703,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
 
        if (nlh->nlmsg_flags & NLM_F_REPLACE) {
                if (nft_rule_is_active_next(net, old_rule)) {
-                       repl = nf_tables_trans_add(&ctx, old_rule);
-                       if (repl == NULL) {
+                       trans = nft_trans_rule_add(&ctx, old_rule);
+                       if (trans == NULL) {
                                err = -ENOMEM;
                                goto err2;
                        }
@@ -1708,7 +1726,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
                        list_add_rcu(&rule->list, &chain->rules);
        }
 
-       if (nf_tables_trans_add(&ctx, rule) == NULL) {
+       if (nft_trans_rule_add(&ctx, rule) == NULL) {
                err = -ENOMEM;
                goto err3;
        }
@@ -1716,11 +1734,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
 
 err3:
        list_del_rcu(&rule->list);
-       if (repl) {
-               list_del_rcu(&repl->rule->list);
-               list_del(&repl->list);
-               nft_rule_clear(net, repl->rule);
-               kfree(repl);
+       if (trans) {
+               list_del_rcu(&nft_trans_rule(trans)->list);
+               nft_rule_clear(net, nft_trans_rule(trans));
+               nft_trans_destroy(trans);
        }
 err2:
        nf_tables_rule_destroy(&ctx, rule);
@@ -1737,7 +1754,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
 {
        /* You cannot delete the same rule twice */
        if (nft_rule_is_active_next(ctx->net, rule)) {
-               if (nf_tables_trans_add(ctx, rule) == NULL)
+               if (nft_trans_rule_add(ctx, rule) == NULL)
                        return -ENOMEM;
                nft_rule_disactivate_next(ctx->net, rule);
                return 0;
@@ -1813,7 +1830,7 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
 static int nf_tables_commit(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
-       struct nft_rule_trans *rupd, *tmp;
+       struct nft_trans *trans, *next;
 
        /* Bump generation counter, invalidate any dump in progress */
        net->nft.genctr++;
@@ -1826,38 +1843,38 @@ static int nf_tables_commit(struct sk_buff *skb)
         */
        synchronize_rcu();
 
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
                /* This rule was inactive in the past and just became active.
                 * Clear the next bit of the genmask since its meaning has
                 * changed, now it is the future.
                 */
-               if (nft_rule_is_active(net, rupd->rule)) {
-                       nft_rule_clear(net, rupd->rule);
-                       nf_tables_rule_notify(skb, rupd->ctx.nlh,
-                                             rupd->ctx.table, rupd->ctx.chain,
-                                             rupd->rule, NFT_MSG_NEWRULE, 0,
-                                             rupd->ctx.afi->family);
-                       list_del(&rupd->list);
-                       kfree(rupd);
+               if (nft_rule_is_active(net, nft_trans_rule(trans))) {
+                       nft_rule_clear(net, nft_trans_rule(trans));
+                       nf_tables_rule_notify(skb, trans->ctx.nlh,
+                                             trans->ctx.table,
+                                             trans->ctx.chain,
+                                             nft_trans_rule(trans),
+                                             NFT_MSG_NEWRULE, 0,
+                                             trans->ctx.afi->family);
+                       nft_trans_destroy(trans);
                        continue;
                }
 
                /* This rule is in the past, get rid of it */
-               list_del_rcu(&rupd->rule->list);
-               nf_tables_rule_notify(skb, rupd->ctx.nlh,
-                                     rupd->ctx.table, rupd->ctx.chain,
-                                     rupd->rule, NFT_MSG_DELRULE, 0,
-                                     rupd->ctx.afi->family);
+               list_del_rcu(&nft_trans_rule(trans)->list);
+               nf_tables_rule_notify(skb, trans->ctx.nlh,
+                                     trans->ctx.table, trans->ctx.chain,
+                                     nft_trans_rule(trans), NFT_MSG_DELRULE,
+                                     0, trans->ctx.afi->family);
        }
 
        /* Make sure we don't see any packet traversing old rules */
        synchronize_rcu();
 
        /* Now we can safely release unused old rules */
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               kfree(rupd);
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               nft_trans_destroy(trans);
        }
 
        return 0;
@@ -1866,27 +1883,25 @@ static int nf_tables_commit(struct sk_buff *skb)
 static int nf_tables_abort(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
-       struct nft_rule_trans *rupd, *tmp;
+       struct nft_trans *trans, *next;
 
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               if (!nft_rule_is_active_next(net, rupd->rule)) {
-                       nft_rule_clear(net, rupd->rule);
-                       list_del(&rupd->list);
-                       kfree(rupd);
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               if (!nft_rule_is_active_next(net, nft_trans_rule(trans))) {
+                       nft_rule_clear(net, nft_trans_rule(trans));
+                       nft_trans_destroy(trans);
                        continue;
                }
 
                /* This rule is inactive, get rid of it */
-               list_del_rcu(&rupd->rule->list);
+               list_del_rcu(&nft_trans_rule(trans)->list);
        }
 
        /* Make sure we don't see any packet accessing aborted rules */
        synchronize_rcu();
 
-       list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
-               nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
-               list_del(&rupd->list);
-               kfree(rupd);
+       list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+               nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
+               nft_trans_destroy(trans);
        }
 
        return 0;