aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2011-12-23 08:19:50 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2011-12-24 20:43:03 -0500
commit9413902796f56f6209e19dd54e840ed46950612c (patch)
treed03b478f7b5e11305cebd5ff26e7342dde7456b4 /net
parent80e60e67bc4bbfe61b61a344f542af23e16abdbf (diff)
netfilter: add extended accounting infrastructure over nfnetlink
We currently have two ways to account traffic in netfilter: - iptables chain and rule counters: # iptables -L -n -v Chain INPUT (policy DROP 3 packets, 867 bytes) pkts bytes target prot opt in out source destination 8 1104 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 - use flow-based accounting provided by ctnetlink: # conntrack -L tcp 6 431999 ESTABLISHED src=192.168.1.130 dst=212.106.219.168 sport=58152 dport=80 packets=47 bytes=7654 src=212.106.219.168 dst=192.168.1.130 sport=80 dport=58152 packets=49 bytes=66340 [ASSURED] mark=0 use=1 While trying to display real-time accounting statistics, we require to pool the kernel periodically to obtain this information. This is OK if the number of flows is relatively low. However, in case that the number of flows is huge, we can spend a considerable amount of cycles to iterate over the list of flows that have been obtained. Moreover, if we want to obtain the sum of the flow accounting results that match some criteria, we have to iterate over the whole list of existing flows, look for matchings and update the counters. This patch adds the extended accounting infrastructure for nfnetlink which aims to allow displaying real-time traffic accounting without the need of complicated and resource-consuming implementation in user-space. Basically, this new infrastructure allows you to create accounting objects. One accounting object is composed of packet and byte counters. In order to manipulate create accounting objects, you require the new libnetfilter_acct library. It contains several examples of use: libnetfilter_acct/examples# ./nfacct-add http-traffic libnetfilter_acct/examples# ./nfacct-get http-traffic = { pkts = 000000000000, bytes = 000000000000 }; Then, you can use one of this accounting objects in several iptables rules using the new nfacct match (which comes in a follow-up patch): # iptables -I INPUT -p tcp --sport 80 -m nfacct --nfacct-name http-traffic # iptables -I OUTPUT -p tcp --dport 80 -m nfacct --nfacct-name http-traffic The idea is simple: if one packet matches the rule, the nfacct match updates the counters. Thanks to Patrick McHardy, Eric Dumazet, Changli Gao for reviewing and providing feedback for this contribution. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/Kconfig8
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/nfnetlink_acct.c352
3 files changed, 361 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index d5597b759ba3..77326acd1f57 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -4,6 +4,14 @@ menu "Core Netfilter Configuration"
4config NETFILTER_NETLINK 4config NETFILTER_NETLINK
5 tristate 5 tristate
6 6
7config NETFILTER_NETLINK_ACCT
8tristate "Netfilter NFACCT over NFNETLINK interface"
9 depends on NETFILTER_ADVANCED
10 select NETFILTER_NETLINK
11 help
12 If this option is enabled, the kernel will include support
13 for extended accounting via NFNETLINK.
14
7config NETFILTER_NETLINK_QUEUE 15config NETFILTER_NETLINK_QUEUE
8 tristate "Netfilter NFQUEUE over NFNETLINK interface" 16 tristate "Netfilter NFQUEUE over NFNETLINK interface"
9 depends on NETFILTER_ADVANCED 17 depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 1a02853df863..4da1c879644f 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -7,6 +7,7 @@ nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
7obj-$(CONFIG_NETFILTER) = netfilter.o 7obj-$(CONFIG_NETFILTER) = netfilter.o
8 8
9obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o 9obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
10obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
10obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o 11obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
11obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o 12obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
12 13
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c
new file mode 100644
index 000000000000..362ab6ca3dc1
--- /dev/null
+++ b/net/netfilter/nfnetlink_acct.c
@@ -0,0 +1,352 @@
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 = 0;
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 break;
191
192 ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).pid,
193 nlh->nlmsg_seq,
194 NFNL_MSG_TYPE(nlh->nlmsg_type),
195 NFNL_MSG_ACCT_NEW, cur);
196 if (ret <= 0)
197 kfree_skb(skb2);
198
199 break;
200 }
201 return ret;
202}
203
204/* try to delete object, fail if it is still in use. */
205static int nfnl_acct_try_del(struct nf_acct *cur)
206{
207 int ret = 0;
208
209 /* we want to avoid races with nfnl_acct_find_get. */
210 if (atomic_dec_and_test(&cur->refcnt)) {
211 /* We are protected by nfnl mutex. */
212 list_del_rcu(&cur->head);
213 kfree_rcu(cur, rcu_head);
214 } else {
215 /* still in use, restore reference counter. */
216 atomic_inc(&cur->refcnt);
217 ret = -EBUSY;
218 }
219 return ret;
220}
221
222static int
223nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
224 const struct nlmsghdr *nlh, const struct nlattr * const tb[])
225{
226 char *acct_name;
227 struct nf_acct *cur;
228 int ret = -ENOENT;
229
230 if (!tb[NFACCT_NAME]) {
231 list_for_each_entry(cur, &nfnl_acct_list, head)
232 nfnl_acct_try_del(cur);
233
234 return 0;
235 }
236 acct_name = nla_data(tb[NFACCT_NAME]);
237
238 list_for_each_entry(cur, &nfnl_acct_list, head) {
239 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0)
240 continue;
241
242 ret = nfnl_acct_try_del(cur);
243 if (ret < 0)
244 return ret;
245
246 break;
247 }
248 return ret;
249}
250
251static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
252 [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
253 [NFACCT_BYTES] = { .type = NLA_U64 },
254 [NFACCT_PKTS] = { .type = NLA_U64 },
255};
256
257static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = {
258 [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new,
259 .attr_count = NFACCT_MAX,
260 .policy = nfnl_acct_policy },
261 [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get,
262 .attr_count = NFACCT_MAX,
263 .policy = nfnl_acct_policy },
264 [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get,
265 .attr_count = NFACCT_MAX,
266 .policy = nfnl_acct_policy },
267 [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del,
268 .attr_count = NFACCT_MAX,
269 .policy = nfnl_acct_policy },
270};
271
272static const struct nfnetlink_subsystem nfnl_acct_subsys = {
273 .name = "acct",
274 .subsys_id = NFNL_SUBSYS_ACCT,
275 .cb_count = NFNL_MSG_ACCT_MAX,
276 .cb = nfnl_acct_cb,
277};
278
279MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT);
280
281struct nf_acct *nfnl_acct_find_get(const char *acct_name)
282{
283 struct nf_acct *cur, *acct = NULL;
284
285 rcu_read_lock();
286 list_for_each_entry_rcu(cur, &nfnl_acct_list, head) {
287 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
288 continue;
289
290 if (!try_module_get(THIS_MODULE))
291 goto err;
292
293 if (!atomic_inc_not_zero(&cur->refcnt)) {
294 module_put(THIS_MODULE);
295 goto err;
296 }
297
298 acct = cur;
299 break;
300 }
301err:
302 rcu_read_unlock();
303 return acct;
304}
305EXPORT_SYMBOL_GPL(nfnl_acct_find_get);
306
307void nfnl_acct_put(struct nf_acct *acct)
308{
309 atomic_dec(&acct->refcnt);
310 module_put(THIS_MODULE);
311}
312EXPORT_SYMBOL_GPL(nfnl_acct_put);
313
314void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct)
315{
316 atomic64_inc(&nfacct->pkts);
317 atomic64_add(skb->len, &nfacct->bytes);
318}
319EXPORT_SYMBOL_GPL(nfnl_acct_update);
320
321static int __init nfnl_acct_init(void)
322{
323 int ret;
324
325 pr_info("nfnl_acct: registering with nfnetlink.\n");
326 ret = nfnetlink_subsys_register(&nfnl_acct_subsys);
327 if (ret < 0) {
328 pr_err("nfnl_acct_init: cannot register with nfnetlink.\n");
329 goto err_out;
330 }
331 return 0;
332err_out:
333 return ret;
334}
335
336static void __exit nfnl_acct_exit(void)
337{
338 struct nf_acct *cur, *tmp;
339
340 pr_info("nfnl_acct: unregistering from nfnetlink.\n");
341 nfnetlink_subsys_unregister(&nfnl_acct_subsys);
342
343 list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) {
344 list_del_rcu(&cur->head);
345 /* We are sure that our objects have no clients at this point,
346 * it's safe to release them all without checking refcnt. */
347 kfree_rcu(cur, rcu_head);
348 }
349}
350
351module_init(nfnl_acct_init);
352module_exit(nfnl_acct_exit);