blob: 80f6624e23554b30090b4a86b763ad279f5e44ac [file] [log] [blame]
Thomas Gleixnerd2912cb2019-06-04 10:11:33 +02001// SPDX-License-Identifier: GPL-2.0-only
Patrick McHardy58590342007-12-04 23:40:05 -08002/*
3 * (C) 2007 Patrick McHardy <kaber@trash.net>
Patrick McHardy58590342007-12-04 23:40:05 -08004 */
5#include <linux/module.h>
6#include <linux/skbuff.h>
7#include <linux/gen_stats.h>
8#include <linux/jhash.h>
9#include <linux/rtnetlink.h>
10#include <linux/random.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090011#include <linux/slab.h>
Patrick McHardy58590342007-12-04 23:40:05 -080012#include <net/gen_stats.h>
Patrick McHardy1e904742008-01-22 22:11:17 -080013#include <net/netlink.h>
Cong Wang3427b2a2018-03-01 18:58:38 -080014#include <net/netns/generic.h>
Patrick McHardy58590342007-12-04 23:40:05 -080015
16#include <linux/netfilter/x_tables.h>
17#include <linux/netfilter/xt_RATEEST.h>
18#include <net/netfilter/xt_rateest.h>
19
Patrick McHardy58590342007-12-04 23:40:05 -080020#define RATEEST_HSIZE 16
Cong Wang3427b2a2018-03-01 18:58:38 -080021
22struct xt_rateest_net {
23 struct mutex hash_lock;
24 struct hlist_head hash[RATEEST_HSIZE];
25};
26
27static unsigned int xt_rateest_id;
28
Patrick McHardy58590342007-12-04 23:40:05 -080029static unsigned int jhash_rnd __read_mostly;
30
31static unsigned int xt_rateest_hash(const char *name)
32{
Pankaj Bharadiyac5936422019-12-09 10:31:43 -080033 return jhash(name, sizeof_field(struct xt_rateest, name), jhash_rnd) &
Patrick McHardy58590342007-12-04 23:40:05 -080034 (RATEEST_HSIZE - 1);
35}
36
Cong Wang3427b2a2018-03-01 18:58:38 -080037static void xt_rateest_hash_insert(struct xt_rateest_net *xn,
38 struct xt_rateest *est)
Patrick McHardy58590342007-12-04 23:40:05 -080039{
40 unsigned int h;
41
42 h = xt_rateest_hash(est->name);
Cong Wang3427b2a2018-03-01 18:58:38 -080043 hlist_add_head(&est->list, &xn->hash[h]);
Patrick McHardy58590342007-12-04 23:40:05 -080044}
45
Cong Wang3427b2a2018-03-01 18:58:38 -080046static struct xt_rateest *__xt_rateest_lookup(struct xt_rateest_net *xn,
47 const char *name)
Patrick McHardy58590342007-12-04 23:40:05 -080048{
49 struct xt_rateest *est;
Patrick McHardy58590342007-12-04 23:40:05 -080050 unsigned int h;
51
52 h = xt_rateest_hash(name);
Cong Wang3427b2a2018-03-01 18:58:38 -080053 hlist_for_each_entry(est, &xn->hash[h], list) {
Patrick McHardy58590342007-12-04 23:40:05 -080054 if (strcmp(est->name, name) == 0) {
55 est->refcnt++;
Patrick McHardy58590342007-12-04 23:40:05 -080056 return est;
57 }
58 }
Cong Wang7dc68e92018-02-05 14:41:45 -080059
Patrick McHardy58590342007-12-04 23:40:05 -080060 return NULL;
61}
Cong Wang7dc68e92018-02-05 14:41:45 -080062
Cong Wang3427b2a2018-03-01 18:58:38 -080063struct xt_rateest *xt_rateest_lookup(struct net *net, const char *name)
Cong Wang7dc68e92018-02-05 14:41:45 -080064{
Cong Wang3427b2a2018-03-01 18:58:38 -080065 struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
Cong Wang7dc68e92018-02-05 14:41:45 -080066 struct xt_rateest *est;
67
Cong Wang3427b2a2018-03-01 18:58:38 -080068 mutex_lock(&xn->hash_lock);
69 est = __xt_rateest_lookup(xn, name);
70 mutex_unlock(&xn->hash_lock);
Cong Wang7dc68e92018-02-05 14:41:45 -080071 return est;
72}
Patrick McHardy58590342007-12-04 23:40:05 -080073EXPORT_SYMBOL_GPL(xt_rateest_lookup);
74
Cong Wang3427b2a2018-03-01 18:58:38 -080075void xt_rateest_put(struct net *net, struct xt_rateest *est)
Patrick McHardy58590342007-12-04 23:40:05 -080076{
Cong Wang3427b2a2018-03-01 18:58:38 -080077 struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
78
79 mutex_lock(&xn->hash_lock);
Patrick McHardy58590342007-12-04 23:40:05 -080080 if (--est->refcnt == 0) {
81 hlist_del(&est->list);
Eric Dumazet1c0d32f2016-12-04 09:48:16 -080082 gen_kill_estimator(&est->rate_est);
Eric Dumazetc7de2cf2010-06-09 02:09:23 +000083 /*
84 * gen_estimator est_timer() might access est->lock or bstats,
85 * wait a RCU grace period before freeing 'est'
86 */
Paul E. McKenneycefcb602011-05-02 01:00:18 -070087 kfree_rcu(est, rcu);
Patrick McHardy58590342007-12-04 23:40:05 -080088 }
Cong Wang3427b2a2018-03-01 18:58:38 -080089 mutex_unlock(&xn->hash_lock);
Patrick McHardy58590342007-12-04 23:40:05 -080090}
91EXPORT_SYMBOL_GPL(xt_rateest_put);
92
93static unsigned int
Jan Engelhardt4b560b42009-07-05 19:43:26 +020094xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par)
Patrick McHardy58590342007-12-04 23:40:05 -080095{
Jan Engelhardt7eb35582008-10-08 11:35:19 +020096 const struct xt_rateest_target_info *info = par->targinfo;
Ahmed S. Darwish50dc9a82021-10-16 10:49:09 +020097 struct gnet_stats_basic_sync *stats = &info->est->bstats;
Patrick McHardy58590342007-12-04 23:40:05 -080098
99 spin_lock_bh(&info->est->lock);
Ahmed S. Darwish50dc9a82021-10-16 10:49:09 +0200100 u64_stats_add(&stats->bytes, skb->len);
101 u64_stats_inc(&stats->packets);
Patrick McHardy58590342007-12-04 23:40:05 -0800102 spin_unlock_bh(&info->est->lock);
103
104 return XT_CONTINUE;
105}
106
Jan Engelhardt135367b2010-03-19 17:16:42 +0100107static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
Patrick McHardy58590342007-12-04 23:40:05 -0800108{
Cong Wang3427b2a2018-03-01 18:58:38 -0800109 struct xt_rateest_net *xn = net_generic(par->net, xt_rateest_id);
Jan Engelhardtaf5d6dc2008-10-08 11:35:19 +0200110 struct xt_rateest_target_info *info = par->targinfo;
Patrick McHardy58590342007-12-04 23:40:05 -0800111 struct xt_rateest *est;
112 struct {
Patrick McHardy1e904742008-01-22 22:11:17 -0800113 struct nlattr opt;
Patrick McHardy58590342007-12-04 23:40:05 -0800114 struct gnet_estimator est;
115 } cfg;
Jan Engelhardt4a5a5c72010-03-19 17:32:59 +0100116 int ret;
Patrick McHardy58590342007-12-04 23:40:05 -0800117
Florian Westphal6cb56212020-12-22 23:23:56 +0100118 if (strnlen(info->name, sizeof(est->name)) >= sizeof(est->name))
119 return -ENAMETOOLONG;
120
Gao Feng7bdc6622016-09-18 10:52:25 +0800121 net_get_random_once(&jhash_rnd, sizeof(jhash_rnd));
Jan Engelhardt5191d502010-01-04 16:27:25 +0100122
Cong Wang3427b2a2018-03-01 18:58:38 -0800123 mutex_lock(&xn->hash_lock);
124 est = __xt_rateest_lookup(xn, info->name);
Patrick McHardy58590342007-12-04 23:40:05 -0800125 if (est) {
Cong Wang3427b2a2018-03-01 18:58:38 -0800126 mutex_unlock(&xn->hash_lock);
Patrick McHardy58590342007-12-04 23:40:05 -0800127 /*
128 * If estimator parameters are specified, they must match the
129 * existing estimator.
130 */
131 if ((!info->interval && !info->ewma_log) ||
132 (info->interval != est->params.interval ||
133 info->ewma_log != est->params.ewma_log)) {
Cong Wang3427b2a2018-03-01 18:58:38 -0800134 xt_rateest_put(par->net, est);
Jan Engelhardtd6b00a52010-03-25 16:34:45 +0100135 return -EINVAL;
Patrick McHardy58590342007-12-04 23:40:05 -0800136 }
137 info->est = est;
Jan Engelhardtd6b00a52010-03-25 16:34:45 +0100138 return 0;
Patrick McHardy58590342007-12-04 23:40:05 -0800139 }
140
Jan Engelhardt4a5a5c72010-03-19 17:32:59 +0100141 ret = -ENOMEM;
Patrick McHardy58590342007-12-04 23:40:05 -0800142 est = kzalloc(sizeof(*est), GFP_KERNEL);
143 if (!est)
144 goto err1;
145
Ahmed S. Darwish50dc9a82021-10-16 10:49:09 +0200146 gnet_stats_basic_sync_init(&est->bstats);
Wolfram Sang8556bce2022-08-18 23:02:24 +0200147 strscpy(est->name, info->name, sizeof(est->name));
Patrick McHardy58590342007-12-04 23:40:05 -0800148 spin_lock_init(&est->lock);
149 est->refcnt = 1;
150 est->params.interval = info->interval;
151 est->params.ewma_log = info->ewma_log;
152
Patrick McHardy1e904742008-01-22 22:11:17 -0800153 cfg.opt.nla_len = nla_attr_size(sizeof(cfg.est));
154 cfg.opt.nla_type = TCA_STATS_RATE_EST;
Patrick McHardy58590342007-12-04 23:40:05 -0800155 cfg.est.interval = info->interval;
156 cfg.est.ewma_log = info->ewma_log;
157
Eric Dumazet1c0d32f2016-12-04 09:48:16 -0800158 ret = gen_new_estimator(&est->bstats, NULL, &est->rate_est,
Eric Dumazetedb09eb2016-06-06 09:37:16 -0700159 &est->lock, NULL, &cfg.opt);
Jan Engelhardt4a5a5c72010-03-19 17:32:59 +0100160 if (ret < 0)
Patrick McHardy58590342007-12-04 23:40:05 -0800161 goto err2;
162
163 info->est = est;
Cong Wang3427b2a2018-03-01 18:58:38 -0800164 xt_rateest_hash_insert(xn, est);
165 mutex_unlock(&xn->hash_lock);
Jan Engelhardtd6b00a52010-03-25 16:34:45 +0100166 return 0;
Patrick McHardy58590342007-12-04 23:40:05 -0800167
168err2:
169 kfree(est);
170err1:
Cong Wang3427b2a2018-03-01 18:58:38 -0800171 mutex_unlock(&xn->hash_lock);
Jan Engelhardt4a5a5c72010-03-19 17:32:59 +0100172 return ret;
Patrick McHardy58590342007-12-04 23:40:05 -0800173}
174
Jan Engelhardta2df1642008-10-08 11:35:19 +0200175static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par)
Patrick McHardy58590342007-12-04 23:40:05 -0800176{
Jan Engelhardta2df1642008-10-08 11:35:19 +0200177 struct xt_rateest_target_info *info = par->targinfo;
Patrick McHardy58590342007-12-04 23:40:05 -0800178
Cong Wang3427b2a2018-03-01 18:58:38 -0800179 xt_rateest_put(par->net, info->est);
Patrick McHardy58590342007-12-04 23:40:05 -0800180}
181
Jan Engelhardt55b69e92008-10-08 11:35:01 +0200182static struct xt_target xt_rateest_tg_reg __read_mostly = {
183 .name = "RATEEST",
184 .revision = 0,
185 .family = NFPROTO_UNSPEC,
186 .target = xt_rateest_tg,
187 .checkentry = xt_rateest_tg_checkentry,
188 .destroy = xt_rateest_tg_destroy,
189 .targetsize = sizeof(struct xt_rateest_target_info),
Willem de Bruijnec231892017-01-02 17:19:46 -0500190 .usersize = offsetof(struct xt_rateest_target_info, est),
Jan Engelhardt55b69e92008-10-08 11:35:01 +0200191 .me = THIS_MODULE,
Patrick McHardy58590342007-12-04 23:40:05 -0800192};
193
Cong Wang3427b2a2018-03-01 18:58:38 -0800194static __net_init int xt_rateest_net_init(struct net *net)
195{
196 struct xt_rateest_net *xn = net_generic(net, xt_rateest_id);
197 int i;
198
199 mutex_init(&xn->hash_lock);
200 for (i = 0; i < ARRAY_SIZE(xn->hash); i++)
201 INIT_HLIST_HEAD(&xn->hash[i]);
202 return 0;
203}
204
Cong Wang3427b2a2018-03-01 18:58:38 -0800205static struct pernet_operations xt_rateest_net_ops = {
206 .init = xt_rateest_net_init,
Cong Wang3427b2a2018-03-01 18:58:38 -0800207 .id = &xt_rateest_id,
208 .size = sizeof(struct xt_rateest_net),
209};
210
Patrick McHardy58590342007-12-04 23:40:05 -0800211static int __init xt_rateest_tg_init(void)
212{
Cong Wang3427b2a2018-03-01 18:58:38 -0800213 int err = register_pernet_subsys(&xt_rateest_net_ops);
Patrick McHardy58590342007-12-04 23:40:05 -0800214
Cong Wang3427b2a2018-03-01 18:58:38 -0800215 if (err)
216 return err;
Jan Engelhardt55b69e92008-10-08 11:35:01 +0200217 return xt_register_target(&xt_rateest_tg_reg);
Patrick McHardy58590342007-12-04 23:40:05 -0800218}
219
220static void __exit xt_rateest_tg_fini(void)
221{
Jan Engelhardt55b69e92008-10-08 11:35:01 +0200222 xt_unregister_target(&xt_rateest_tg_reg);
Cong Wang3427b2a2018-03-01 18:58:38 -0800223 unregister_pernet_subsys(&xt_rateest_net_ops);
Patrick McHardy58590342007-12-04 23:40:05 -0800224}
225
226
227MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
228MODULE_LICENSE("GPL");
Jan Engelhardt2ae15b62008-01-14 23:42:28 -0800229MODULE_DESCRIPTION("Xtables: packet rate estimator");
Patrick McHardy58590342007-12-04 23:40:05 -0800230MODULE_ALIAS("ipt_RATEEST");
231MODULE_ALIAS("ip6t_RATEEST");
232module_init(xt_rateest_tg_init);
233module_exit(xt_rateest_tg_fini);