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/netlink.h>
  14#include <linux/rculist.h>
  15#include <linux/slab.h>
  16#include <linux/types.h>
  17#include <linux/errno.h>
  18#include <net/netlink.h>
  19#include <net/sock.h>
  20#include <asm/atomic.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_u64(tb[NFACCT_BYTES])));
  83        }
  84        if (tb[NFACCT_PKTS]) {
  85                atomic64_set(&nfacct->pkts,
  86                             be64_to_cpu(nla_get_u64(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 pid, u32 seq, u32 type,
  95                   int event, struct nf_acct *acct)
  96{
  97        struct nlmsghdr *nlh;
  98        struct nfgenmsg *nfmsg;
  99        unsigned int flags = pid ? NLM_F_MULTI : 0;
 100        u64 pkts, bytes;
 101
 102        event |= NFNL_SUBSYS_ACCT << 8;
 103        nlh = nlmsg_put(skb, pid, 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        NLA_PUT_STRING(skb, NFACCT_NAME, acct->name);
 113
 114        if (type == NFNL_MSG_ACCT_GET_CTRZERO) {
 115                pkts = atomic64_xchg(&acct->pkts, 0);
 116                bytes = atomic64_xchg(&acct->bytes, 0);
 117        } else {
 118                pkts = atomic64_read(&acct->pkts);
 119                bytes = atomic64_read(&acct->bytes);
 120        }
 121        NLA_PUT_BE64(skb, NFACCT_PKTS, cpu_to_be64(pkts));
 122        NLA_PUT_BE64(skb, NFACCT_BYTES, cpu_to_be64(bytes));
 123        NLA_PUT_BE32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)));
 124
 125        nlmsg_end(skb, nlh);
 126        return skb->len;
 127
 128nlmsg_failure:
 129nla_put_failure:
 130        nlmsg_cancel(skb, nlh);
 131        return -1;
 132}
 133
 134static int
 135nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
 136{
 137        struct nf_acct *cur, *last;
 138
 139        if (cb->args[2])
 140                return 0;
 141
 142        last = (struct nf_acct *)cb->args[1];
 143        if (cb->args[1])
 144                cb->args[1] = 0;
 145
 146        rcu_read_lock();
 147        list_for_each_entry_rcu(cur, &nfnl_acct_list, head) {
 148                if (last && cur != last)
 149                        continue;
 150
 151                if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).pid,
 152                                       cb->nlh->nlmsg_seq,
 153                                       NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
 154                                       NFNL_MSG_ACCT_NEW, cur) < 0) {
 155                        cb->args[1] = (unsigned long)cur;
 156                        break;
 157                }
 158        }
 159        if (!cb->args[1])
 160                cb->args[2] = 1;
 161        rcu_read_unlock();
 162        return skb->len;
 163}
 164
 165static int
 166nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
 167             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 168{
 169        int ret = -ENOENT;
 170        struct nf_acct *cur;
 171        char *acct_name;
 172
 173        if (nlh->nlmsg_flags & NLM_F_DUMP) {
 174                return netlink_dump_start(nfnl, skb, nlh, nfnl_acct_dump,
 175                                          NULL, 0);
 176        }
 177
 178        if (!tb[NFACCT_NAME])
 179                return -EINVAL;
 180        acct_name = nla_data(tb[NFACCT_NAME]);
 181
 182        list_for_each_entry(cur, &nfnl_acct_list, head) {
 183                struct sk_buff *skb2;
 184
 185                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
 186                        continue;
 187
 188                skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 189                if (skb2 == NULL) {
 190                        ret = -ENOMEM;
 191                        break;
 192                }
 193
 194                ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).pid,
 195                                         nlh->nlmsg_seq,
 196                                         NFNL_MSG_TYPE(nlh->nlmsg_type),
 197                                         NFNL_MSG_ACCT_NEW, cur);
 198                if (ret <= 0) {
 199                        kfree_skb(skb2);
 200                        break;
 201                }
 202                ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid,
 203                                        MSG_DONTWAIT);
 204                if (ret > 0)
 205                        ret = 0;
 206
 207                /* this avoids a loop in nfnetlink. */
 208                return ret == -EAGAIN ? -ENOBUFS : ret;
 209        }
 210        return ret;
 211}
 212
 213/* try to delete object, fail if it is still in use. */
 214static int nfnl_acct_try_del(struct nf_acct *cur)
 215{
 216        int ret = 0;
 217
 218        /* we want to avoid races with nfnl_acct_find_get. */
 219        if (atomic_dec_and_test(&cur->refcnt)) {
 220                /* We are protected by nfnl mutex. */
 221                list_del_rcu(&cur->head);
 222                kfree_rcu(cur, rcu_head);
 223        } else {
 224                /* still in use, restore reference counter. */
 225                atomic_inc(&cur->refcnt);
 226                ret = -EBUSY;
 227        }
 228        return ret;
 229}
 230
 231static int
 232nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
 233             const struct nlmsghdr *nlh, const struct nlattr * const tb[])
 234{
 235        char *acct_name;
 236        struct nf_acct *cur;
 237        int ret = -ENOENT;
 238
 239        if (!tb[NFACCT_NAME]) {
 240                list_for_each_entry(cur, &nfnl_acct_list, head)
 241                        nfnl_acct_try_del(cur);
 242
 243                return 0;
 244        }
 245        acct_name = nla_data(tb[NFACCT_NAME]);
 246
 247        list_for_each_entry(cur, &nfnl_acct_list, head) {
 248                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0)
 249                        continue;
 250
 251                ret = nfnl_acct_try_del(cur);
 252                if (ret < 0)
 253                        return ret;
 254
 255                break;
 256        }
 257        return ret;
 258}
 259
 260static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
 261        [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
 262        [NFACCT_BYTES] = { .type = NLA_U64 },
 263        [NFACCT_PKTS] = { .type = NLA_U64 },
 264};
 265
 266static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = {
 267        [NFNL_MSG_ACCT_NEW]             = { .call = nfnl_acct_new,
 268                                            .attr_count = NFACCT_MAX,
 269                                            .policy = nfnl_acct_policy },
 270        [NFNL_MSG_ACCT_GET]             = { .call = nfnl_acct_get,
 271                                            .attr_count = NFACCT_MAX,
 272                                            .policy = nfnl_acct_policy },
 273        [NFNL_MSG_ACCT_GET_CTRZERO]     = { .call = nfnl_acct_get,
 274                                            .attr_count = NFACCT_MAX,
 275                                            .policy = nfnl_acct_policy },
 276        [NFNL_MSG_ACCT_DEL]             = { .call = nfnl_acct_del,
 277                                            .attr_count = NFACCT_MAX,
 278                                            .policy = nfnl_acct_policy },
 279};
 280
 281static const struct nfnetlink_subsystem nfnl_acct_subsys = {
 282        .name                           = "acct",
 283        .subsys_id                      = NFNL_SUBSYS_ACCT,
 284        .cb_count                       = NFNL_MSG_ACCT_MAX,
 285        .cb                             = nfnl_acct_cb,
 286};
 287
 288MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT);
 289
 290struct nf_acct *nfnl_acct_find_get(const char *acct_name)
 291{
 292        struct nf_acct *cur, *acct = NULL;
 293
 294        rcu_read_lock();
 295        list_for_each_entry_rcu(cur, &nfnl_acct_list, head) {
 296                if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
 297                        continue;
 298
 299                if (!try_module_get(THIS_MODULE))
 300                        goto err;
 301
 302                if (!atomic_inc_not_zero(&cur->refcnt)) {
 303                        module_put(THIS_MODULE);
 304                        goto err;
 305                }
 306
 307                acct = cur;
 308                break;
 309        }
 310err:
 311        rcu_read_unlock();
 312        return acct;
 313}
 314EXPORT_SYMBOL_GPL(nfnl_acct_find_get);
 315
 316void nfnl_acct_put(struct nf_acct *acct)
 317{
 318        atomic_dec(&acct->refcnt);
 319        module_put(THIS_MODULE);
 320}
 321EXPORT_SYMBOL_GPL(nfnl_acct_put);
 322
 323void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct)
 324{
 325        atomic64_inc(&nfacct->pkts);
 326        atomic64_add(skb->len, &nfacct->bytes);
 327}
 328EXPORT_SYMBOL_GPL(nfnl_acct_update);
 329
 330static int __init nfnl_acct_init(void)
 331{
 332        int ret;
 333
 334        pr_info("nfnl_acct: registering with nfnetlink.\n");
 335        ret = nfnetlink_subsys_register(&nfnl_acct_subsys);
 336        if (ret < 0) {
 337                pr_err("nfnl_acct_init: cannot register with nfnetlink.\n");
 338                goto err_out;
 339        }
 340        return 0;
 341err_out:
 342        return ret;
 343}
 344
 345static void __exit nfnl_acct_exit(void)
 346{
 347        struct nf_acct *cur, *tmp;
 348
 349        pr_info("nfnl_acct: unregistering from nfnetlink.\n");
 350        nfnetlink_subsys_unregister(&nfnl_acct_subsys);
 351
 352        list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) {
 353                list_del_rcu(&cur->head);
 354                /* We are sure that our objects have no clients at this point,
 355                 * it's safe to release them all without checking refcnt. */
 356                kfree_rcu(cur, rcu_head);
 357        }
 358}
 359
 360module_init(nfnl_acct_init);
 361module_exit(nfnl_acct_exit);
 362