Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
nfnetlink_acct.c
Go to the documentation of this file.
1 /*
2  * (C) 2011 Pablo Neira Ayuso <[email protected]>
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 
26 MODULE_LICENSE("GPL");
27 MODULE_AUTHOR("Pablo Neira Ayuso <[email protected]>");
28 MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure");
29 
30 static LIST_HEAD(nfnl_acct_list);
31 
32 struct nf_acct {
35  struct list_head head;
39 };
40 
41 static int
42 nfnl_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 
93 static int
94 nfnl_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 
130 nlmsg_failure:
131 nla_put_failure:
132  nlmsg_cancel(skb, nlh);
133  return -1;
134 }
135 
136 static int
137 nfnl_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 
167 static int
168 nfnl_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,
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. */
218 static 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 
235 static int
236 nfnl_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 
264 static 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 
270 static 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 
285 static 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 
293 
294 struct 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  }
314 err:
315  rcu_read_unlock();
316  return acct;
317 }
319 
320 void nfnl_acct_put(struct nf_acct *acct)
321 {
322  atomic_dec(&acct->refcnt);
323  module_put(THIS_MODULE);
324 }
326 
327 void 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 }
333 
334 static 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;
345 err_out:
346  return ret;
347 }
348 
349 static 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 
364 module_init(nfnl_acct_init);
365 module_exit(nfnl_acct_exit);