netfilter: nf_tables: add set_elem notifications
authorArturo Borrero <arturo.borrero.glez@gmail.com>
Tue, 1 Apr 2014 12:06:07 +0000 (14:06 +0200)
committerPablo Neira Ayuso <pablo@gnumonks.org>
Thu, 3 Apr 2014 10:22:25 +0000 (12:22 +0200)
This patch adds set_elems notifications. When a set_elem is
added/deleted, all listening peers in userspace will receive the
corresponding notification.

Signed-off-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@gnumonks.org>
net/netfilter/nf_tables_api.c

index bd3381e16084716f4b18cecabd38efc38ce5835c..60feca9aa70a0c0f5e3a8115287028c6bdc5a035 100644 (file)
@@ -2807,6 +2807,85 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
        return -EOPNOTSUPP;
 }
 
+static int nf_tables_fill_setelem_info(struct sk_buff *skb,
+                                      const struct nft_ctx *ctx, u32 seq,
+                                      u32 portid, int event, u16 flags,
+                                      const struct nft_set *set,
+                                      const struct nft_set_elem *elem)
+{
+       struct nfgenmsg *nfmsg;
+       struct nlmsghdr *nlh;
+       struct nlattr *nest;
+       int err;
+
+       event |= NFNL_SUBSYS_NFTABLES << 8;
+       nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
+                       flags);
+       if (nlh == NULL)
+               goto nla_put_failure;
+
+       nfmsg = nlmsg_data(nlh);
+       nfmsg->nfgen_family     = ctx->afi->family;
+       nfmsg->version          = NFNETLINK_V0;
+       nfmsg->res_id           = 0;
+
+       if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
+               goto nla_put_failure;
+       if (nla_put_string(skb, NFTA_SET_NAME, set->name))
+               goto nla_put_failure;
+
+       nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
+       if (nest == NULL)
+               goto nla_put_failure;
+
+       err = nf_tables_fill_setelem(skb, set, elem);
+       if (err < 0)
+               goto nla_put_failure;
+
+       nla_nest_end(skb, nest);
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_trim(skb, nlh);
+       return -1;
+}
+
+static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
+                                   const struct nft_set *set,
+                                   const struct nft_set_elem *elem,
+                                   int event, u16 flags)
+{
+       const struct sk_buff *oskb = ctx->skb;
+       struct net *net = sock_net(oskb->sk);
+       u32 portid = NETLINK_CB(oskb).portid;
+       bool report = nlmsg_report(ctx->nlh);
+       struct sk_buff *skb;
+       int err;
+
+       if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
+               return 0;
+
+       err = -ENOBUFS;
+       skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (skb == NULL)
+               goto err;
+
+       err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
+                                         set, elem);
+       if (err < 0) {
+               kfree_skb(skb);
+               goto err;
+       }
+
+       err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
+                            GFP_KERNEL);
+err:
+       if (err < 0)
+               nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
+       return err;
+}
+
 static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
@@ -2887,6 +2966,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
                goto err3;
        set->nelems++;
 
+       nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_NEWSETELEM, 0);
        return 0;
 
 err3:
@@ -2957,6 +3037,8 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
        set->ops->remove(set, &elem);
        set->nelems--;
 
+       nf_tables_setelem_notify(ctx, set, &elem, NFT_MSG_DELSETELEM, 0);
+
        nft_data_uninit(&elem.key, NFT_DATA_VALUE);
        if (set->flags & NFT_SET_MAP)
                nft_data_uninit(&elem.data, set->dtype);