netfilter: xt_RATEEST: acquire xt_rateest_mutex for hash insert
[pandora-kernel.git] / net / netfilter / xt_RATEEST.c
1 /*
2  * (C) 2007 Patrick McHardy <kaber@trash.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 #include <linux/module.h>
9 #include <linux/skbuff.h>
10 #include <linux/gen_stats.h>
11 #include <linux/jhash.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/random.h>
14 #include <linux/slab.h>
15 #include <net/gen_stats.h>
16 #include <net/netlink.h>
17
18 #include <linux/netfilter/x_tables.h>
19 #include <linux/netfilter/xt_RATEEST.h>
20 #include <net/netfilter/xt_rateest.h>
21
22 static DEFINE_MUTEX(xt_rateest_mutex);
23
24 #define RATEEST_HSIZE   16
25 static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly;
26 static unsigned int jhash_rnd __read_mostly;
27 static bool rnd_inited __read_mostly;
28
29 static unsigned int xt_rateest_hash(const char *name)
30 {
31         return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) &
32                (RATEEST_HSIZE - 1);
33 }
34
35 static void xt_rateest_hash_insert(struct xt_rateest *est)
36 {
37         unsigned int h;
38
39         h = xt_rateest_hash(est->name);
40         hlist_add_head(&est->list, &rateest_hash[h]);
41 }
42
43 static struct xt_rateest *__xt_rateest_lookup(const char *name)
44 {
45         struct xt_rateest *est;
46         struct hlist_node *n;
47         unsigned int h;
48
49         h = xt_rateest_hash(name);
50         hlist_for_each_entry(est, n, &rateest_hash[h], list) {
51                 if (strcmp(est->name, name) == 0) {
52                         est->refcnt++;
53                         return est;
54                 }
55         }
56
57         return NULL;
58 }
59
60 struct xt_rateest *xt_rateest_lookup(const char *name)
61 {
62         struct xt_rateest *est;
63
64         mutex_lock(&xt_rateest_mutex);
65         est = __xt_rateest_lookup(name);
66         mutex_unlock(&xt_rateest_mutex);
67         return est;
68 }
69 EXPORT_SYMBOL_GPL(xt_rateest_lookup);
70
71 void xt_rateest_put(struct xt_rateest *est)
72 {
73         mutex_lock(&xt_rateest_mutex);
74         if (--est->refcnt == 0) {
75                 hlist_del(&est->list);
76                 gen_kill_estimator(&est->bstats, &est->rstats);
77                 /*
78                  * gen_estimator est_timer() might access est->lock or bstats,
79                  * wait a RCU grace period before freeing 'est'
80                  */
81                 kfree_rcu(est, rcu);
82         }
83         mutex_unlock(&xt_rateest_mutex);
84 }
85 EXPORT_SYMBOL_GPL(xt_rateest_put);
86
87 static unsigned int
88 xt_rateest_tg(struct sk_buff *skb, const struct xt_action_param *par)
89 {
90         const struct xt_rateest_target_info *info = par->targinfo;
91         struct gnet_stats_basic_packed *stats = &info->est->bstats;
92
93         spin_lock_bh(&info->est->lock);
94         stats->bytes += skb->len;
95         stats->packets++;
96         spin_unlock_bh(&info->est->lock);
97
98         return XT_CONTINUE;
99 }
100
101 static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
102 {
103         struct xt_rateest_target_info *info = par->targinfo;
104         struct xt_rateest *est;
105         struct {
106                 struct nlattr           opt;
107                 struct gnet_estimator   est;
108         } cfg;
109         int ret;
110
111         if (unlikely(!rnd_inited)) {
112                 get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
113                 rnd_inited = true;
114         }
115
116         mutex_lock(&xt_rateest_mutex);
117         est = __xt_rateest_lookup(info->name);
118         if (est) {
119                 mutex_unlock(&xt_rateest_mutex);
120                 /*
121                  * If estimator parameters are specified, they must match the
122                  * existing estimator.
123                  */
124                 if ((!info->interval && !info->ewma_log) ||
125                     (info->interval != est->params.interval ||
126                      info->ewma_log != est->params.ewma_log)) {
127                         xt_rateest_put(est);
128                         return -EINVAL;
129                 }
130                 info->est = est;
131                 return 0;
132         }
133
134         ret = -ENOMEM;
135         est = kzalloc(sizeof(*est), GFP_KERNEL);
136         if (!est)
137                 goto err1;
138
139         strlcpy(est->name, info->name, sizeof(est->name));
140         spin_lock_init(&est->lock);
141         est->refcnt             = 1;
142         est->params.interval    = info->interval;
143         est->params.ewma_log    = info->ewma_log;
144
145         cfg.opt.nla_len         = nla_attr_size(sizeof(cfg.est));
146         cfg.opt.nla_type        = TCA_STATS_RATE_EST;
147         cfg.est.interval        = info->interval;
148         cfg.est.ewma_log        = info->ewma_log;
149
150         ret = gen_new_estimator(&est->bstats, &est->rstats,
151                                 &est->lock, &cfg.opt);
152         if (ret < 0)
153                 goto err2;
154
155         info->est = est;
156         xt_rateest_hash_insert(est);
157         mutex_unlock(&xt_rateest_mutex);
158         return 0;
159
160 err2:
161         kfree(est);
162 err1:
163         mutex_unlock(&xt_rateest_mutex);
164         return ret;
165 }
166
167 static void xt_rateest_tg_destroy(const struct xt_tgdtor_param *par)
168 {
169         struct xt_rateest_target_info *info = par->targinfo;
170
171         xt_rateest_put(info->est);
172 }
173
174 static struct xt_target xt_rateest_tg_reg __read_mostly = {
175         .name       = "RATEEST",
176         .revision   = 0,
177         .family     = NFPROTO_UNSPEC,
178         .target     = xt_rateest_tg,
179         .checkentry = xt_rateest_tg_checkentry,
180         .destroy    = xt_rateest_tg_destroy,
181         .targetsize = sizeof(struct xt_rateest_target_info),
182         .me         = THIS_MODULE,
183 };
184
185 static int __init xt_rateest_tg_init(void)
186 {
187         unsigned int i;
188
189         for (i = 0; i < ARRAY_SIZE(rateest_hash); i++)
190                 INIT_HLIST_HEAD(&rateest_hash[i]);
191
192         return xt_register_target(&xt_rateest_tg_reg);
193 }
194
195 static void __exit xt_rateest_tg_fini(void)
196 {
197         xt_unregister_target(&xt_rateest_tg_reg);
198 }
199
200
201 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
202 MODULE_LICENSE("GPL");
203 MODULE_DESCRIPTION("Xtables: packet rate estimator");
204 MODULE_ALIAS("ipt_RATEEST");
205 MODULE_ALIAS("ip6t_RATEEST");
206 module_init(xt_rateest_tg_init);
207 module_exit(xt_rateest_tg_fini);