Sapan says vnet_tun is obsolete.
[iptables.git] / extensions / libxt_hashlimit.c
1 /* ip6tables 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 #include <stdbool.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <getopt.h>
18 #include <xtables.h>
19 #include <stddef.h>
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter/xt_hashlimit.h>
22
23 #define XT_HASHLIMIT_BURST      5
24
25 /* miliseconds */
26 #define XT_HASHLIMIT_GCINTERVAL 1000
27 #define XT_HASHLIMIT_EXPIRE     10000
28
29 /* Function which prints out usage message. */
30 static void hashlimit_help(void)
31 {
32         printf(
33 "hashlimit match options:\n"
34 "--hashlimit <avg>              max average match rate\n"
35 "                                [Packets per second unless followed by \n"
36 "                                /sec /minute /hour /day postfixes]\n"
37 "--hashlimit-mode <mode>                mode is a comma-separated list of\n"
38 "                                       dstip,srcip,dstport,srcport\n"
39 "--hashlimit-name <name>                name for /proc/net/ipt_hashlimit/\n"
40 "[--hashlimit-burst <num>]      number to match in a burst, default %u\n"
41 "[--hashlimit-htable-size <num>]        number of hashtable buckets\n"
42 "[--hashlimit-htable-max <num>] number of hashtable entries\n"
43 "[--hashlimit-htable-gcinterval]        interval between garbage collection runs\n"
44 "[--hashlimit-htable-expire]    after which time are idle entries expired?\n",
45 XT_HASHLIMIT_BURST);
46 }
47
48 static void hashlimit_mt_help(void)
49 {
50         printf(
51 "hashlimit match options:\n"
52 "  --hashlimit-upto <avg>           max average match rate\n"
53 "                                   [Packets per second unless followed by \n"
54 "                                   /sec /minute /hour /day postfixes]\n"
55 "  --hashlimit-above <avg>          min average match rate\n"
56 "  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
57 "                                   dstip,srcip,dstport,srcport (or none)\n"
58 "  --hashlimit-srcmask <length>     source address grouping prefix length\n"
59 "  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
60 "  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
61 "  --hashlimit-burst <num>          number to match in a burst, default %u\n"
62 "  --hashlimit-htable-size <num>    number of hashtable buckets\n"
63 "  --hashlimit-htable-max <num>     number of hashtable entries\n"
64 "  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
65 "  --hashlimit-htable-expire        after which time are idle entries expired?\n"
66 "\n", XT_HASHLIMIT_BURST);
67 }
68
69 static const struct option hashlimit_opts[] = {
70         { "hashlimit", 1, NULL, '%' },
71         { "hashlimit-burst", 1, NULL, '$' },
72         { "hashlimit-htable-size", 1, NULL, '&' },
73         { "hashlimit-htable-max", 1, NULL, '*' },
74         { "hashlimit-htable-gcinterval", 1, NULL, '(' },
75         { "hashlimit-htable-expire", 1, NULL, ')' },
76         { "hashlimit-mode", 1, NULL, '_' },
77         { "hashlimit-name", 1, NULL, '"' },
78         { .name = NULL }
79 };
80
81 static const struct option hashlimit_mt_opts[] = {
82         {.name = "hashlimit-upto",              .has_arg = true, .val = '%'},
83         {.name = "hashlimit-above",             .has_arg = true, .val = '^'},
84         {.name = "hashlimit",                   .has_arg = true, .val = '%'},
85         {.name = "hashlimit-srcmask",           .has_arg = true, .val = '<'},
86         {.name = "hashlimit-dstmask",           .has_arg = true, .val = '>'},
87         {.name = "hashlimit-burst",             .has_arg = true, .val = '$'},
88         {.name = "hashlimit-htable-size",       .has_arg = true, .val = '&'},
89         {.name = "hashlimit-htable-max",        .has_arg = true, .val = '*'},
90         {.name = "hashlimit-htable-gcinterval", .has_arg = true, .val = '('},
91         {.name = "hashlimit-htable-expire",     .has_arg = true, .val = ')'},
92         {.name = "hashlimit-mode",              .has_arg = true, .val = '_'},
93         {.name = "hashlimit-name",              .has_arg = true, .val = '"'},
94         {},
95 };
96
97 static
98 int parse_rate(const char *rate, u_int32_t *val)
99 {
100         const char *delim;
101         u_int32_t r;
102         u_int32_t mult = 1;  /* Seconds by default. */
103
104         delim = strchr(rate, '/');
105         if (delim) {
106                 if (strlen(delim+1) == 0)
107                         return 0;
108
109                 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
110                         mult = 1;
111                 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
112                         mult = 60;
113                 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
114                         mult = 60*60;
115                 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
116                         mult = 24*60*60;
117                 else
118                         return 0;
119         }
120         r = atoi(rate);
121         if (!r)
122                 return 0;
123
124         /* This would get mapped to infinite (1/day is minimum they
125            can specify, so we're ok at that end). */
126         if (r / mult > XT_HASHLIMIT_SCALE)
127                 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
128
129         *val = XT_HASHLIMIT_SCALE * mult / r;
130         return 1;
131 }
132
133 /* Initialize the match. */
134 static void hashlimit_init(struct xt_entry_match *m)
135 {
136         struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
137
138         r->cfg.mode = 0;
139         r->cfg.burst = XT_HASHLIMIT_BURST;
140         r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
141         r->cfg.expire = XT_HASHLIMIT_EXPIRE;
142
143 }
144
145 static void hashlimit_mt4_init(struct xt_entry_match *match)
146 {
147         struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
148
149         info->cfg.mode        = 0;
150         info->cfg.burst       = XT_HASHLIMIT_BURST;
151         info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
152         info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
153         info->cfg.srcmask     = 32;
154         info->cfg.dstmask     = 32;
155 }
156
157 static void hashlimit_mt6_init(struct xt_entry_match *match)
158 {
159         struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
160
161         info->cfg.mode        = 0;
162         info->cfg.burst       = XT_HASHLIMIT_BURST;
163         info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
164         info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
165         info->cfg.srcmask     = 128;
166         info->cfg.dstmask     = 128;
167 }
168
169 /* Parse a 'mode' parameter into the required bitmask */
170 static int parse_mode(uint32_t *mode, char *option_arg)
171 {
172         char *tok;
173         char *arg = strdup(option_arg);
174
175         if (!arg)
176                 return -1;
177
178         for (tok = strtok(arg, ",|");
179              tok;
180              tok = strtok(NULL, ",|")) {
181                 if (!strcmp(tok, "dstip"))
182                         *mode |= XT_HASHLIMIT_HASH_DIP;
183                 else if (!strcmp(tok, "srcip"))
184                         *mode |= XT_HASHLIMIT_HASH_SIP;
185                 else if (!strcmp(tok, "srcport"))
186                         *mode |= XT_HASHLIMIT_HASH_SPT;
187                 else if (!strcmp(tok, "dstport"))
188                         *mode |= XT_HASHLIMIT_HASH_DPT;
189                 else {
190                         free(arg);
191                         return -1;
192                 }
193         }
194         free(arg);
195         return 0;
196 }
197
198 enum {
199         PARAM_LIMIT      = 1 << 0,
200         PARAM_BURST      = 1 << 1,
201         PARAM_MODE       = 1 << 2,
202         PARAM_NAME       = 1 << 3,
203         PARAM_SIZE       = 1 << 4,
204         PARAM_MAX        = 1 << 5,
205         PARAM_GCINTERVAL = 1 << 6,
206         PARAM_EXPIRE     = 1 << 7,
207         PARAM_SRCMASK    = 1 << 8,
208         PARAM_DSTMASK    = 1 << 9,
209 };
210
211 /* Function which parses command options; returns true if it
212    ate an option */
213 static int
214 hashlimit_parse(int c, char **argv, int invert, unsigned int *flags,
215                 const void *entry, struct xt_entry_match **match)
216 {
217         struct xt_hashlimit_info *r = 
218                         (struct xt_hashlimit_info *)(*match)->data;
219         unsigned int num;
220
221         switch(c) {
222         case '%':
223                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit",
224                           *flags & PARAM_LIMIT);
225                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
226                 if (!parse_rate(optarg, &r->cfg.avg))
227                         exit_error(PARAMETER_PROBLEM,
228                                    "bad rate `%s'", optarg);
229                 *flags |= PARAM_LIMIT;
230                 break;
231
232         case '$':
233                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-burst",
234                           *flags & PARAM_BURST);
235                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
236                 if (string_to_number(optarg, 0, 10000, &num) == -1)
237                         exit_error(PARAMETER_PROBLEM,
238                                    "bad --hashlimit-burst `%s'", optarg);
239                 r->cfg.burst = num;
240                 *flags |= PARAM_BURST;
241                 break;
242         case '&':
243                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-size",
244                           *flags & PARAM_SIZE);
245                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
246                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
247                         exit_error(PARAMETER_PROBLEM,
248                                 "bad --hashlimit-htable-size: `%s'", optarg);
249                 r->cfg.size = num;
250                 *flags |= PARAM_SIZE;
251                 break;
252         case '*':
253                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-max",
254                           *flags & PARAM_MAX);
255                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
256                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
257                         exit_error(PARAMETER_PROBLEM,
258                                 "bad --hashlimit-htable-max: `%s'", optarg);
259                 r->cfg.max = num;
260                 *flags |= PARAM_MAX;
261                 break;
262         case '(':
263                 param_act(P_ONLY_ONCE, "hashlimit",
264                           "--hashlimit-htable-gcinterval",
265                           *flags & PARAM_GCINTERVAL);
266                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
267                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
268                         exit_error(PARAMETER_PROBLEM,
269                                 "bad --hashlimit-htable-gcinterval: `%s'", 
270                                 optarg);
271                 /* FIXME: not HZ dependent!! */
272                 r->cfg.gc_interval = num;
273                 *flags |= PARAM_GCINTERVAL;
274                 break;
275         case ')':
276                 param_act(P_ONLY_ONCE, "hashlimit",
277                           "--hashlimit-htable-expire", *flags & PARAM_EXPIRE);
278                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
279                 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
280                         exit_error(PARAMETER_PROBLEM,
281                                 "bad --hashlimit-htable-expire: `%s'", optarg);
282                 /* FIXME: not HZ dependent */
283                 r->cfg.expire = num;
284                 *flags |= PARAM_EXPIRE;
285                 break;
286         case '_':
287                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-mode",
288                           *flags & PARAM_MODE);
289                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
290                 if (parse_mode(&r->cfg.mode, optarg) < 0)
291                         exit_error(PARAMETER_PROBLEM, 
292                                    "bad --hashlimit-mode: `%s'\n", optarg);
293                 *flags |= PARAM_MODE;
294                 break;
295         case '"':
296                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-name",
297                           *flags & PARAM_NAME);
298                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
299                 if (strlen(optarg) == 0)
300                         exit_error(PARAMETER_PROBLEM, "Zero-length name?");
301                 strncpy(r->name, optarg, sizeof(r->name));
302                 *flags |= PARAM_NAME;
303                 break;
304         default:
305                 return 0;
306         }
307
308         if (invert)
309                 exit_error(PARAMETER_PROBLEM,
310                            "hashlimit does not support invert");
311
312         return 1;
313 }
314
315 static int
316 hashlimit_mt_parse(struct xt_hashlimit_mtinfo1 *info, unsigned int *flags,
317                    int c, int invert, unsigned int maxmask)
318 {
319         unsigned int num;
320
321         switch(c) {
322         case '%': /* --hashlimit / --hashlimit-below */
323                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-upto",
324                           *flags & PARAM_LIMIT);
325                 if (invert)
326                         info->cfg.mode |= XT_HASHLIMIT_INVERT;
327                 if (!parse_rate(optarg, &info->cfg.avg))
328                         param_act(P_BAD_VALUE, "hashlimit",
329                                   "--hashlimit-upto", optarg);
330                 *flags |= PARAM_LIMIT;
331                 return true;
332
333         case '^': /* --hashlimit-above == !--hashlimit-below */
334                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-above",
335                           *flags & PARAM_LIMIT);
336                 if (!invert)
337                         info->cfg.mode |= XT_HASHLIMIT_INVERT;
338                 if (!parse_rate(optarg, &info->cfg.avg))
339                         param_act(P_BAD_VALUE, "hashlimit",
340                                   "--hashlimit-above", optarg);
341                 *flags |= PARAM_LIMIT;
342                 return true;
343
344         case '$': /* --hashlimit-burst */
345                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-burst",
346                           *flags & PARAM_BURST);
347                 if (!strtonum(optarg, NULL, &num, 0, 10000))
348                         param_act(P_BAD_VALUE, "hashlimit",
349                                   "--hashlimit-burst", optarg);
350                 info->cfg.burst = num;
351                 *flags |= PARAM_BURST;
352                 return true;
353
354         case '&': /* --hashlimit-htable-size */
355                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-size",
356                           *flags & PARAM_SIZE);
357                 if (!strtonum(optarg, NULL, &num, 0, 0xffffffff))
358                         param_act(P_BAD_VALUE, "hashlimit",
359                                   "--hashlimit-htable-size", optarg);
360                 info->cfg.size = num;
361                 *flags |= PARAM_SIZE;
362                 return true;
363
364         case '*': /* --hashlimit-htable-max */
365                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-max",
366                           *flags & PARAM_MAX);
367                 if (!strtonum(optarg, NULL, &num, 0, 0xffffffff))
368                         param_act(P_BAD_VALUE, "hashlimit",
369                                   "--hashlimit-htable-max", optarg);
370                 info->cfg.max = num;
371                 *flags |= PARAM_MAX;
372                 return true;
373
374         case '(': /* --hashlimit-htable-gcinterval */
375                 param_act(P_ONLY_ONCE, "hashlimit",
376                           "--hashlimit-htable-gcinterval",
377                           *flags & PARAM_GCINTERVAL);
378                 if (!strtonum(optarg, NULL, &num, 0, 0xffffffff))
379                         param_act(P_BAD_VALUE, "hashlimit",
380                                   "--hashlimit-htable-gcinterval", optarg);
381                 /* FIXME: not HZ dependent!! */
382                 info->cfg.gc_interval = num;
383                 *flags |= PARAM_GCINTERVAL;
384                 return true;
385
386         case ')': /* --hashlimit-htable-expire */
387                 param_act(P_ONLY_ONCE, "hashlimit",
388                           "--hashlimit-htable-expire", *flags & PARAM_EXPIRE);
389                 if (!strtonum(optarg, NULL, &num, 0, 0xffffffff))
390                         param_act(P_BAD_VALUE, "hashlimit",
391                                   "--hashlimit-htable-expire", optarg);
392                 /* FIXME: not HZ dependent */
393                 info->cfg.expire = num;
394                 *flags |= PARAM_EXPIRE;
395                 return true;
396
397         case '_':
398                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-mode",
399                           *flags & PARAM_MODE);
400                 if (parse_mode(&info->cfg.mode, optarg) < 0)
401                         param_act(P_BAD_VALUE, "hashlimit",
402                                   "--hashlimit-mode", optarg);
403                 *flags |= PARAM_MODE;
404                 return true;
405
406         case '"': /* --hashlimit-name */
407                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-name",
408                           *flags & PARAM_NAME);
409                 if (strlen(optarg) == 0)
410                         exit_error(PARAMETER_PROBLEM, "Zero-length name?");
411                 strncpy(info->name, optarg, sizeof(info->name));
412                 info->name[sizeof(info->name)-1] = '\0';
413                 *flags |= PARAM_NAME;
414                 return true;
415
416         case '<': /* --hashlimit-srcmask */
417                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-srcmask",
418                           *flags & PARAM_SRCMASK);
419                 if (!strtonum(optarg, NULL, &num, 0, maxmask))
420                         param_act(P_BAD_VALUE, "hashlimit",
421                                   "--hashlimit-srcmask", optarg);
422                 info->cfg.srcmask = num;
423                 *flags |= PARAM_SRCMASK;
424                 return true;
425
426         case '>': /* --hashlimit-dstmask */
427                 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-dstmask",
428                           *flags & PARAM_DSTMASK);
429                 if (!strtonum(optarg, NULL, &num, 0, maxmask))
430                         param_act(P_BAD_VALUE, "hashlimit",
431                                   "--hashlimit-dstmask", optarg);
432                 info->cfg.dstmask = num;
433                 *flags |= PARAM_DSTMASK;
434                 return true;
435         }
436         return false;
437 }
438
439 static int
440 hashlimit_mt4_parse(int c, char **argv, int invert, unsigned int *flags,
441                     const void *entry, struct xt_entry_match **match)
442 {
443         return hashlimit_mt_parse((void *)(*match)->data,
444                flags, c, invert, 32);
445 }
446
447 static int
448 hashlimit_mt6_parse(int c, char **argv, int invert, unsigned int *flags,
449                     const void *entry, struct xt_entry_match **match)
450 {
451         return hashlimit_mt_parse((void *)(*match)->data,
452                flags, c, invert, 128);
453 }
454
455 /* Final check; nothing. */
456 static void hashlimit_check(unsigned int flags)
457 {
458         if (!(flags & PARAM_LIMIT))
459                 exit_error(PARAMETER_PROBLEM,
460                                 "You have to specify --hashlimit");
461         if (!(flags & PARAM_MODE))
462                 exit_error(PARAMETER_PROBLEM,
463                                 "You have to specify --hashlimit-mode");
464         if (!(flags & PARAM_NAME))
465                 exit_error(PARAMETER_PROBLEM,
466                                 "You have to specify --hashlimit-name");
467 }
468
469 static void hashlimit_mt_check(unsigned int flags)
470 {
471         if (!(flags & PARAM_LIMIT))
472                 exit_error(PARAMETER_PROBLEM, "You have to specify "
473                            "--hashlimit-upto or --hashlimit-above");
474         if (!(flags & PARAM_NAME))
475                 exit_error(PARAMETER_PROBLEM,
476                            "You have to specify --hashlimit-name");
477 }
478
479 static const struct rates
480 {
481         const char *name;
482         u_int32_t mult;
483 } rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
484               { "hour", XT_HASHLIMIT_SCALE*60*60 },
485               { "min", XT_HASHLIMIT_SCALE*60 },
486               { "sec", XT_HASHLIMIT_SCALE } };
487
488 static void print_rate(u_int32_t period)
489 {
490         unsigned int i;
491
492         for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
493                 if (period > rates[i].mult
494             || rates[i].mult/period < rates[i].mult%period)
495                         break;
496         }
497
498         printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
499 }
500
501 static void print_mode(unsigned int mode, char separator)
502 {
503         bool prevmode = false;
504
505         if (mode & XT_HASHLIMIT_HASH_SIP) {
506                 fputs("srcip", stdout);
507                 prevmode = 1;
508         }
509         if (mode & XT_HASHLIMIT_HASH_SPT) {
510                 if (prevmode)
511                         putchar(separator);
512                 fputs("srcport", stdout);
513                 prevmode = 1;
514         }
515         if (mode & XT_HASHLIMIT_HASH_DIP) {
516                 if (prevmode)
517                         putchar(separator);
518                 fputs("dstip", stdout);
519                 prevmode = 1;
520         }
521         if (mode & XT_HASHLIMIT_HASH_DPT) {
522                 if (prevmode)
523                         putchar(separator);
524                 fputs("dstport", stdout);
525         }
526         putchar(' ');
527 }
528
529 /* Prints out the matchinfo. */
530 static void hashlimit_print(const void *ip,
531                             const struct xt_entry_match *match, int numeric)
532 {
533         struct xt_hashlimit_info *r = 
534                 (struct xt_hashlimit_info *)match->data;
535         fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
536         printf("burst %u ", r->cfg.burst);
537         fputs("mode ", stdout);
538         print_mode(r->cfg.mode, '-');
539         if (r->cfg.size)
540                 printf("htable-size %u ", r->cfg.size);
541         if (r->cfg.max)
542                 printf("htable-max %u ", r->cfg.max);
543         if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
544                 printf("htable-gcinterval %u ", r->cfg.gc_interval);
545         if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
546                 printf("htable-expire %u ", r->cfg.expire);
547 }
548
549 static void
550 hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
551 {
552         if (info->cfg.mode & XT_HASHLIMIT_INVERT)
553                 fputs("limit: above ", stdout);
554         else
555                 fputs("limit: up to ", stdout);
556         print_rate(info->cfg.avg);
557         printf("burst %u ", info->cfg.burst);
558         if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
559             XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
560                 fputs("mode ", stdout);
561                 print_mode(info->cfg.mode, '-');
562         }
563         if (info->cfg.size != 0)
564                 printf("htable-size %u ", info->cfg.size);
565         if (info->cfg.max != 0)
566                 printf("htable-max %u ", info->cfg.max);
567         if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
568                 printf("htable-gcinterval %u ", info->cfg.gc_interval);
569         if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
570                 printf("htable-expire %u ", info->cfg.expire);
571
572         if (info->cfg.srcmask != dmask)
573                 printf("srcmask %u ", info->cfg.srcmask);
574         if (info->cfg.dstmask != dmask)
575                 printf("dstmask %u ", info->cfg.dstmask);
576 }
577
578 static void
579 hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
580                    int numeric)
581 {
582         const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
583
584         hashlimit_mt_print(info, 32);
585 }
586
587 static void
588 hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
589                    int numeric)
590 {
591         const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
592
593         hashlimit_mt_print(info, 128);
594 }
595
596 /* FIXME: Make minimalist: only print rate if not default --RR */
597 static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
598 {
599         struct xt_hashlimit_info *r = 
600                 (struct xt_hashlimit_info *)match->data;
601
602         fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
603         if (r->cfg.burst != XT_HASHLIMIT_BURST)
604                 printf("--hashlimit-burst %u ", r->cfg.burst);
605
606         fputs("--hashlimit-mode ", stdout);
607         print_mode(r->cfg.mode, ',');
608         
609         printf("--hashlimit-name %s ", r->name);
610
611         if (r->cfg.size)
612                 printf("--hashlimit-htable-size %u ", r->cfg.size);
613         if (r->cfg.max)
614                 printf("--hashlimit-htable-max %u ", r->cfg.max);
615         if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
616                 printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
617         if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
618                 printf("--hashlimit-htable-expire %u ", r->cfg.expire);
619 }
620
621 static void
622 hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
623 {
624         if (info->cfg.mode & XT_HASHLIMIT_INVERT)
625                 fputs("--hashlimit-above ", stdout);
626         else
627                 fputs("--hashlimit-upto ", stdout);
628         print_rate(info->cfg.avg);
629         if (info->cfg.burst != XT_HASHLIMIT_BURST)
630                 printf("--hashlimit-burst %u ", info->cfg.burst);
631
632         if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
633             XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
634                 fputs("--hashlimit-mode ", stdout);
635                 print_mode(info->cfg.mode, ',');
636         }
637
638         printf("--hashlimit-name %s ", info->name);
639
640         if (info->cfg.size != 0)
641                 printf("--hashlimit-htable-size %u ", info->cfg.size);
642         if (info->cfg.max != 0)
643                 printf("--hashlimit-htable-max %u ", info->cfg.max);
644         if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
645                 printf("--hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
646         if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
647                 printf("--hashlimit-htable-expire %u ", info->cfg.expire);
648
649         if (info->cfg.srcmask != dmask)
650                 printf("--hashlimit-srcmask %u ", info->cfg.srcmask);
651         if (info->cfg.dstmask != dmask)
652                 printf("--hashlimit-dstmask %u ", info->cfg.dstmask);
653 }
654
655 static void
656 hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
657 {
658         const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
659
660         hashlimit_mt_save(info, 32);
661 }
662
663 static void
664 hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
665 {
666         const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
667
668         hashlimit_mt_save(info, 128);
669 }
670
671 static struct xtables_match hashlimit_match = {
672         .family         = AF_INET,
673         .name           = "hashlimit",
674         .version        = XTABLES_VERSION,
675         .revision       = 0,
676         .size           = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
677         .userspacesize  = offsetof(struct xt_hashlimit_info, hinfo),
678         .help           = hashlimit_help,
679         .init           = hashlimit_init,
680         .parse          = hashlimit_parse,
681         .final_check    = hashlimit_check,
682         .print          = hashlimit_print,
683         .save           = hashlimit_save,
684         .extra_opts     = hashlimit_opts,
685 };
686
687 static struct xtables_match hashlimit_match6 = {
688         .family         = AF_INET6,
689         .name           = "hashlimit",
690         .version        = XTABLES_VERSION,
691         .revision       = 0,
692         .size           = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
693         .userspacesize  = offsetof(struct xt_hashlimit_info, hinfo),
694         .help           = hashlimit_help,
695         .init           = hashlimit_init,
696         .parse          = hashlimit_parse,
697         .final_check    = hashlimit_check,
698         .print          = hashlimit_print,
699         .save           = hashlimit_save,
700         .extra_opts     = hashlimit_opts,
701 };
702
703 static struct xtables_match hashlimit_mt_reg = {
704         .version        = XTABLES_VERSION,
705         .name           = "hashlimit",
706         .revision       = 1,
707         .family         = AF_INET,
708         .size           = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
709         .userspacesize  = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
710         .help           = hashlimit_mt_help,
711         .init           = hashlimit_mt4_init,
712         .parse          = hashlimit_mt4_parse,
713         .final_check    = hashlimit_mt_check,
714         .print          = hashlimit_mt4_print,
715         .save           = hashlimit_mt4_save,
716         .extra_opts     = hashlimit_mt_opts,
717 };
718
719 static struct xtables_match hashlimit_mt6_reg = {
720         .version        = XTABLES_VERSION,
721         .name           = "hashlimit",
722         .revision       = 1,
723         .family         = AF_INET6,
724         .size           = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
725         .userspacesize  = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
726         .help           = hashlimit_mt_help,
727         .init           = hashlimit_mt6_init,
728         .parse          = hashlimit_mt6_parse,
729         .final_check    = hashlimit_mt_check,
730         .print          = hashlimit_mt6_print,
731         .save           = hashlimit_mt6_save,
732         .extra_opts     = hashlimit_mt_opts,
733 };
734
735 void _init(void)
736 {
737         xtables_register_match(&hashlimit_match);
738         xtables_register_match(&hashlimit_match6);
739         xtables_register_match(&hashlimit_mt_reg);
740         xtables_register_match(&hashlimit_mt6_reg);
741 }