[NETFILTER]: x_tables: add RATEEST target
[safe/jmp/linux-2.6] / 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 <net/gen_stats.h>
15
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_RATEEST.h>
18 #include <net/netfilter/xt_rateest.h>
19
20 static DEFINE_MUTEX(xt_rateest_mutex);
21
22 #define RATEEST_HSIZE   16
23 static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly;
24 static unsigned int jhash_rnd __read_mostly;
25
26 static unsigned int xt_rateest_hash(const char *name)
27 {
28         return jhash(name, FIELD_SIZEOF(struct xt_rateest, name), jhash_rnd) &
29                (RATEEST_HSIZE - 1);
30 }
31
32 static void xt_rateest_hash_insert(struct xt_rateest *est)
33 {
34         unsigned int h;
35
36         h = xt_rateest_hash(est->name);
37         hlist_add_head(&est->list, &rateest_hash[h]);
38 }
39
40 struct xt_rateest *xt_rateest_lookup(const char *name)
41 {
42         struct xt_rateest *est;
43         struct hlist_node *n;
44         unsigned int h;
45
46         h = xt_rateest_hash(name);
47         mutex_lock(&xt_rateest_mutex);
48         hlist_for_each_entry(est, n, &rateest_hash[h], list) {
49                 if (strcmp(est->name, name) == 0) {
50                         est->refcnt++;
51                         mutex_unlock(&xt_rateest_mutex);
52                         return est;
53                 }
54         }
55         mutex_unlock(&xt_rateest_mutex);
56         return NULL;
57 }
58 EXPORT_SYMBOL_GPL(xt_rateest_lookup);
59
60 void xt_rateest_put(struct xt_rateest *est)
61 {
62         mutex_lock(&xt_rateest_mutex);
63         if (--est->refcnt == 0) {
64                 hlist_del(&est->list);
65                 gen_kill_estimator(&est->bstats, &est->rstats);
66                 kfree(est);
67         }
68         mutex_unlock(&xt_rateest_mutex);
69 }
70 EXPORT_SYMBOL_GPL(xt_rateest_put);
71
72 static unsigned int
73 xt_rateest_tg(struct sk_buff *skb,
74               const struct net_device *in,
75               const struct net_device *out,
76               unsigned int hooknum,
77               const struct xt_target *target,
78               const void *targinfo)
79 {
80         const struct xt_rateest_target_info *info = targinfo;
81         struct gnet_stats_basic *stats = &info->est->bstats;
82
83         spin_lock_bh(&info->est->lock);
84         stats->bytes += skb->len;
85         stats->packets++;
86         spin_unlock_bh(&info->est->lock);
87
88         return XT_CONTINUE;
89 }
90
91 static bool
92 xt_rateest_tg_checkentry(const char *tablename,
93                          const void *entry,
94                          const struct xt_target *target,
95                          void *targinfo,
96                          unsigned int hook_mask)
97 {
98         struct xt_rateest_target_info *info = (void *)targinfo;
99         struct xt_rateest *est;
100         struct {
101                 struct rtattr           opt;
102                 struct gnet_estimator   est;
103         } cfg;
104
105         est = xt_rateest_lookup(info->name);
106         if (est) {
107                 /*
108                  * If estimator parameters are specified, they must match the
109                  * existing estimator.
110                  */
111                 if ((!info->interval && !info->ewma_log) ||
112                     (info->interval != est->params.interval ||
113                      info->ewma_log != est->params.ewma_log)) {
114                         xt_rateest_put(est);
115                         return false;
116                 }
117                 info->est = est;
118                 return true;
119         }
120
121         est = kzalloc(sizeof(*est), GFP_KERNEL);
122         if (!est)
123                 goto err1;
124
125         strlcpy(est->name, info->name, sizeof(est->name));
126         spin_lock_init(&est->lock);
127         est->refcnt             = 1;
128         est->params.interval    = info->interval;
129         est->params.ewma_log    = info->ewma_log;
130
131         cfg.opt.rta_len         = RTA_LENGTH(sizeof(cfg.est));
132         cfg.opt.rta_type        = TCA_STATS_RATE_EST;
133         cfg.est.interval        = info->interval;
134         cfg.est.ewma_log        = info->ewma_log;
135
136         if (gen_new_estimator(&est->bstats, &est->rstats, &est->lock,
137                               &cfg.opt) < 0)
138                 goto err2;
139
140         info->est = est;
141         xt_rateest_hash_insert(est);
142
143         return true;
144
145 err2:
146         kfree(est);
147 err1:
148         return false;
149 }
150
151 static void xt_rateest_tg_destroy(const struct xt_target *target,
152                                   void *targinfo)
153 {
154         struct xt_rateest_target_info *info = targinfo;
155
156         xt_rateest_put(info->est);
157 }
158
159 static struct xt_target xt_rateest_target[] __read_mostly = {
160         {
161                 .family         = AF_INET,
162                 .name           = "RATEEST",
163                 .target         = xt_rateest_tg,
164                 .checkentry     = xt_rateest_tg_checkentry,
165                 .destroy        = xt_rateest_tg_destroy,
166                 .targetsize     = sizeof(struct xt_rateest_target_info),
167                 .me             = THIS_MODULE,
168         },
169         {
170                 .family         = AF_INET6,
171                 .name           = "RATEEST",
172                 .target         = xt_rateest_tg,
173                 .checkentry     = xt_rateest_tg_checkentry,
174                 .destroy        = xt_rateest_tg_destroy,
175                 .targetsize     = sizeof(struct xt_rateest_target_info),
176                 .me             = THIS_MODULE,
177         },
178 };
179
180 static int __init xt_rateest_tg_init(void)
181 {
182         unsigned int i;
183
184         for (i = 0; i < ARRAY_SIZE(rateest_hash); i++)
185                 INIT_HLIST_HEAD(&rateest_hash[i]);
186
187         get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
188         return xt_register_targets(xt_rateest_target,
189                                    ARRAY_SIZE(xt_rateest_target));
190 }
191
192 static void __exit xt_rateest_tg_fini(void)
193 {
194         xt_unregister_targets(xt_rateest_target, ARRAY_SIZE(xt_rateest_target));
195 }
196
197
198 MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
199 MODULE_LICENSE("GPL");
200 MODULE_DESCRIPTION("xtables rate estimator");
201 MODULE_ALIAS("ipt_RATEEST");
202 MODULE_ALIAS("ip6t_RATEEST");
203 module_init(xt_rateest_tg_init);
204 module_exit(xt_rateest_tg_fini);