fix for f12, gcc4.4
[iptables.git] / extensions / libipt_hashlimit.c
1 /* iptables match extension for limiting packets per destination
2  *
3  * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
4  *
5  * Development of this code was funded by Astaro AG, http://www.astaro.com/
6  *
7  * Based on ipt_limit.c by
8  * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
9  * Hervé Eychenne    <rv@wallfire.org>
10  * 
11  * Error corections by nmalykh@bilim.com (22.01.2005)
12  */
13
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <getopt.h>
18 #include <iptables.h>
19 #include <stddef.h>
20 #include <linux/netfilter_ipv4/ip_tables.h>
21 #include <linux/netfilter_ipv4/ipt_hashlimit.h>
22
23 #define IPT_HASHLIMIT_BURST     5
24
25 /* miliseconds */
26 #define IPT_HASHLIMIT_GCINTERVAL        1000
27 #define IPT_HASHLIMIT_EXPIRE    10000
28
29 /* Function which prints out usage message. */
30 static void
31 help(void)
32 {
33         printf(
34 "hashlimit v%s options:\n"
35 "--hashlimit <avg>              max average match rate\n"
36 "                                [Packets per second unless followed by \n"
37 "                                /sec /minute /hour /day postfixes]\n"
38 "--hashlimit-mode <mode>                mode is a comma-separated list of\n"
39 "                                       dstip,srcip,dstport,srcport\n"
40 "--hashlimit-name <name>                name for /proc/net/ipt_hashlimit/\n"
41 "[--hashlimit-burst <num>]      number to match in a burst, default %u\n"
42 "[--hashlimit-htable-size <num>]        number of hashtable buckets\n"
43 "[--hashlimit-htable-max <num>] number of hashtable entries\n"
44 "[--hashlimit-htable-gcinterval]        interval between garbage collection runs\n"
45 "[--hashlimit-htable-expire]    after which time are idle entries expired?\n"
46 "\n", IPTABLES_VERSION, IPT_HASHLIMIT_BURST);
47 }
48
49 static struct option opts[] = {
50         { "hashlimit", 1, 0, '%' },
51         { "hashlimit-burst", 1, 0, '$' },
52         { "hashlimit-htable-size", 1, 0, '&' },
53         { "hashlimit-htable-max", 1, 0, '*' },
54         { "hashlimit-htable-gcinterval", 1, 0, '(' },
55         { "hashlimit-htable-expire", 1, 0, ')' },
56         { "hashlimit-mode", 1, 0, '_' },
57         { "hashlimit-name", 1, 0, '"' },
58         { 0 }
59 };
60
61 static
62 int parse_rate(const char *rate, u_int32_t *val)
63 {
64         const char *delim;
65         u_int32_t r;
66         u_int32_t mult = 1;  /* Seconds by default. */
67
68         delim = strchr(rate, '/');
69         if (delim) {
70                 if (strlen(delim+1) == 0)
71                         return 0;
72
73                 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
74                         mult = 1;
75                 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
76                         mult = 60;
77                 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
78                         mult = 60*60;
79                 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
80                         mult = 24*60*60;
81                 else
82                         return 0;
83         }
84         r = atoi(rate);
85         if (!r)
86                 return 0;
87
88         /* This would get mapped to infinite (1/day is minimum they
89            can specify, so we're ok at that end). */
90         if (r / mult > IPT_HASHLIMIT_SCALE)
91                 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
92
93         *val = IPT_HASHLIMIT_SCALE * mult / r;
94         return 1;
95 }
96
97 /* Initialize the match. */
98 static void
99 init(struct ipt_entry_match *m, unsigned int *nfcache)
100 {
101         struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *)m->data;
102
103         r->cfg.burst = IPT_HASHLIMIT_BURST;
104         r->cfg.gc_interval = IPT_HASHLIMIT_GCINTERVAL;
105         r->cfg.expire = IPT_HASHLIMIT_EXPIRE;
106
107 }
108
109
110 /* Parse a 'mode' parameter into the required bitmask */
111 static int parse_mode(struct ipt_hashlimit_info *r, char *optarg)
112 {
113         char *tok;
114         char *arg = strdup(optarg);
115
116         if (!arg)
117                 return -1;
118
119         r->cfg.mode = 0;
120
121         for (tok = strtok(arg, ",|");
122              tok;
123              tok = strtok(NULL, ",|")) {
124                 if (!strcmp(tok, "dstip"))
125                         r->cfg.mode |= IPT_HASHLIMIT_HASH_DIP;
126                 else if (!strcmp(tok, "srcip"))
127                         r->cfg.mode |= IPT_HASHLIMIT_HASH_SIP;
128                 else if (!strcmp(tok, "srcport"))
129                         r->cfg.mode |= IPT_HASHLIMIT_HASH_SPT;
130                 else if (!strcmp(tok, "dstport"))
131                         r->cfg.mode |= IPT_HASHLIMIT_HASH_DPT;
132                 else {
133                         free(arg);
134                         return -1;
135                 }
136         }
137         free(arg);
138         return 0;
139 }
140
141 #define PARAM_LIMIT             0x00000001
142 #define PARAM_BURST             0x00000002
143 #define PARAM_MODE              0x00000004
144 #define PARAM_NAME              0x00000008
145 #define PARAM_SIZE              0x00000010
146 #define PARAM_MAX               0x00000020
147 #define PARAM_GCINTERVAL        0x00000040
148 #define PARAM_EXPIRE            0x00000080
149
150 /* Function which parses command options; returns true if it
151    ate an option */
152 static int
153 parse(int c, char **argv, int invert, unsigned int *flags,
154       const struct ipt_entry *entry,
155       unsigned int *nfcache,
156       struct ipt_entry_match **match)
157 {
158         struct ipt_hashlimit_info *r = 
159                         (struct ipt_hashlimit_info *)(*match)->data;
160         unsigned int num;
161
162         switch(c) {
163         case '%':
164                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
165                 if (!parse_rate(optarg, &r->cfg.avg))
166                         exit_error(PARAMETER_PROBLEM,
167                                    "bad rate `%s'", optarg);
168                 *flags |= PARAM_LIMIT;
169                 break;
170
171         case '$':
172                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
173                 if (string_to_number(optarg, 0, 10000, &num) == -1)
174                         exit_error(PARAMETER_PROBLEM,
175                                    "bad --hashlimit-burst `%s'", optarg);
176                 r->cfg.burst = num;
177                 *flags |= PARAM_BURST;
178                 break;
179         case '&':
180                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
181                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
182                         exit_error(PARAMETER_PROBLEM,
183                                 "bad --hashlimit-htable-size: `%s'", optarg);
184                 r->cfg.size = num;
185                 *flags |= PARAM_SIZE;
186                 break;
187         case '*':
188                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
189                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
190                         exit_error(PARAMETER_PROBLEM,
191                                 "bad --hashlimit-htable-max: `%s'", optarg);
192                 r->cfg.max = num;
193                 *flags |= PARAM_MAX;
194                 break;
195         case '(':
196                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
197                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
198                         exit_error(PARAMETER_PROBLEM,
199                                 "bad --hashlimit-htable-gcinterval: `%s'", 
200                                 optarg);
201                 /* FIXME: not HZ dependent!! */
202                 r->cfg.gc_interval = num;
203                 *flags |= PARAM_GCINTERVAL;
204                 break;
205         case ')':
206                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
207                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
208                         exit_error(PARAMETER_PROBLEM,
209                                 "bad --hashlimit-htable-expire: `%s'", optarg);
210                 /* FIXME: not HZ dependent */
211                 r->cfg.expire = num;
212                 *flags |= PARAM_EXPIRE;
213                 break;
214         case '_':
215                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
216                 if (parse_mode(r, optarg) < 0)
217                         exit_error(PARAMETER_PROBLEM, 
218                                    "bad --hashlimit-mode: `%s'\n", optarg);
219                 *flags |= PARAM_MODE;
220                 break;
221         case '"':
222                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
223                 if (strlen(optarg) == 0)
224                         exit_error(PARAMETER_PROBLEM, "Zero-length name?");
225                 strncpy(r->name, optarg, sizeof(r->name));
226                 *flags |= PARAM_NAME;
227                 break;
228         default:
229                 return 0;
230         }
231
232         if (invert)
233                 exit_error(PARAMETER_PROBLEM,
234                            "hashlimit does not support invert");
235
236         return 1;
237 }
238
239 /* Final check; nothing. */
240 static void final_check(unsigned int flags)
241 {
242         if (!(flags & PARAM_LIMIT))
243                 exit_error(PARAMETER_PROBLEM,
244                                 "You have to specify --hashlimit");
245         if (!(flags & PARAM_MODE))
246                 exit_error(PARAMETER_PROBLEM,
247                                 "You have to specify --hashlimit-mode");
248         if (!(flags & PARAM_NAME))
249                 exit_error(PARAMETER_PROBLEM,
250                                 "You have to specify --hashlimit-name");
251 }
252
253 static struct rates
254 {
255         const char *name;
256         u_int32_t mult;
257 } rates[] = { { "day", IPT_HASHLIMIT_SCALE*24*60*60 },
258               { "hour", IPT_HASHLIMIT_SCALE*60*60 },
259               { "min", IPT_HASHLIMIT_SCALE*60 },
260               { "sec", IPT_HASHLIMIT_SCALE } };
261
262 static void print_rate(u_int32_t period)
263 {
264         unsigned int i;
265
266         for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
267                 if (period > rates[i].mult
268             || rates[i].mult/period < rates[i].mult%period)
269                         break;
270         }
271
272         printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
273 }
274
275 static void print_mode(const struct ipt_hashlimit_info *r, char separator)
276 {
277         int prevmode = 0;
278
279         if (r->cfg.mode & IPT_HASHLIMIT_HASH_SIP) {
280                 if (prevmode)
281                         putchar(separator);
282                 fputs("srcip", stdout);
283                 prevmode = 1;
284         }
285         if (r->cfg.mode & IPT_HASHLIMIT_HASH_SPT) {
286                 if (prevmode)
287                         putchar(separator);
288                 fputs("srcport", stdout);
289                 prevmode = 1;
290         }
291         if (r->cfg.mode & IPT_HASHLIMIT_HASH_DIP) {
292                 if (prevmode)
293                         putchar(separator);
294                 fputs("dstip", stdout);
295                 prevmode = 1;
296         }
297         if (r->cfg.mode & IPT_HASHLIMIT_HASH_DPT) {
298                 if (prevmode)
299                         putchar(separator);
300                 fputs("dstport", stdout);
301         }
302         putchar(' ');
303 }
304
305 /* Prints out the matchinfo. */
306 static void
307 print(const struct ipt_ip *ip,
308       const struct ipt_entry_match *match,
309       int numeric)
310 {
311         struct ipt_hashlimit_info *r = 
312                 (struct ipt_hashlimit_info *)match->data;
313         fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
314         printf("burst %u ", r->cfg.burst);
315         fputs("mode ", stdout);
316         print_mode(r, '-');
317         if (r->cfg.size)
318                 printf("htable-size %u ", r->cfg.size);
319         if (r->cfg.max)
320                 printf("htable-max %u ", r->cfg.max);
321         if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
322                 printf("htable-gcinterval %u ", r->cfg.gc_interval);
323         if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
324                 printf("htable-expire %u ", r->cfg.expire);
325 }
326
327 /* FIXME: Make minimalist: only print rate if not default --RR */
328 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
329 {
330         struct ipt_hashlimit_info *r = 
331                 (struct ipt_hashlimit_info *)match->data;
332
333         fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
334         if (r->cfg.burst != IPT_HASHLIMIT_BURST)
335                 printf("--hashlimit-burst %u ", r->cfg.burst);
336
337         fputs("--hashlimit-mode ", stdout);
338         print_mode(r, ',');
339         
340         printf("--hashlimit-name %s ", r->name);
341
342         if (r->cfg.size)
343                 printf("--hashlimit-htable-size %u ", r->cfg.size);
344         if (r->cfg.max)
345                 printf("--hashlimit-htable-max %u ", r->cfg.max);
346         if (r->cfg.gc_interval != IPT_HASHLIMIT_GCINTERVAL)
347                 printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
348         if (r->cfg.expire != IPT_HASHLIMIT_EXPIRE)
349                 printf("--hashlimit-htable-expire %u ", r->cfg.expire);
350 }
351
352 static struct iptables_match hashlimit = { NULL,
353         .name           = "hashlimit",
354         .version        = IPTABLES_VERSION,
355         .size           = IPT_ALIGN(sizeof(struct ipt_hashlimit_info)),
356         .userspacesize  = offsetof(struct ipt_hashlimit_info, hinfo),
357         .help           = &help,
358         .init           = &init,
359         .parse          = &parse,
360         .final_check    = &final_check,
361         .print          = &print,
362         .save           = &save,
363         .extra_opts     = opts
364 };
365
366 void _init(void)
367 {
368         register_match(&hashlimit);
369 }