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