1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2006 Patrick McHardy <kaber@trash.net> |
4 | * |
5 | * Based on ipt_random and ipt_nth by Fabrice MARIE <fabrice@netfilter.org>. |
6 | */ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/spinlock.h> |
10 | #include <linux/skbuff.h> |
11 | #include <linux/net.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <linux/netfilter/xt_statistic.h> |
15 | #include <linux/netfilter/x_tables.h> |
16 | #include <linux/module.h> |
17 | |
18 | struct xt_statistic_priv { |
19 | atomic_t count; |
20 | } ____cacheline_aligned_in_smp; |
21 | |
22 | MODULE_LICENSE("GPL" ); |
23 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>" ); |
24 | MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)" ); |
25 | MODULE_ALIAS("ipt_statistic" ); |
26 | MODULE_ALIAS("ip6t_statistic" ); |
27 | |
28 | static bool |
29 | statistic_mt(const struct sk_buff *skb, struct xt_action_param *par) |
30 | { |
31 | const struct xt_statistic_info *info = par->matchinfo; |
32 | bool ret = info->flags & XT_STATISTIC_INVERT; |
33 | int nval, oval; |
34 | |
35 | switch (info->mode) { |
36 | case XT_STATISTIC_MODE_RANDOM: |
37 | if ((get_random_u32() & 0x7FFFFFFF) < info->u.random.probability) |
38 | ret = !ret; |
39 | break; |
40 | case XT_STATISTIC_MODE_NTH: |
41 | do { |
42 | oval = atomic_read(v: &info->master->count); |
43 | nval = (oval == info->u.nth.every) ? 0 : oval + 1; |
44 | } while (atomic_cmpxchg(v: &info->master->count, old: oval, new: nval) != oval); |
45 | if (nval == 0) |
46 | ret = !ret; |
47 | break; |
48 | } |
49 | |
50 | return ret; |
51 | } |
52 | |
53 | static int statistic_mt_check(const struct xt_mtchk_param *par) |
54 | { |
55 | struct xt_statistic_info *info = par->matchinfo; |
56 | |
57 | if (info->mode > XT_STATISTIC_MODE_MAX || |
58 | info->flags & ~XT_STATISTIC_MASK) |
59 | return -EINVAL; |
60 | |
61 | info->master = kzalloc(size: sizeof(*info->master), GFP_KERNEL); |
62 | if (info->master == NULL) |
63 | return -ENOMEM; |
64 | atomic_set(v: &info->master->count, i: info->u.nth.count); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static void statistic_mt_destroy(const struct xt_mtdtor_param *par) |
70 | { |
71 | const struct xt_statistic_info *info = par->matchinfo; |
72 | |
73 | kfree(objp: info->master); |
74 | } |
75 | |
76 | static struct xt_match xt_statistic_mt_reg __read_mostly = { |
77 | .name = "statistic" , |
78 | .revision = 0, |
79 | .family = NFPROTO_UNSPEC, |
80 | .match = statistic_mt, |
81 | .checkentry = statistic_mt_check, |
82 | .destroy = statistic_mt_destroy, |
83 | .matchsize = sizeof(struct xt_statistic_info), |
84 | .usersize = offsetof(struct xt_statistic_info, master), |
85 | .me = THIS_MODULE, |
86 | }; |
87 | |
88 | static int __init statistic_mt_init(void) |
89 | { |
90 | return xt_register_match(target: &xt_statistic_mt_reg); |
91 | } |
92 | |
93 | static void __exit statistic_mt_exit(void) |
94 | { |
95 | xt_unregister_match(target: &xt_statistic_mt_reg); |
96 | } |
97 | |
98 | module_init(statistic_mt_init); |
99 | module_exit(statistic_mt_exit); |
100 | |