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