linux/net/netfilter/nfnetlink_acct.c
<<
>>
Prefs
   1/*
   2 * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org>
   3 * (C) 2011 Intra2net AG <http://www.intra2net.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation (or any later at your option).
   8 */
   9#include <linux/init.h>
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/skbuff.h>
  13#include <linux/atomic.h>
  14#include <linux/netlink.h>
  15#include <linux/rculist.h>
  16#include <linux/slab.h>
  17#include <linux/types.h>
  18#include <linux/errno.h>
  19#include <net/netlink.h>
  20#include <net/sock.h>
  21
  22#include <linux/netfilter.h>
  23#include <linux/netfilter/nfnetlink.h>
  24#include <linux/netfilter/nfnetlink_acct.h>
  25
  26MODULE_LICENSE("GPL");
  27MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
  28MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure");
  29
  30static LIST_HEAD(nfnl_acct_list);
  31
  32struct nf_acct {
  33        atomic64_t              pkts;
  34        atomic64_t              bytes;
  35        struct list_head        head;
  36        atomic_t                refcnt;
  37        char                    name[NFACCT_NAME_MAX];
  38        struct rcu_head         rcu_head;
  39};
  40
  41static int
  42nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
  43             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
  44{
  45        struct nf_acct *nfacct, *matching = NULL;
  46        char *acct_name;
  47
  48        if (!tb[NFACCT_NAME])
  49                return -EINVAL;
  50
  51        acct_name = nla_data(tb[NFACCT_NAME]);
  52
  53        list_for_each_entry(nfacct, &nfnl_acct_list, head) {
  54                if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0)
  55                        continue;
  56
  57                if (nlh->nlmsg_flags & NLM_F_EXCL)
  58                        return -EEXIST;
  59
  60                matching = nfacct;
  61                break;
  62        }
  63
  64        if (matching) {
  65                if (nlh->nlmsg_flags & NLM_F_REPLACE) {
  66                        /* reset counters if you request a replacement. */
  67                        atomic64_set(&matching->pkts, 0);
  68                        atomic64_set(&matching->bytes, 0);
  69                        return 0;
  70                }
  71                return -EBUSY;
  72        }
  73
  74        nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL);
  75        if (nfacct == NULL)
  76                return -ENOMEM;
  77
  78        strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX);
  79
  80        if (tb[NFACCT_BYTES]) {
  81                atomic64_set(&nfacct->bytes,
  82                             be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES])));
  83        }
  84        if (tb[NFACCT_PKTS]) {
  85                atomic64_set(&nfacct->pkts,
  86                             be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS])));
  87        }
  88        atomic_set(&nfacct->refcnt, 1);
  89        list_add_tail_rcu(&nfacct->head, &nfnl_acct_list);
  90        return 0;
  91}
  92
  93static int
  94nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
  95                   int event, struct nf_acct *acct)
  96{
  97        struct nlmsghdr *nlh;
  98        struct nfgenmsg *nfmsg;
  99        unsigned int flags = portid ? NLM_F_MULTI : 0;
 100        u64 pkts, bytes;
 101
 102        event |= NFNL_SUBSYS_ACCT << 8;
 103        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags);
 104        if (nlh == NULL)
 105                goto nlmsg_failure;
 106
 107        nfmsg = nlmsg_data(nlh);
 108        nfmsg->nfgen_family = AF_UNSPEC;
 109        nfmsg->version = NFNETLINK_V0;
 110        nfmsg->res_id = 0;
 111
 112        if (nla_put_string(skb, NFACCT_NAME, acct->name))
 113                goto nla_put_failure;
 114
 115        if (type == NFNL_MSG_ACCT_GET_CTRZERO) {
 116                pkts = atomic64_xchg(&acct->pkts, 0);
 117                bytes = atomic64_xchg(&acct->bytes, 0);
 118        } else {
 119                pkts = atomic64_read(&acct->pkts);
 120                bytes = atomic64_read(&acct->bytes);
 121        }
 122        if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) ||
 123            nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) ||
 124            nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt))))
 125                goto nla_put_failure;
 126
 127        nlmsg_end(skb, nlh);
 128        return skb->len;
 129
 130nlmsg_failure:
 131nla_put_failure:
 132        nlmsg_cancel(skb, nlh);
 133        return -1;
 134}
 135
 136static int
 137nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
 138{
 139        struct nf_acct *cur, *last;
 140
 141        if (cb->args[2])
 142                return 0;
 143
 144        last = (struct nf_acct *)cb->args[1];
 145        if (cb->args[1])
 146                cb->args[1] = 0;
 147
 148        rcu_read_lock();
 149        list_for_each_entry_rcu(cur, &nfnl_acct_list, head) {
 150                if (last && cur != last)
 151                        continue;
 152
 153                if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid,
 154                                       cb->nlh->nlmsg_seq,
 155                                       NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
 156                                       NFNL_MSG_ACCT_NEW, cur) < 0) {
 157                        cb->args[1] = (unsigned long)cur;
 158                        break;
 159                }
 160        }
 161        if (!cb->args[1])
 162                cb->args[2] = 1;
 163        rcu_read_unlock();
 164        return skb->len;
 165}
 166
 167static int
 168nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
 169             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 170{
 171        int ret = -ENOENT;
 172        struct nf_acct *cur;
 173        char *acct_name;
 174
 175        if (nlh->nlmsg_flags & NLM_F_DUMP) {
 176                struct netlink_dump_control c = {
 177                        .dump = nfnl_acct_dump,
 178                };
 179                return netlink_dump_start(nfnl, skb, nlh, &c);
 180        }
 181
 182        if (!tb[NFACCT_NAME])
 183                return -EINVAL;
 184        acct_name = nla_data(tb[NFACCT_NAME]);
 185
 186        list_for_each_entry(cur, &nfnl_acct_list, head) {
 187                struct sk_buff *skb2;
 188
 189                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
 190                        continue;
 191
 192                skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 193                if (skb2 == NULL) {
 194                        ret = -ENOMEM;
 195                        break;
 196                }
 197
 198                ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid,
 199                                         nlh->nlmsg_seq,
 200                                         NFNL_MSG_TYPE(nlh->nlmsg_type),
 201                                         NFNL_MSG_ACCT_NEW, cur);
 202                if (ret <= 0) {
 203                        kfree_skb(skb2);
 204                        break;
 205                }
 206                ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid,
 207                                        MSG_DONTWAIT);
 208                if (ret > 0)
 209                        ret = 0;
 210
 211                /* this avoids a loop in nfnetlink. */
 212                return ret == -EAGAIN ? -ENOBUFS : ret;
 213        }
 214        return ret;
 215}
 216
 217/* try to delete object, fail if it is still in use. */
 218static int nfnl_acct_try_del(struct nf_acct *cur)
 219{
 220        int ret = 0;
 221
 222        /* we want to avoid races with nfnl_acct_find_get. */
 223        if (atomic_dec_and_test(&cur->refcnt)) {
 224                /* We are protected by nfnl mutex. */
 225                list_del_rcu(&cur->head);
 226                kfree_rcu(cur, rcu_head);
 227        } else {
 228                /* still in use, restore reference counter. */
 229                atomic_inc(&cur->refcnt);
 230                ret = -EBUSY;
 231        }
 232        return ret;
 233}
 234
 235static int
 236nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
 237             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 238{
 239        char *acct_name;
 240        struct nf_acct *cur;
 241        int ret = -ENOENT;
 242
 243        if (!tb[NFACCT_NAME]) {
 244                list_for_each_entry(cur, &nfnl_acct_list, head)
 245                        nfnl_acct_try_del(cur);
 246
 247                return 0;
 248        }
 249        acct_name = nla_data(tb[NFACCT_NAME]);
 250
 251        list_for_each_entry(cur, &nfnl_acct_list, head) {
 252                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0)
 253                        continue;
 254
 255                ret = nfnl_acct_try_del(cur);
 256                if (ret < 0)
 257                        return ret;
 258
 259                break;
 260        }
 261        return ret;
 262}
 263
 264static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
 265        [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
 266        [NFACCT_BYTES] = { .type = NLA_U64 },
 267        [NFACCT_PKTS] = { .type = NLA_U64 },
 268};
 269
 270static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = {
 271        [NFNL_MSG_ACCT_NEW]             = { .call = nfnl_acct_new,
 272                                            .attr_count = NFACCT_MAX,
 273                                            .policy = nfnl_acct_policy },
 274        [NFNL_MSG_ACCT_GET]             = { .call = nfnl_acct_get,
 275                                            .attr_count = NFACCT_MAX,
 276                                            .policy = nfnl_acct_policy },
 277        [NFNL_MSG_ACCT_GET_CTRZERO]     = { .call = nfnl_acct_get,
 278                                            .attr_count = NFACCT_MAX,
 279                                            .policy = nfnl_acct_policy },
 280        [NFNL_MSG_ACCT_DEL]             = { .call = nfnl_acct_del,
 281                                            .attr_count = NFACCT_MAX,
 282                                            .policy = nfnl_acct_policy },
 283};
 284
 285static const struct nfnetlink_subsystem nfnl_acct_subsys = {
 286        .name                           = "acct",
 287        .subsys_id                      = NFNL_SUBSYS_ACCT,
 288        .cb_count                       = NFNL_MSG_ACCT_MAX,
 289        .cb                             = nfnl_acct_cb,
 290};
 291
 292MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT);
 293
 294struct nf_acct *nfnl_acct_find_get(const char *acct_name)
 295{
 296        struct nf_acct *cur, *acct = NULL;
 297
 298        rcu_read_lock();
 299        list_for_each_entry_rcu(cur, &nfnl_acct_list, head) {
 300                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
 301                        continue;
 302
 303                if (!try_module_get(THIS_MODULE))
 304                        goto err;
 305
 306                if (!atomic_inc_not_zero(&cur->refcnt)) {
 307                        module_put(THIS_MODULE);
 308                        goto err;
 309                }
 310
 311                acct = cur;
 312                break;
 313        }
 314err:
 315        rcu_read_unlock();
 316        return acct;
 317}
 318EXPORT_SYMBOL_GPL(nfnl_acct_find_get);
 319
 320void nfnl_acct_put(struct nf_acct *acct)
 321{
 322        atomic_dec(&acct->refcnt);
 323        module_put(THIS_MODULE);
 324}
 325EXPORT_SYMBOL_GPL(nfnl_acct_put);
 326
 327void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct)
 328{
 329        atomic64_inc(&nfacct->pkts);
 330        atomic64_add(skb->len, &nfacct->bytes);
 331}
 332EXPORT_SYMBOL_GPL(nfnl_acct_update);
 333
 334static int __init nfnl_acct_init(void)
 335{
 336        int ret;
 337
 338        pr_info("nfnl_acct: registering with nfnetlink.\n");
 339        ret = nfnetlink_subsys_register(&nfnl_acct_subsys);
 340        if (ret < 0) {
 341                pr_err("nfnl_acct_init: cannot register with nfnetlink.\n");
 342                goto err_out;
 343        }
 344        return 0;
 345err_out:
 346        return ret;
 347}
 348
 349static void __exit nfnl_acct_exit(void)
 350{
 351        struct nf_acct *cur, *tmp;
 352
 353        pr_info("nfnl_acct: unregistering from nfnetlink.\n");
 354        nfnetlink_subsys_unregister(&nfnl_acct_subsys);
 355
 356        list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) {
 357                list_del_rcu(&cur->head);
 358                /* We are sure that our objects have no clients at this point,
 359                 * it's safe to release them all without checking refcnt. */
 360                kfree_rcu(cur, rcu_head);
 361        }
 362}
 363
 364module_init(nfnl_acct_init);
 365module_exit(nfnl_acct_exit);
 366
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.