1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org> |
4 | * (C) 2011 Intra2net AG <https://www.intra2net.com> |
5 | */ |
6 | #include <linux/init.h> |
7 | #include <linux/module.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/skbuff.h> |
10 | #include <linux/atomic.h> |
11 | #include <linux/refcount.h> |
12 | #include <linux/netlink.h> |
13 | #include <linux/rculist.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/types.h> |
16 | #include <linux/errno.h> |
17 | #include <net/netlink.h> |
18 | #include <net/sock.h> |
19 | #include <net/netns/generic.h> |
20 | |
21 | #include <linux/netfilter.h> |
22 | #include <linux/netfilter/nfnetlink.h> |
23 | #include <linux/netfilter/nfnetlink_acct.h> |
24 | |
25 | MODULE_LICENSE("GPL" ); |
26 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>" ); |
27 | MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure" ); |
28 | |
29 | struct nf_acct { |
30 | atomic64_t pkts; |
31 | atomic64_t bytes; |
32 | unsigned long flags; |
33 | struct list_head head; |
34 | refcount_t refcnt; |
35 | char name[NFACCT_NAME_MAX]; |
36 | struct rcu_head rcu_head; |
37 | char data[]; |
38 | }; |
39 | |
40 | struct nfacct_filter { |
41 | u32 value; |
42 | u32 mask; |
43 | }; |
44 | |
45 | struct nfnl_acct_net { |
46 | struct list_head nfnl_acct_list; |
47 | }; |
48 | |
49 | static unsigned int nfnl_acct_net_id __read_mostly; |
50 | |
51 | static inline struct nfnl_acct_net *nfnl_acct_pernet(struct net *net) |
52 | { |
53 | return net_generic(net, id: nfnl_acct_net_id); |
54 | } |
55 | |
56 | #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) |
57 | #define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */ |
58 | |
59 | static int nfnl_acct_new(struct sk_buff *skb, const struct nfnl_info *info, |
60 | const struct nlattr * const tb[]) |
61 | { |
62 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net: info->net); |
63 | struct nf_acct *nfacct, *matching = NULL; |
64 | unsigned int size = 0; |
65 | char *acct_name; |
66 | u32 flags = 0; |
67 | |
68 | if (!tb[NFACCT_NAME]) |
69 | return -EINVAL; |
70 | |
71 | acct_name = nla_data(nla: tb[NFACCT_NAME]); |
72 | if (strlen(acct_name) == 0) |
73 | return -EINVAL; |
74 | |
75 | list_for_each_entry(nfacct, &nfnl_acct_net->nfnl_acct_list, head) { |
76 | if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) |
77 | continue; |
78 | |
79 | if (info->nlh->nlmsg_flags & NLM_F_EXCL) |
80 | return -EEXIST; |
81 | |
82 | matching = nfacct; |
83 | break; |
84 | } |
85 | |
86 | if (matching) { |
87 | if (info->nlh->nlmsg_flags & NLM_F_REPLACE) { |
88 | /* reset counters if you request a replacement. */ |
89 | atomic64_set(v: &matching->pkts, i: 0); |
90 | atomic64_set(v: &matching->bytes, i: 0); |
91 | smp_mb__before_atomic(); |
92 | /* reset overquota flag if quota is enabled. */ |
93 | if ((matching->flags & NFACCT_F_QUOTA)) |
94 | clear_bit(NFACCT_OVERQUOTA_BIT, |
95 | addr: &matching->flags); |
96 | return 0; |
97 | } |
98 | return -EBUSY; |
99 | } |
100 | |
101 | if (tb[NFACCT_FLAGS]) { |
102 | flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); |
103 | if (flags & ~NFACCT_F_QUOTA) |
104 | return -EOPNOTSUPP; |
105 | if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) |
106 | return -EINVAL; |
107 | if (flags & NFACCT_F_OVERQUOTA) |
108 | return -EINVAL; |
109 | if ((flags & NFACCT_F_QUOTA) && !tb[NFACCT_QUOTA]) |
110 | return -EINVAL; |
111 | |
112 | size += sizeof(u64); |
113 | } |
114 | |
115 | nfacct = kzalloc(size: sizeof(struct nf_acct) + size, GFP_KERNEL); |
116 | if (nfacct == NULL) |
117 | return -ENOMEM; |
118 | |
119 | if (flags & NFACCT_F_QUOTA) { |
120 | u64 *quota = (u64 *)nfacct->data; |
121 | |
122 | *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); |
123 | nfacct->flags = flags; |
124 | } |
125 | |
126 | nla_strscpy(dst: nfacct->name, nla: tb[NFACCT_NAME], NFACCT_NAME_MAX); |
127 | |
128 | if (tb[NFACCT_BYTES]) { |
129 | atomic64_set(v: &nfacct->bytes, |
130 | be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES]))); |
131 | } |
132 | if (tb[NFACCT_PKTS]) { |
133 | atomic64_set(v: &nfacct->pkts, |
134 | be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); |
135 | } |
136 | refcount_set(r: &nfacct->refcnt, n: 1); |
137 | list_add_tail_rcu(new: &nfacct->head, head: &nfnl_acct_net->nfnl_acct_list); |
138 | return 0; |
139 | } |
140 | |
141 | static int |
142 | nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, |
143 | int event, struct nf_acct *acct) |
144 | { |
145 | struct nlmsghdr *nlh; |
146 | unsigned int flags = portid ? NLM_F_MULTI : 0; |
147 | u64 pkts, bytes; |
148 | u32 old_flags; |
149 | |
150 | event = nfnl_msg_type(NFNL_SUBSYS_ACCT, msg_type: event); |
151 | nlh = nfnl_msg_put(skb, portid, seq, type: event, flags, AF_UNSPEC, |
152 | NFNETLINK_V0, res_id: 0); |
153 | if (!nlh) |
154 | goto nlmsg_failure; |
155 | |
156 | if (nla_put_string(skb, attrtype: NFACCT_NAME, str: acct->name)) |
157 | goto nla_put_failure; |
158 | |
159 | old_flags = acct->flags; |
160 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { |
161 | pkts = atomic64_xchg(v: &acct->pkts, new: 0); |
162 | bytes = atomic64_xchg(v: &acct->bytes, new: 0); |
163 | smp_mb__before_atomic(); |
164 | if (acct->flags & NFACCT_F_QUOTA) |
165 | clear_bit(NFACCT_OVERQUOTA_BIT, addr: &acct->flags); |
166 | } else { |
167 | pkts = atomic64_read(v: &acct->pkts); |
168 | bytes = atomic64_read(v: &acct->bytes); |
169 | } |
170 | if (nla_put_be64(skb, attrtype: NFACCT_PKTS, cpu_to_be64(pkts), |
171 | padattr: NFACCT_PAD) || |
172 | nla_put_be64(skb, attrtype: NFACCT_BYTES, cpu_to_be64(bytes), |
173 | padattr: NFACCT_PAD) || |
174 | nla_put_be32(skb, attrtype: NFACCT_USE, htonl(refcount_read(&acct->refcnt)))) |
175 | goto nla_put_failure; |
176 | if (acct->flags & NFACCT_F_QUOTA) { |
177 | u64 *quota = (u64 *)acct->data; |
178 | |
179 | if (nla_put_be32(skb, attrtype: NFACCT_FLAGS, htonl(old_flags)) || |
180 | nla_put_be64(skb, attrtype: NFACCT_QUOTA, cpu_to_be64(*quota), |
181 | padattr: NFACCT_PAD)) |
182 | goto nla_put_failure; |
183 | } |
184 | nlmsg_end(skb, nlh); |
185 | return skb->len; |
186 | |
187 | nlmsg_failure: |
188 | nla_put_failure: |
189 | nlmsg_cancel(skb, nlh); |
190 | return -1; |
191 | } |
192 | |
193 | static int |
194 | nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) |
195 | { |
196 | struct net *net = sock_net(sk: skb->sk); |
197 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); |
198 | struct nf_acct *cur, *last; |
199 | const struct nfacct_filter *filter = cb->data; |
200 | |
201 | if (cb->args[2]) |
202 | return 0; |
203 | |
204 | last = (struct nf_acct *)cb->args[1]; |
205 | if (cb->args[1]) |
206 | cb->args[1] = 0; |
207 | |
208 | rcu_read_lock(); |
209 | list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) { |
210 | if (last) { |
211 | if (cur != last) |
212 | continue; |
213 | |
214 | last = NULL; |
215 | } |
216 | |
217 | if (filter && (cur->flags & filter->mask) != filter->value) |
218 | continue; |
219 | |
220 | if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, |
221 | seq: cb->nlh->nlmsg_seq, |
222 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), |
223 | event: NFNL_MSG_ACCT_NEW, acct: cur) < 0) { |
224 | cb->args[1] = (unsigned long)cur; |
225 | break; |
226 | } |
227 | } |
228 | if (!cb->args[1]) |
229 | cb->args[2] = 1; |
230 | rcu_read_unlock(); |
231 | return skb->len; |
232 | } |
233 | |
234 | static int nfnl_acct_done(struct netlink_callback *cb) |
235 | { |
236 | kfree(objp: cb->data); |
237 | return 0; |
238 | } |
239 | |
240 | static const struct nla_policy filter_policy[NFACCT_FILTER_MAX + 1] = { |
241 | [NFACCT_FILTER_MASK] = { .type = NLA_U32 }, |
242 | [NFACCT_FILTER_VALUE] = { .type = NLA_U32 }, |
243 | }; |
244 | |
245 | static int nfnl_acct_start(struct netlink_callback *cb) |
246 | { |
247 | const struct nlattr *const attr = cb->data; |
248 | struct nlattr *tb[NFACCT_FILTER_MAX + 1]; |
249 | struct nfacct_filter *filter; |
250 | int err; |
251 | |
252 | if (!attr) |
253 | return 0; |
254 | |
255 | err = nla_parse_nested_deprecated(tb, NFACCT_FILTER_MAX, nla: attr, |
256 | policy: filter_policy, NULL); |
257 | if (err < 0) |
258 | return err; |
259 | |
260 | if (!tb[NFACCT_FILTER_MASK] || !tb[NFACCT_FILTER_VALUE]) |
261 | return -EINVAL; |
262 | |
263 | filter = kzalloc(size: sizeof(struct nfacct_filter), GFP_KERNEL); |
264 | if (!filter) |
265 | return -ENOMEM; |
266 | |
267 | filter->mask = ntohl(nla_get_be32(tb[NFACCT_FILTER_MASK])); |
268 | filter->value = ntohl(nla_get_be32(tb[NFACCT_FILTER_VALUE])); |
269 | cb->data = filter; |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | static int nfnl_acct_get(struct sk_buff *skb, const struct nfnl_info *info, |
275 | const struct nlattr * const tb[]) |
276 | { |
277 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net: info->net); |
278 | int ret = -ENOENT; |
279 | struct nf_acct *cur; |
280 | char *acct_name; |
281 | |
282 | if (info->nlh->nlmsg_flags & NLM_F_DUMP) { |
283 | struct netlink_dump_control c = { |
284 | .dump = nfnl_acct_dump, |
285 | .start = nfnl_acct_start, |
286 | .done = nfnl_acct_done, |
287 | .data = (void *)tb[NFACCT_FILTER], |
288 | }; |
289 | |
290 | return netlink_dump_start(ssk: info->sk, skb, nlh: info->nlh, control: &c); |
291 | } |
292 | |
293 | if (!tb[NFACCT_NAME]) |
294 | return -EINVAL; |
295 | acct_name = nla_data(nla: tb[NFACCT_NAME]); |
296 | |
297 | list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) { |
298 | struct sk_buff *skb2; |
299 | |
300 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
301 | continue; |
302 | |
303 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
304 | if (skb2 == NULL) { |
305 | ret = -ENOMEM; |
306 | break; |
307 | } |
308 | |
309 | ret = nfnl_acct_fill_info(skb: skb2, NETLINK_CB(skb).portid, |
310 | seq: info->nlh->nlmsg_seq, |
311 | NFNL_MSG_TYPE(info->nlh->nlmsg_type), |
312 | event: NFNL_MSG_ACCT_NEW, acct: cur); |
313 | if (ret <= 0) { |
314 | kfree_skb(skb: skb2); |
315 | break; |
316 | } |
317 | |
318 | ret = nfnetlink_unicast(skb: skb2, net: info->net, NETLINK_CB(skb).portid); |
319 | break; |
320 | } |
321 | |
322 | return ret; |
323 | } |
324 | |
325 | /* try to delete object, fail if it is still in use. */ |
326 | static int nfnl_acct_try_del(struct nf_acct *cur) |
327 | { |
328 | int ret = 0; |
329 | |
330 | /* We want to avoid races with nfnl_acct_put. So only when the current |
331 | * refcnt is 1, we decrease it to 0. |
332 | */ |
333 | if (refcount_dec_if_one(r: &cur->refcnt)) { |
334 | /* We are protected by nfnl mutex. */ |
335 | list_del_rcu(entry: &cur->head); |
336 | kfree_rcu(cur, rcu_head); |
337 | } else { |
338 | ret = -EBUSY; |
339 | } |
340 | return ret; |
341 | } |
342 | |
343 | static int nfnl_acct_del(struct sk_buff *skb, const struct nfnl_info *info, |
344 | const struct nlattr * const tb[]) |
345 | { |
346 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net: info->net); |
347 | struct nf_acct *cur, *tmp; |
348 | int ret = -ENOENT; |
349 | char *acct_name; |
350 | |
351 | if (!tb[NFACCT_NAME]) { |
352 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head) |
353 | nfnl_acct_try_del(cur); |
354 | |
355 | return 0; |
356 | } |
357 | acct_name = nla_data(nla: tb[NFACCT_NAME]); |
358 | |
359 | list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) { |
360 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) |
361 | continue; |
362 | |
363 | ret = nfnl_acct_try_del(cur); |
364 | if (ret < 0) |
365 | return ret; |
366 | |
367 | break; |
368 | } |
369 | return ret; |
370 | } |
371 | |
372 | static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { |
373 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, |
374 | [NFACCT_BYTES] = { .type = NLA_U64 }, |
375 | [NFACCT_PKTS] = { .type = NLA_U64 }, |
376 | [NFACCT_FLAGS] = { .type = NLA_U32 }, |
377 | [NFACCT_QUOTA] = { .type = NLA_U64 }, |
378 | [NFACCT_FILTER] = {.type = NLA_NESTED }, |
379 | }; |
380 | |
381 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { |
382 | [NFNL_MSG_ACCT_NEW] = { |
383 | .call = nfnl_acct_new, |
384 | .type = NFNL_CB_MUTEX, |
385 | .attr_count = NFACCT_MAX, |
386 | .policy = nfnl_acct_policy |
387 | }, |
388 | [NFNL_MSG_ACCT_GET] = { |
389 | .call = nfnl_acct_get, |
390 | .type = NFNL_CB_MUTEX, |
391 | .attr_count = NFACCT_MAX, |
392 | .policy = nfnl_acct_policy |
393 | }, |
394 | [NFNL_MSG_ACCT_GET_CTRZERO] = { |
395 | .call = nfnl_acct_get, |
396 | .type = NFNL_CB_MUTEX, |
397 | .attr_count = NFACCT_MAX, |
398 | .policy = nfnl_acct_policy |
399 | }, |
400 | [NFNL_MSG_ACCT_DEL] = { |
401 | .call = nfnl_acct_del, |
402 | .type = NFNL_CB_MUTEX, |
403 | .attr_count = NFACCT_MAX, |
404 | .policy = nfnl_acct_policy |
405 | }, |
406 | }; |
407 | |
408 | static const struct nfnetlink_subsystem nfnl_acct_subsys = { |
409 | .name = "acct" , |
410 | .subsys_id = NFNL_SUBSYS_ACCT, |
411 | .cb_count = NFNL_MSG_ACCT_MAX, |
412 | .cb = nfnl_acct_cb, |
413 | }; |
414 | |
415 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); |
416 | |
417 | struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name) |
418 | { |
419 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); |
420 | struct nf_acct *cur, *acct = NULL; |
421 | |
422 | rcu_read_lock(); |
423 | list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) { |
424 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
425 | continue; |
426 | |
427 | if (!try_module_get(THIS_MODULE)) |
428 | goto err; |
429 | |
430 | if (!refcount_inc_not_zero(r: &cur->refcnt)) { |
431 | module_put(THIS_MODULE); |
432 | goto err; |
433 | } |
434 | |
435 | acct = cur; |
436 | break; |
437 | } |
438 | err: |
439 | rcu_read_unlock(); |
440 | return acct; |
441 | } |
442 | EXPORT_SYMBOL_GPL(nfnl_acct_find_get); |
443 | |
444 | void nfnl_acct_put(struct nf_acct *acct) |
445 | { |
446 | if (refcount_dec_and_test(r: &acct->refcnt)) |
447 | kfree_rcu(acct, rcu_head); |
448 | |
449 | module_put(THIS_MODULE); |
450 | } |
451 | EXPORT_SYMBOL_GPL(nfnl_acct_put); |
452 | |
453 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) |
454 | { |
455 | atomic64_inc(v: &nfacct->pkts); |
456 | atomic64_add(i: skb->len, v: &nfacct->bytes); |
457 | } |
458 | EXPORT_SYMBOL_GPL(nfnl_acct_update); |
459 | |
460 | static void nfnl_overquota_report(struct net *net, struct nf_acct *nfacct) |
461 | { |
462 | int ret; |
463 | struct sk_buff *skb; |
464 | |
465 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
466 | if (skb == NULL) |
467 | return; |
468 | |
469 | ret = nfnl_acct_fill_info(skb, portid: 0, seq: 0, type: NFNL_MSG_ACCT_OVERQUOTA, event: 0, |
470 | acct: nfacct); |
471 | if (ret <= 0) { |
472 | kfree_skb(skb); |
473 | return; |
474 | } |
475 | nfnetlink_broadcast(net, skb, portid: 0, NFNLGRP_ACCT_QUOTA, GFP_ATOMIC); |
476 | } |
477 | |
478 | int nfnl_acct_overquota(struct net *net, struct nf_acct *nfacct) |
479 | { |
480 | u64 now; |
481 | u64 *quota; |
482 | int ret = NFACCT_UNDERQUOTA; |
483 | |
484 | /* no place here if we don't have a quota */ |
485 | if (!(nfacct->flags & NFACCT_F_QUOTA)) |
486 | return NFACCT_NO_QUOTA; |
487 | |
488 | quota = (u64 *)nfacct->data; |
489 | now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? |
490 | atomic64_read(v: &nfacct->pkts) : atomic64_read(v: &nfacct->bytes); |
491 | |
492 | ret = now > *quota; |
493 | |
494 | if (now >= *quota && |
495 | !test_and_set_bit(NFACCT_OVERQUOTA_BIT, addr: &nfacct->flags)) { |
496 | nfnl_overquota_report(net, nfacct); |
497 | } |
498 | |
499 | return ret; |
500 | } |
501 | EXPORT_SYMBOL_GPL(nfnl_acct_overquota); |
502 | |
503 | static int __net_init nfnl_acct_net_init(struct net *net) |
504 | { |
505 | INIT_LIST_HEAD(list: &nfnl_acct_pernet(net)->nfnl_acct_list); |
506 | |
507 | return 0; |
508 | } |
509 | |
510 | static void __net_exit nfnl_acct_net_exit(struct net *net) |
511 | { |
512 | struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net); |
513 | struct nf_acct *cur, *tmp; |
514 | |
515 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head) { |
516 | list_del_rcu(entry: &cur->head); |
517 | |
518 | if (refcount_dec_and_test(r: &cur->refcnt)) |
519 | kfree_rcu(cur, rcu_head); |
520 | } |
521 | } |
522 | |
523 | static struct pernet_operations nfnl_acct_ops = { |
524 | .init = nfnl_acct_net_init, |
525 | .exit = nfnl_acct_net_exit, |
526 | .id = &nfnl_acct_net_id, |
527 | .size = sizeof(struct nfnl_acct_net), |
528 | }; |
529 | |
530 | static int __init nfnl_acct_init(void) |
531 | { |
532 | int ret; |
533 | |
534 | ret = register_pernet_subsys(&nfnl_acct_ops); |
535 | if (ret < 0) { |
536 | pr_err("nfnl_acct_init: failed to register pernet ops\n" ); |
537 | goto err_out; |
538 | } |
539 | |
540 | ret = nfnetlink_subsys_register(n: &nfnl_acct_subsys); |
541 | if (ret < 0) { |
542 | pr_err("nfnl_acct_init: cannot register with nfnetlink.\n" ); |
543 | goto cleanup_pernet; |
544 | } |
545 | return 0; |
546 | |
547 | cleanup_pernet: |
548 | unregister_pernet_subsys(&nfnl_acct_ops); |
549 | err_out: |
550 | return ret; |
551 | } |
552 | |
553 | static void __exit nfnl_acct_exit(void) |
554 | { |
555 | nfnetlink_subsys_unregister(n: &nfnl_acct_subsys); |
556 | unregister_pernet_subsys(&nfnl_acct_ops); |
557 | } |
558 | |
559 | module_init(nfnl_acct_init); |
560 | module_exit(nfnl_acct_exit); |
561 | |