libxt_hashlimit.c revision c5e85736c207f211d82d2878a5781f512327dfce
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 < ARRAY_SIZE(rates); ++i)
489		if (period > rates[i].mult
490            || rates[i].mult/period < rates[i].mult%period)
491			break;
492
493	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
494}
495
496static void print_mode(unsigned int mode, char separator)
497{
498	bool prevmode = false;
499
500	if (mode & XT_HASHLIMIT_HASH_SIP) {
501		fputs("srcip", stdout);
502		prevmode = 1;
503	}
504	if (mode & XT_HASHLIMIT_HASH_SPT) {
505		if (prevmode)
506			putchar(separator);
507		fputs("srcport", stdout);
508		prevmode = 1;
509	}
510	if (mode & XT_HASHLIMIT_HASH_DIP) {
511		if (prevmode)
512			putchar(separator);
513		fputs("dstip", stdout);
514		prevmode = 1;
515	}
516	if (mode & XT_HASHLIMIT_HASH_DPT) {
517		if (prevmode)
518			putchar(separator);
519		fputs("dstport", stdout);
520	}
521	putchar(' ');
522}
523
524static void hashlimit_print(const void *ip,
525                            const struct xt_entry_match *match, int numeric)
526{
527	const struct xt_hashlimit_info *r = (const void *)match->data;
528	fputs("limit: avg ", stdout); print_rate(r->cfg.avg);
529	printf("burst %u ", r->cfg.burst);
530	fputs("mode ", stdout);
531	print_mode(r->cfg.mode, '-');
532	if (r->cfg.size)
533		printf("htable-size %u ", r->cfg.size);
534	if (r->cfg.max)
535		printf("htable-max %u ", r->cfg.max);
536	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
537		printf("htable-gcinterval %u ", r->cfg.gc_interval);
538	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
539		printf("htable-expire %u ", r->cfg.expire);
540}
541
542static void
543hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
544{
545	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
546		fputs("limit: above ", stdout);
547	else
548		fputs("limit: up to ", stdout);
549	print_rate(info->cfg.avg);
550	printf("burst %u ", info->cfg.burst);
551	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
552	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
553		fputs("mode ", stdout);
554		print_mode(info->cfg.mode, '-');
555	}
556	if (info->cfg.size != 0)
557		printf("htable-size %u ", info->cfg.size);
558	if (info->cfg.max != 0)
559		printf("htable-max %u ", info->cfg.max);
560	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
561		printf("htable-gcinterval %u ", info->cfg.gc_interval);
562	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
563		printf("htable-expire %u ", info->cfg.expire);
564
565	if (info->cfg.srcmask != dmask)
566		printf("srcmask %u ", info->cfg.srcmask);
567	if (info->cfg.dstmask != dmask)
568		printf("dstmask %u ", info->cfg.dstmask);
569}
570
571static void
572hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
573                   int numeric)
574{
575	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
576
577	hashlimit_mt_print(info, 32);
578}
579
580static void
581hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
582                   int numeric)
583{
584	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
585
586	hashlimit_mt_print(info, 128);
587}
588
589static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
590{
591	const struct xt_hashlimit_info *r = (const void *)match->data;
592
593	fputs("--hashlimit ", stdout); print_rate(r->cfg.avg);
594	if (r->cfg.burst != XT_HASHLIMIT_BURST)
595		printf("--hashlimit-burst %u ", r->cfg.burst);
596
597	fputs("--hashlimit-mode ", stdout);
598	print_mode(r->cfg.mode, ',');
599
600	printf("--hashlimit-name %s ", r->name);
601
602	if (r->cfg.size)
603		printf("--hashlimit-htable-size %u ", r->cfg.size);
604	if (r->cfg.max)
605		printf("--hashlimit-htable-max %u ", r->cfg.max);
606	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
607		printf("--hashlimit-htable-gcinterval %u ", r->cfg.gc_interval);
608	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
609		printf("--hashlimit-htable-expire %u ", r->cfg.expire);
610}
611
612static void
613hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
614{
615	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
616		fputs("--hashlimit-above ", stdout);
617	else
618		fputs("--hashlimit-upto ", stdout);
619	print_rate(info->cfg.avg);
620	if (info->cfg.burst != XT_HASHLIMIT_BURST)
621		printf("--hashlimit-burst %u ", info->cfg.burst);
622
623	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
624	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
625		fputs("--hashlimit-mode ", stdout);
626		print_mode(info->cfg.mode, ',');
627	}
628
629	printf("--hashlimit-name %s ", info->name);
630
631	if (info->cfg.size != 0)
632		printf("--hashlimit-htable-size %u ", info->cfg.size);
633	if (info->cfg.max != 0)
634		printf("--hashlimit-htable-max %u ", info->cfg.max);
635	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
636		printf("--hashlimit-htable-gcinterval %u ", info->cfg.gc_interval);
637	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
638		printf("--hashlimit-htable-expire %u ", info->cfg.expire);
639
640	if (info->cfg.srcmask != dmask)
641		printf("--hashlimit-srcmask %u ", info->cfg.srcmask);
642	if (info->cfg.dstmask != dmask)
643		printf("--hashlimit-dstmask %u ", info->cfg.dstmask);
644}
645
646static void
647hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
648{
649	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
650
651	hashlimit_mt_save(info, 32);
652}
653
654static void
655hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
656{
657	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
658
659	hashlimit_mt_save(info, 128);
660}
661
662static struct xtables_match hashlimit_match = {
663	.family		= NFPROTO_UNSPEC,
664	.name		= "hashlimit",
665	.version	= XTABLES_VERSION,
666	.revision	= 0,
667	.size		= XT_ALIGN(sizeof(struct xt_hashlimit_info)),
668	.userspacesize	= offsetof(struct xt_hashlimit_info, hinfo),
669	.help		= hashlimit_help,
670	.init		= hashlimit_init,
671	.parse		= hashlimit_parse,
672	.final_check	= hashlimit_check,
673	.print		= hashlimit_print,
674	.save		= hashlimit_save,
675	.extra_opts	= hashlimit_opts,
676};
677
678static struct xtables_match hashlimit_mt_reg = {
679	.version        = XTABLES_VERSION,
680	.name           = "hashlimit",
681	.revision       = 1,
682	.family         = NFPROTO_IPV4,
683	.size           = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
684	.userspacesize  = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
685	.help           = hashlimit_mt_help,
686	.init           = hashlimit_mt4_init,
687	.parse          = hashlimit_mt4_parse,
688	.final_check	= hashlimit_mt_check,
689	.print          = hashlimit_mt4_print,
690	.save           = hashlimit_mt4_save,
691	.extra_opts     = hashlimit_mt_opts,
692};
693
694static struct xtables_match hashlimit_mt6_reg = {
695	.version        = XTABLES_VERSION,
696	.name           = "hashlimit",
697	.revision       = 1,
698	.family         = NFPROTO_IPV6,
699	.size           = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
700	.userspacesize  = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
701	.help           = hashlimit_mt_help,
702	.init           = hashlimit_mt6_init,
703	.parse          = hashlimit_mt6_parse,
704	.final_check	= hashlimit_mt_check,
705	.print          = hashlimit_mt6_print,
706	.save           = hashlimit_mt6_save,
707	.extra_opts     = hashlimit_mt_opts,
708};
709
710void _init(void)
711{
712	xtables_register_match(&hashlimit_match);
713	xtables_register_match(&hashlimit_mt_reg);
714	xtables_register_match(&hashlimit_mt6_reg);
715}
716