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