194139027SPablo Neira Ayuso /* 294139027SPablo Neira Ayuso * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org> 394139027SPablo Neira Ayuso * (C) 2011 Intra2net AG <http://www.intra2net.com> 494139027SPablo Neira Ayuso * 594139027SPablo Neira Ayuso * This program is free software; you can redistribute it and/or modify 694139027SPablo Neira Ayuso * it under the terms of the GNU General Public License version 2 as 794139027SPablo Neira Ayuso * published by the Free Software Foundation (or any later at your option). 894139027SPablo Neira Ayuso */ 994139027SPablo Neira Ayuso #include <linux/init.h> 1094139027SPablo Neira Ayuso #include <linux/module.h> 1194139027SPablo Neira Ayuso #include <linux/kernel.h> 1294139027SPablo Neira Ayuso #include <linux/skbuff.h> 136523cf9aSAndrew Morton #include <linux/atomic.h> 1494139027SPablo Neira Ayuso #include <linux/netlink.h> 1594139027SPablo Neira Ayuso #include <linux/rculist.h> 1694139027SPablo Neira Ayuso #include <linux/slab.h> 1794139027SPablo Neira Ayuso #include <linux/types.h> 1894139027SPablo Neira Ayuso #include <linux/errno.h> 1994139027SPablo Neira Ayuso #include <net/netlink.h> 2094139027SPablo Neira Ayuso #include <net/sock.h> 2194139027SPablo Neira Ayuso 2294139027SPablo Neira Ayuso #include <linux/netfilter.h> 2394139027SPablo Neira Ayuso #include <linux/netfilter/nfnetlink.h> 2494139027SPablo Neira Ayuso #include <linux/netfilter/nfnetlink_acct.h> 2594139027SPablo Neira Ayuso 2694139027SPablo Neira Ayuso MODULE_LICENSE("GPL"); 2794139027SPablo Neira Ayuso MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 2894139027SPablo Neira Ayuso MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure"); 2994139027SPablo Neira Ayuso 3094139027SPablo Neira Ayuso static LIST_HEAD(nfnl_acct_list); 3194139027SPablo Neira Ayuso 3294139027SPablo Neira Ayuso struct nf_acct { 3394139027SPablo Neira Ayuso atomic64_t pkts; 3494139027SPablo Neira Ayuso atomic64_t bytes; 35683399edSMathieu Poirier unsigned long flags; 3694139027SPablo Neira Ayuso struct list_head head; 3794139027SPablo Neira Ayuso atomic_t refcnt; 3894139027SPablo Neira Ayuso char name[NFACCT_NAME_MAX]; 3994139027SPablo Neira Ayuso struct rcu_head rcu_head; 40683399edSMathieu Poirier char data[0]; 4194139027SPablo Neira Ayuso }; 4294139027SPablo Neira Ayuso 43683399edSMathieu Poirier #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) 44683399edSMathieu Poirier 4594139027SPablo Neira Ayuso static int 4694139027SPablo Neira Ayuso nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, 4794139027SPablo Neira Ayuso const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 4894139027SPablo Neira Ayuso { 4994139027SPablo Neira Ayuso struct nf_acct *nfacct, *matching = NULL; 5094139027SPablo Neira Ayuso char *acct_name; 51683399edSMathieu Poirier unsigned int size = 0; 52683399edSMathieu Poirier u32 flags = 0; 5394139027SPablo Neira Ayuso 5494139027SPablo Neira Ayuso if (!tb[NFACCT_NAME]) 5594139027SPablo Neira Ayuso return -EINVAL; 5694139027SPablo Neira Ayuso 5794139027SPablo Neira Ayuso acct_name = nla_data(tb[NFACCT_NAME]); 58deadcfc3SPablo Neira Ayuso if (strlen(acct_name) == 0) 59deadcfc3SPablo Neira Ayuso return -EINVAL; 6094139027SPablo Neira Ayuso 6194139027SPablo Neira Ayuso list_for_each_entry(nfacct, &nfnl_acct_list, head) { 6294139027SPablo Neira Ayuso if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) 6394139027SPablo Neira Ayuso continue; 6494139027SPablo Neira Ayuso 6594139027SPablo Neira Ayuso if (nlh->nlmsg_flags & NLM_F_EXCL) 6694139027SPablo Neira Ayuso return -EEXIST; 6794139027SPablo Neira Ayuso 6894139027SPablo Neira Ayuso matching = nfacct; 6994139027SPablo Neira Ayuso break; 7094139027SPablo Neira Ayuso } 7194139027SPablo Neira Ayuso 7294139027SPablo Neira Ayuso if (matching) { 7394139027SPablo Neira Ayuso if (nlh->nlmsg_flags & NLM_F_REPLACE) { 7494139027SPablo Neira Ayuso /* reset counters if you request a replacement. */ 7594139027SPablo Neira Ayuso atomic64_set(&matching->pkts, 0); 7694139027SPablo Neira Ayuso atomic64_set(&matching->bytes, 0); 77f9da455bSLinus Torvalds smp_mb__before_atomic(); 78683399edSMathieu Poirier /* reset overquota flag if quota is enabled. */ 79683399edSMathieu Poirier if ((matching->flags & NFACCT_F_QUOTA)) 80683399edSMathieu Poirier clear_bit(NFACCT_F_OVERQUOTA, &matching->flags); 8194139027SPablo Neira Ayuso return 0; 8294139027SPablo Neira Ayuso } 8394139027SPablo Neira Ayuso return -EBUSY; 8494139027SPablo Neira Ayuso } 8594139027SPablo Neira Ayuso 86683399edSMathieu Poirier if (tb[NFACCT_FLAGS]) { 87683399edSMathieu Poirier flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); 88683399edSMathieu Poirier if (flags & ~NFACCT_F_QUOTA) 89683399edSMathieu Poirier return -EOPNOTSUPP; 90683399edSMathieu Poirier if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) 91683399edSMathieu Poirier return -EINVAL; 92683399edSMathieu Poirier if (flags & NFACCT_F_OVERQUOTA) 93683399edSMathieu Poirier return -EINVAL; 94683399edSMathieu Poirier 95683399edSMathieu Poirier size += sizeof(u64); 96683399edSMathieu Poirier } 97683399edSMathieu Poirier 98683399edSMathieu Poirier nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); 9994139027SPablo Neira Ayuso if (nfacct == NULL) 10094139027SPablo Neira Ayuso return -ENOMEM; 10194139027SPablo Neira Ayuso 102683399edSMathieu Poirier if (flags & NFACCT_F_QUOTA) { 103683399edSMathieu Poirier u64 *quota = (u64 *)nfacct->data; 104683399edSMathieu Poirier 105683399edSMathieu Poirier *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); 106683399edSMathieu Poirier nfacct->flags = flags; 107683399edSMathieu Poirier } 108683399edSMathieu Poirier 10994139027SPablo Neira Ayuso strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); 11094139027SPablo Neira Ayuso 11194139027SPablo Neira Ayuso if (tb[NFACCT_BYTES]) { 11294139027SPablo Neira Ayuso atomic64_set(&nfacct->bytes, 113fe31d1a8SPatrick McHardy be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES]))); 11494139027SPablo Neira Ayuso } 11594139027SPablo Neira Ayuso if (tb[NFACCT_PKTS]) { 11694139027SPablo Neira Ayuso atomic64_set(&nfacct->pkts, 117fe31d1a8SPatrick McHardy be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); 11894139027SPablo Neira Ayuso } 11994139027SPablo Neira Ayuso atomic_set(&nfacct->refcnt, 1); 12094139027SPablo Neira Ayuso list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); 12194139027SPablo Neira Ayuso return 0; 12294139027SPablo Neira Ayuso } 12394139027SPablo Neira Ayuso 12494139027SPablo Neira Ayuso static int 12515e47304SEric W. Biederman nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, 12694139027SPablo Neira Ayuso int event, struct nf_acct *acct) 12794139027SPablo Neira Ayuso { 12894139027SPablo Neira Ayuso struct nlmsghdr *nlh; 12994139027SPablo Neira Ayuso struct nfgenmsg *nfmsg; 13015e47304SEric W. Biederman unsigned int flags = portid ? NLM_F_MULTI : 0; 13194139027SPablo Neira Ayuso u64 pkts, bytes; 13294139027SPablo Neira Ayuso 13394139027SPablo Neira Ayuso event |= NFNL_SUBSYS_ACCT << 8; 13415e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); 13594139027SPablo Neira Ayuso if (nlh == NULL) 13694139027SPablo Neira Ayuso goto nlmsg_failure; 13794139027SPablo Neira Ayuso 13894139027SPablo Neira Ayuso nfmsg = nlmsg_data(nlh); 13994139027SPablo Neira Ayuso nfmsg->nfgen_family = AF_UNSPEC; 14094139027SPablo Neira Ayuso nfmsg->version = NFNETLINK_V0; 14194139027SPablo Neira Ayuso nfmsg->res_id = 0; 14294139027SPablo Neira Ayuso 1437c801189SDavid S. Miller if (nla_put_string(skb, NFACCT_NAME, acct->name)) 1447c801189SDavid S. Miller goto nla_put_failure; 14594139027SPablo Neira Ayuso 14694139027SPablo Neira Ayuso if (type == NFNL_MSG_ACCT_GET_CTRZERO) { 14794139027SPablo Neira Ayuso pkts = atomic64_xchg(&acct->pkts, 0); 14894139027SPablo Neira Ayuso bytes = atomic64_xchg(&acct->bytes, 0); 149f9da455bSLinus Torvalds smp_mb__before_atomic(); 150683399edSMathieu Poirier if (acct->flags & NFACCT_F_QUOTA) 151683399edSMathieu Poirier clear_bit(NFACCT_F_OVERQUOTA, &acct->flags); 15294139027SPablo Neira Ayuso } else { 15394139027SPablo Neira Ayuso pkts = atomic64_read(&acct->pkts); 15494139027SPablo Neira Ayuso bytes = atomic64_read(&acct->bytes); 15594139027SPablo Neira Ayuso } 1567c801189SDavid S. Miller if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || 1577c801189SDavid S. Miller nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || 1587c801189SDavid S. Miller nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) 1597c801189SDavid S. Miller goto nla_put_failure; 160683399edSMathieu Poirier if (acct->flags & NFACCT_F_QUOTA) { 161683399edSMathieu Poirier u64 *quota = (u64 *)acct->data; 16294139027SPablo Neira Ayuso 163683399edSMathieu Poirier if (nla_put_be32(skb, NFACCT_FLAGS, htonl(acct->flags)) || 164683399edSMathieu Poirier nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) 165683399edSMathieu Poirier goto nla_put_failure; 166683399edSMathieu Poirier } 16794139027SPablo Neira Ayuso nlmsg_end(skb, nlh); 16894139027SPablo Neira Ayuso return skb->len; 16994139027SPablo Neira Ayuso 17094139027SPablo Neira Ayuso nlmsg_failure: 17194139027SPablo Neira Ayuso nla_put_failure: 17294139027SPablo Neira Ayuso nlmsg_cancel(skb, nlh); 17394139027SPablo Neira Ayuso return -1; 17494139027SPablo Neira Ayuso } 17594139027SPablo Neira Ayuso 17694139027SPablo Neira Ayuso static int 17794139027SPablo Neira Ayuso nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) 17894139027SPablo Neira Ayuso { 17994139027SPablo Neira Ayuso struct nf_acct *cur, *last; 18094139027SPablo Neira Ayuso 18194139027SPablo Neira Ayuso if (cb->args[2]) 18294139027SPablo Neira Ayuso return 0; 18394139027SPablo Neira Ayuso 18494139027SPablo Neira Ayuso last = (struct nf_acct *)cb->args[1]; 18594139027SPablo Neira Ayuso if (cb->args[1]) 18694139027SPablo Neira Ayuso cb->args[1] = 0; 18794139027SPablo Neira Ayuso 18894139027SPablo Neira Ayuso rcu_read_lock(); 18994139027SPablo Neira Ayuso list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { 190991a6b73SPablo Neira Ayuso if (last) { 191991a6b73SPablo Neira Ayuso if (cur != last) 19294139027SPablo Neira Ayuso continue; 19394139027SPablo Neira Ayuso 194991a6b73SPablo Neira Ayuso last = NULL; 195991a6b73SPablo Neira Ayuso } 19615e47304SEric W. Biederman if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, 19794139027SPablo Neira Ayuso cb->nlh->nlmsg_seq, 19894139027SPablo Neira Ayuso NFNL_MSG_TYPE(cb->nlh->nlmsg_type), 19994139027SPablo Neira Ayuso NFNL_MSG_ACCT_NEW, cur) < 0) { 20094139027SPablo Neira Ayuso cb->args[1] = (unsigned long)cur; 20194139027SPablo Neira Ayuso break; 20294139027SPablo Neira Ayuso } 20394139027SPablo Neira Ayuso } 20494139027SPablo Neira Ayuso if (!cb->args[1]) 20594139027SPablo Neira Ayuso cb->args[2] = 1; 20694139027SPablo Neira Ayuso rcu_read_unlock(); 20794139027SPablo Neira Ayuso return skb->len; 20894139027SPablo Neira Ayuso } 20994139027SPablo Neira Ayuso 21094139027SPablo Neira Ayuso static int 21194139027SPablo Neira Ayuso nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, 21294139027SPablo Neira Ayuso const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 21394139027SPablo Neira Ayuso { 2143ab0b245SPablo Neira Ayuso int ret = -ENOENT; 21594139027SPablo Neira Ayuso struct nf_acct *cur; 21694139027SPablo Neira Ayuso char *acct_name; 21794139027SPablo Neira Ayuso 21894139027SPablo Neira Ayuso if (nlh->nlmsg_flags & NLM_F_DUMP) { 21980d326faSPablo Neira Ayuso struct netlink_dump_control c = { 22080d326faSPablo Neira Ayuso .dump = nfnl_acct_dump, 22180d326faSPablo Neira Ayuso }; 22280d326faSPablo Neira Ayuso return netlink_dump_start(nfnl, skb, nlh, &c); 22394139027SPablo Neira Ayuso } 22494139027SPablo Neira Ayuso 22594139027SPablo Neira Ayuso if (!tb[NFACCT_NAME]) 22694139027SPablo Neira Ayuso return -EINVAL; 22794139027SPablo Neira Ayuso acct_name = nla_data(tb[NFACCT_NAME]); 22894139027SPablo Neira Ayuso 22994139027SPablo Neira Ayuso list_for_each_entry(cur, &nfnl_acct_list, head) { 23094139027SPablo Neira Ayuso struct sk_buff *skb2; 23194139027SPablo Neira Ayuso 23294139027SPablo Neira Ayuso if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 23394139027SPablo Neira Ayuso continue; 23494139027SPablo Neira Ayuso 23594139027SPablo Neira Ayuso skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 2363ab0b245SPablo Neira Ayuso if (skb2 == NULL) { 2373ab0b245SPablo Neira Ayuso ret = -ENOMEM; 23894139027SPablo Neira Ayuso break; 2393ab0b245SPablo Neira Ayuso } 24094139027SPablo Neira Ayuso 24115e47304SEric W. Biederman ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, 24294139027SPablo Neira Ayuso nlh->nlmsg_seq, 24394139027SPablo Neira Ayuso NFNL_MSG_TYPE(nlh->nlmsg_type), 24494139027SPablo Neira Ayuso NFNL_MSG_ACCT_NEW, cur); 2453ab0b245SPablo Neira Ayuso if (ret <= 0) { 24694139027SPablo Neira Ayuso kfree_skb(skb2); 24794139027SPablo Neira Ayuso break; 24894139027SPablo Neira Ayuso } 24915e47304SEric W. Biederman ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, 2503ab0b245SPablo Neira Ayuso MSG_DONTWAIT); 2513ab0b245SPablo Neira Ayuso if (ret > 0) 2523ab0b245SPablo Neira Ayuso ret = 0; 2533ab0b245SPablo Neira Ayuso 2543ab0b245SPablo Neira Ayuso /* this avoids a loop in nfnetlink. */ 2553ab0b245SPablo Neira Ayuso return ret == -EAGAIN ? -ENOBUFS : ret; 2563ab0b245SPablo Neira Ayuso } 25794139027SPablo Neira Ayuso return ret; 25894139027SPablo Neira Ayuso } 25994139027SPablo Neira Ayuso 26094139027SPablo Neira Ayuso /* try to delete object, fail if it is still in use. */ 26194139027SPablo Neira Ayuso static int nfnl_acct_try_del(struct nf_acct *cur) 26294139027SPablo Neira Ayuso { 26394139027SPablo Neira Ayuso int ret = 0; 26494139027SPablo Neira Ayuso 26594139027SPablo Neira Ayuso /* we want to avoid races with nfnl_acct_find_get. */ 26694139027SPablo Neira Ayuso if (atomic_dec_and_test(&cur->refcnt)) { 26794139027SPablo Neira Ayuso /* We are protected by nfnl mutex. */ 26894139027SPablo Neira Ayuso list_del_rcu(&cur->head); 26994139027SPablo Neira Ayuso kfree_rcu(cur, rcu_head); 27094139027SPablo Neira Ayuso } else { 27194139027SPablo Neira Ayuso /* still in use, restore reference counter. */ 27294139027SPablo Neira Ayuso atomic_inc(&cur->refcnt); 27394139027SPablo Neira Ayuso ret = -EBUSY; 27494139027SPablo Neira Ayuso } 27594139027SPablo Neira Ayuso return ret; 27694139027SPablo Neira Ayuso } 27794139027SPablo Neira Ayuso 27894139027SPablo Neira Ayuso static int 27994139027SPablo Neira Ayuso nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, 28094139027SPablo Neira Ayuso const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 28194139027SPablo Neira Ayuso { 28294139027SPablo Neira Ayuso char *acct_name; 28394139027SPablo Neira Ayuso struct nf_acct *cur; 28494139027SPablo Neira Ayuso int ret = -ENOENT; 28594139027SPablo Neira Ayuso 28694139027SPablo Neira Ayuso if (!tb[NFACCT_NAME]) { 28794139027SPablo Neira Ayuso list_for_each_entry(cur, &nfnl_acct_list, head) 28894139027SPablo Neira Ayuso nfnl_acct_try_del(cur); 28994139027SPablo Neira Ayuso 29094139027SPablo Neira Ayuso return 0; 29194139027SPablo Neira Ayuso } 29294139027SPablo Neira Ayuso acct_name = nla_data(tb[NFACCT_NAME]); 29394139027SPablo Neira Ayuso 29494139027SPablo Neira Ayuso list_for_each_entry(cur, &nfnl_acct_list, head) { 29594139027SPablo Neira Ayuso if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) 29694139027SPablo Neira Ayuso continue; 29794139027SPablo Neira Ayuso 29894139027SPablo Neira Ayuso ret = nfnl_acct_try_del(cur); 29994139027SPablo Neira Ayuso if (ret < 0) 30094139027SPablo Neira Ayuso return ret; 30194139027SPablo Neira Ayuso 30294139027SPablo Neira Ayuso break; 30394139027SPablo Neira Ayuso } 30494139027SPablo Neira Ayuso return ret; 30594139027SPablo Neira Ayuso } 30694139027SPablo Neira Ayuso 30794139027SPablo Neira Ayuso static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { 30894139027SPablo Neira Ayuso [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, 30994139027SPablo Neira Ayuso [NFACCT_BYTES] = { .type = NLA_U64 }, 31094139027SPablo Neira Ayuso [NFACCT_PKTS] = { .type = NLA_U64 }, 311683399edSMathieu Poirier [NFACCT_FLAGS] = { .type = NLA_U32 }, 312683399edSMathieu Poirier [NFACCT_QUOTA] = { .type = NLA_U64 }, 31394139027SPablo Neira Ayuso }; 31494139027SPablo Neira Ayuso 31594139027SPablo Neira Ayuso static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { 31694139027SPablo Neira Ayuso [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, 31794139027SPablo Neira Ayuso .attr_count = NFACCT_MAX, 31894139027SPablo Neira Ayuso .policy = nfnl_acct_policy }, 31994139027SPablo Neira Ayuso [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, 32094139027SPablo Neira Ayuso .attr_count = NFACCT_MAX, 32194139027SPablo Neira Ayuso .policy = nfnl_acct_policy }, 32294139027SPablo Neira Ayuso [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, 32394139027SPablo Neira Ayuso .attr_count = NFACCT_MAX, 32494139027SPablo Neira Ayuso .policy = nfnl_acct_policy }, 32594139027SPablo Neira Ayuso [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, 32694139027SPablo Neira Ayuso .attr_count = NFACCT_MAX, 32794139027SPablo Neira Ayuso .policy = nfnl_acct_policy }, 32894139027SPablo Neira Ayuso }; 32994139027SPablo Neira Ayuso 33094139027SPablo Neira Ayuso static const struct nfnetlink_subsystem nfnl_acct_subsys = { 33194139027SPablo Neira Ayuso .name = "acct", 33294139027SPablo Neira Ayuso .subsys_id = NFNL_SUBSYS_ACCT, 33394139027SPablo Neira Ayuso .cb_count = NFNL_MSG_ACCT_MAX, 33494139027SPablo Neira Ayuso .cb = nfnl_acct_cb, 33594139027SPablo Neira Ayuso }; 33694139027SPablo Neira Ayuso 33794139027SPablo Neira Ayuso MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); 33894139027SPablo Neira Ayuso 33994139027SPablo Neira Ayuso struct nf_acct *nfnl_acct_find_get(const char *acct_name) 34094139027SPablo Neira Ayuso { 34194139027SPablo Neira Ayuso struct nf_acct *cur, *acct = NULL; 34294139027SPablo Neira Ayuso 34394139027SPablo Neira Ayuso rcu_read_lock(); 34494139027SPablo Neira Ayuso list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { 34594139027SPablo Neira Ayuso if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 34694139027SPablo Neira Ayuso continue; 34794139027SPablo Neira Ayuso 34894139027SPablo Neira Ayuso if (!try_module_get(THIS_MODULE)) 34994139027SPablo Neira Ayuso goto err; 35094139027SPablo Neira Ayuso 35194139027SPablo Neira Ayuso if (!atomic_inc_not_zero(&cur->refcnt)) { 35294139027SPablo Neira Ayuso module_put(THIS_MODULE); 35394139027SPablo Neira Ayuso goto err; 35494139027SPablo Neira Ayuso } 35594139027SPablo Neira Ayuso 35694139027SPablo Neira Ayuso acct = cur; 35794139027SPablo Neira Ayuso break; 35894139027SPablo Neira Ayuso } 35994139027SPablo Neira Ayuso err: 36094139027SPablo Neira Ayuso rcu_read_unlock(); 36194139027SPablo Neira Ayuso return acct; 36294139027SPablo Neira Ayuso } 36394139027SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnl_acct_find_get); 36494139027SPablo Neira Ayuso 36594139027SPablo Neira Ayuso void nfnl_acct_put(struct nf_acct *acct) 36694139027SPablo Neira Ayuso { 36794139027SPablo Neira Ayuso atomic_dec(&acct->refcnt); 36894139027SPablo Neira Ayuso module_put(THIS_MODULE); 36994139027SPablo Neira Ayuso } 37094139027SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnl_acct_put); 37194139027SPablo Neira Ayuso 37294139027SPablo Neira Ayuso void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) 37394139027SPablo Neira Ayuso { 37494139027SPablo Neira Ayuso atomic64_inc(&nfacct->pkts); 37594139027SPablo Neira Ayuso atomic64_add(skb->len, &nfacct->bytes); 37694139027SPablo Neira Ayuso } 37794139027SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnl_acct_update); 37894139027SPablo Neira Ayuso 379683399edSMathieu Poirier static void nfnl_overquota_report(struct nf_acct *nfacct) 380683399edSMathieu Poirier { 381683399edSMathieu Poirier int ret; 382683399edSMathieu Poirier struct sk_buff *skb; 383683399edSMathieu Poirier 384683399edSMathieu Poirier skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 385683399edSMathieu Poirier if (skb == NULL) 386683399edSMathieu Poirier return; 387683399edSMathieu Poirier 388683399edSMathieu Poirier ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, 389683399edSMathieu Poirier nfacct); 390683399edSMathieu Poirier if (ret <= 0) { 391683399edSMathieu Poirier kfree_skb(skb); 392683399edSMathieu Poirier return; 393683399edSMathieu Poirier } 394683399edSMathieu Poirier netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, 395683399edSMathieu Poirier GFP_ATOMIC); 396683399edSMathieu Poirier } 397683399edSMathieu Poirier 398683399edSMathieu Poirier int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) 399683399edSMathieu Poirier { 400683399edSMathieu Poirier u64 now; 401683399edSMathieu Poirier u64 *quota; 402683399edSMathieu Poirier int ret = NFACCT_UNDERQUOTA; 403683399edSMathieu Poirier 404683399edSMathieu Poirier /* no place here if we don't have a quota */ 405683399edSMathieu Poirier if (!(nfacct->flags & NFACCT_F_QUOTA)) 406683399edSMathieu Poirier return NFACCT_NO_QUOTA; 407683399edSMathieu Poirier 408683399edSMathieu Poirier quota = (u64 *)nfacct->data; 409683399edSMathieu Poirier now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? 410683399edSMathieu Poirier atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); 411683399edSMathieu Poirier 412683399edSMathieu Poirier ret = now > *quota; 413683399edSMathieu Poirier 414683399edSMathieu Poirier if (now >= *quota && 415683399edSMathieu Poirier !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) { 416683399edSMathieu Poirier nfnl_overquota_report(nfacct); 417683399edSMathieu Poirier } 418683399edSMathieu Poirier 419683399edSMathieu Poirier return ret; 420683399edSMathieu Poirier } 421683399edSMathieu Poirier EXPORT_SYMBOL_GPL(nfnl_acct_overquota); 422683399edSMathieu Poirier 42394139027SPablo Neira Ayuso static int __init nfnl_acct_init(void) 42494139027SPablo Neira Ayuso { 42594139027SPablo Neira Ayuso int ret; 42694139027SPablo Neira Ayuso 42794139027SPablo Neira Ayuso pr_info("nfnl_acct: registering with nfnetlink.\n"); 42894139027SPablo Neira Ayuso ret = nfnetlink_subsys_register(&nfnl_acct_subsys); 42994139027SPablo Neira Ayuso if (ret < 0) { 43094139027SPablo Neira Ayuso pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); 43194139027SPablo Neira Ayuso goto err_out; 43294139027SPablo Neira Ayuso } 43394139027SPablo Neira Ayuso return 0; 43494139027SPablo Neira Ayuso err_out: 43594139027SPablo Neira Ayuso return ret; 43694139027SPablo Neira Ayuso } 43794139027SPablo Neira Ayuso 43894139027SPablo Neira Ayuso static void __exit nfnl_acct_exit(void) 43994139027SPablo Neira Ayuso { 44094139027SPablo Neira Ayuso struct nf_acct *cur, *tmp; 44194139027SPablo Neira Ayuso 44294139027SPablo Neira Ayuso pr_info("nfnl_acct: unregistering from nfnetlink.\n"); 44394139027SPablo Neira Ayuso nfnetlink_subsys_unregister(&nfnl_acct_subsys); 44494139027SPablo Neira Ayuso 44594139027SPablo Neira Ayuso list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { 44694139027SPablo Neira Ayuso list_del_rcu(&cur->head); 44794139027SPablo Neira Ayuso /* We are sure that our objects have no clients at this point, 44894139027SPablo Neira Ayuso * it's safe to release them all without checking refcnt. */ 44994139027SPablo Neira Ayuso kfree_rcu(cur, rcu_head); 45094139027SPablo Neira Ayuso } 45194139027SPablo Neira Ayuso } 45294139027SPablo Neira Ayuso 45394139027SPablo Neira Ayuso module_init(nfnl_acct_init); 45494139027SPablo Neira Ayuso module_exit(nfnl_acct_exit); 455