libxt_hashlimit.c revision abdef13f36b63758f8775eb86febd96bf062df6f
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 <xtables.h>
19#include <linux/netfilter/x_tables.h>
20#include <linux/netfilter/xt_hashlimit.h>
21
22#define XT_HASHLIMIT_BURST	5
23#define XT_HASHLIMIT_BURST_MAX	10000
24
25#define XT_HASHLIMIT_BYTE_EXPIRE	15
26#define XT_HASHLIMIT_BYTE_EXPIRE_BURST	60
27
28/* miliseconds */
29#define XT_HASHLIMIT_GCINTERVAL	1000
30
31struct hashlimit_mt_udata {
32	uint32_t mult;
33};
34
35static void hashlimit_help(void)
36{
37	printf(
38"hashlimit match options:\n"
39"--hashlimit <avg>		max average match rate\n"
40"                                [Packets per second unless followed by \n"
41"                                /sec /minute /hour /day postfixes]\n"
42"--hashlimit-mode <mode>		mode is a comma-separated list of\n"
43"					dstip,srcip,dstport,srcport\n"
44"--hashlimit-name <name>		name for /proc/net/ipt_hashlimit/\n"
45"[--hashlimit-burst <num>]	number to match in a burst, default %u\n"
46"[--hashlimit-htable-size <num>]	number of hashtable buckets\n"
47"[--hashlimit-htable-max <num>]	number of hashtable entries\n"
48"[--hashlimit-htable-gcinterval]	interval between garbage collection runs\n"
49"[--hashlimit-htable-expire]	after which time are idle entries expired?\n",
50XT_HASHLIMIT_BURST);
51}
52
53enum {
54	O_UPTO = 0,
55	O_ABOVE,
56	O_LIMIT,
57	O_MODE,
58	O_SRCMASK,
59	O_DSTMASK,
60	O_NAME,
61	O_BURST,
62	O_HTABLE_SIZE,
63	O_HTABLE_MAX,
64	O_HTABLE_GCINT,
65	O_HTABLE_EXPIRE,
66	F_BURST         = 1 << O_BURST,
67	F_UPTO          = 1 << O_UPTO,
68	F_ABOVE         = 1 << O_ABOVE,
69	F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE,
70};
71
72static void hashlimit_mt_help(void)
73{
74	printf(
75"hashlimit match options:\n"
76"  --hashlimit-upto <avg>           max average match rate\n"
77"                                   [Packets per second unless followed by \n"
78"                                   /sec /minute /hour /day postfixes]\n"
79"  --hashlimit-above <avg>          min average match rate\n"
80"  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
81"                                   dstip,srcip,dstport,srcport (or none)\n"
82"  --hashlimit-srcmask <length>     source address grouping prefix length\n"
83"  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
84"  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
85"  --hashlimit-burst <num>	    number to match in a burst, default %u\n"
86"  --hashlimit-htable-size <num>    number of hashtable buckets\n"
87"  --hashlimit-htable-max <num>     number of hashtable entries\n"
88"  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
89"  --hashlimit-htable-expire        after which time are idle entries expired?\n"
90"\n", XT_HASHLIMIT_BURST);
91}
92
93#define s struct xt_hashlimit_info
94static const struct xt_option_entry hashlimit_opts[] = {
95	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
96	 .type = XTTYPE_STRING},
97	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
98	 .min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT,
99	 XTOPT_POINTER(s, cfg.burst)},
100	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
101	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
102	 XTOPT_POINTER(s, cfg.size)},
103	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
104	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
105	 XTOPT_POINTER(s, cfg.max)},
106	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
107	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
108	 XTOPT_POINTER(s, cfg.gc_interval)},
109	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
110	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
111	 XTOPT_POINTER(s, cfg.expire)},
112	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING,
113	 .flags = XTOPT_MAND},
114	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
115	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
116	XTOPT_TABLEEND,
117};
118#undef s
119
120#define s struct xt_hashlimit_mtinfo1
121static const struct xt_option_entry hashlimit_mt_opts[] = {
122	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
123	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
124	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
125	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
126	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
127	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
128	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
129	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
130	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
131	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
132	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
133	 XTOPT_POINTER(s, cfg.size)},
134	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
135	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
136	 XTOPT_POINTER(s, cfg.max)},
137	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
138	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
139	 XTOPT_POINTER(s, cfg.gc_interval)},
140	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
141	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
142	 XTOPT_POINTER(s, cfg.expire)},
143	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
144	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
145	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
146	XTOPT_TABLEEND,
147};
148#undef s
149
150static uint32_t cost_to_bytes(uint32_t cost)
151{
152	uint32_t r;
153
154	r = cost ? UINT32_MAX / cost : UINT32_MAX;
155	r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
156	return r;
157}
158
159static uint64_t bytes_to_cost(uint32_t bytes)
160{
161	uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
162	return UINT32_MAX / (r+1);
163}
164
165static uint32_t get_factor(int chr)
166{
167	switch (chr) {
168	case 'm': return 1024 * 1024;
169	case 'k': return 1024;
170	}
171	return 1;
172}
173
174static void burst_error(void)
175{
176	xtables_error(PARAMETER_PROBLEM, "bad value for option "
177			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
178}
179
180static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
181{
182	uintmax_t v;
183	char *end;
184
185	if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
186	    (*end == 0 && v > XT_HASHLIMIT_BURST_MAX))
187		burst_error();
188
189	v *= get_factor(*end);
190	if (v > UINT32_MAX)
191		xtables_error(PARAMETER_PROBLEM, "bad value for option "
192			"\"--hashlimit-burst\", value \"%s\" too large "
193				"(max %umb).", burst, UINT32_MAX/1024/1024);
194	return v;
195}
196
197static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
198{
199	unsigned int factor = 1;
200	uint64_t tmp;
201	int r;
202	const char *mode = strstr(rate, "b/s");
203	if (!mode || mode == rate)
204		return false;
205
206	mode--;
207	r = atoi(rate);
208	if (r == 0)
209		return false;
210
211	factor = get_factor(*mode);
212	tmp = (uint64_t) r * factor;
213	if (tmp > UINT32_MAX)
214		xtables_error(PARAMETER_PROBLEM,
215			"Rate value too large \"%llu\" (max %u)\n",
216					(unsigned long long)tmp, UINT32_MAX);
217
218	*val = bytes_to_cost(tmp);
219	if (*val == 0)
220		xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
221
222	ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
223	return true;
224}
225
226static
227int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
228{
229	const char *delim;
230	uint32_t r;
231
232	ud->mult = 1;  /* Seconds by default. */
233	delim = strchr(rate, '/');
234	if (delim) {
235		if (strlen(delim+1) == 0)
236			return 0;
237
238		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
239			ud->mult = 1;
240		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
241			ud->mult = 60;
242		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
243			ud->mult = 60*60;
244		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
245			ud->mult = 24*60*60;
246		else
247			return 0;
248	}
249	r = atoi(rate);
250	if (!r)
251		return 0;
252
253	/* This would get mapped to infinite (1/day is minimum they
254           can specify, so we're ok at that end). */
255	if (r / ud->mult > XT_HASHLIMIT_SCALE)
256		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
257
258	*val = XT_HASHLIMIT_SCALE * ud->mult / r;
259	return 1;
260}
261
262static void hashlimit_init(struct xt_entry_match *m)
263{
264	struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
265
266	r->cfg.burst = XT_HASHLIMIT_BURST;
267	r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
268
269}
270
271static void hashlimit_mt4_init(struct xt_entry_match *match)
272{
273	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
274
275	info->cfg.mode        = 0;
276	info->cfg.burst       = XT_HASHLIMIT_BURST;
277	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
278	info->cfg.srcmask     = 32;
279	info->cfg.dstmask     = 32;
280}
281
282static void hashlimit_mt6_init(struct xt_entry_match *match)
283{
284	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
285
286	info->cfg.mode        = 0;
287	info->cfg.burst       = XT_HASHLIMIT_BURST;
288	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
289	info->cfg.srcmask     = 128;
290	info->cfg.dstmask     = 128;
291}
292
293/* Parse a 'mode' parameter into the required bitmask */
294static int parse_mode(uint32_t *mode, const char *option_arg)
295{
296	char *tok;
297	char *arg = strdup(option_arg);
298
299	if (!arg)
300		return -1;
301
302	for (tok = strtok(arg, ",|");
303	     tok;
304	     tok = strtok(NULL, ",|")) {
305		if (!strcmp(tok, "dstip"))
306			*mode |= XT_HASHLIMIT_HASH_DIP;
307		else if (!strcmp(tok, "srcip"))
308			*mode |= XT_HASHLIMIT_HASH_SIP;
309		else if (!strcmp(tok, "srcport"))
310			*mode |= XT_HASHLIMIT_HASH_SPT;
311		else if (!strcmp(tok, "dstport"))
312			*mode |= XT_HASHLIMIT_HASH_DPT;
313		else {
314			free(arg);
315			return -1;
316		}
317	}
318	free(arg);
319	return 0;
320}
321
322static void hashlimit_parse(struct xt_option_call *cb)
323{
324	struct xt_hashlimit_info *info = cb->data;
325
326	xtables_option_parse(cb);
327	switch (cb->entry->id) {
328	case O_UPTO:
329		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
330			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
331			          "--hashlimit-upto", cb->arg);
332		break;
333	case O_MODE:
334		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
335			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
336			          "--hashlimit-mode", cb->arg);
337		break;
338	}
339}
340
341static void hashlimit_mt_parse(struct xt_option_call *cb)
342{
343	struct xt_hashlimit_mtinfo1 *info = cb->data;
344
345	xtables_option_parse(cb);
346	switch (cb->entry->id) {
347	case O_BURST:
348		info->cfg.burst = parse_burst(cb->arg, info);
349		break;
350	case O_UPTO:
351		if (cb->invert)
352			info->cfg.mode |= XT_HASHLIMIT_INVERT;
353		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
354			info->cfg.mode |= XT_HASHLIMIT_BYTES;
355		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
356			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
357			          "--hashlimit-upto", cb->arg);
358		break;
359	case O_ABOVE:
360		if (!cb->invert)
361			info->cfg.mode |= XT_HASHLIMIT_INVERT;
362		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
363			info->cfg.mode |= XT_HASHLIMIT_BYTES;
364		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
365			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
366			          "--hashlimit-above", cb->arg);
367		break;
368	case O_MODE:
369		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
370			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
371			          "--hashlimit-mode", cb->arg);
372		break;
373	case O_SRCMASK:
374		info->cfg.srcmask = cb->val.hlen;
375		break;
376	case O_DSTMASK:
377		info->cfg.dstmask = cb->val.hlen;
378		break;
379	}
380}
381
382static void hashlimit_check(struct xt_fcheck_call *cb)
383{
384	const struct hashlimit_mt_udata *udata = cb->udata;
385	struct xt_hashlimit_info *info = cb->data;
386
387	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
388		xtables_error(PARAMETER_PROBLEM,
389				"You have to specify --hashlimit");
390	if (!(cb->xflags & F_HTABLE_EXPIRE))
391		info->cfg.expire = udata->mult * 1000; /* from s to msec */
392}
393
394static void hashlimit_mt_check(struct xt_fcheck_call *cb)
395{
396	const struct hashlimit_mt_udata *udata = cb->udata;
397	struct xt_hashlimit_mtinfo1 *info = cb->data;
398
399	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
400		xtables_error(PARAMETER_PROBLEM,
401				"You have to specify --hashlimit");
402	if (!(cb->xflags & F_HTABLE_EXPIRE))
403		info->cfg.expire = udata->mult * 1000; /* from s to msec */
404
405	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
406		uint32_t burst = 0;
407		if (cb->xflags & F_BURST) {
408			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
409				xtables_error(PARAMETER_PROBLEM,
410					"burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
411
412			burst = info->cfg.burst;
413			burst /= cost_to_bytes(info->cfg.avg);
414			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
415				burst++;
416			if (!(cb->xflags & F_HTABLE_EXPIRE))
417				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
418		}
419		info->cfg.burst = burst;
420	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
421		burst_error();
422}
423
424static const struct rates
425{
426	const char *name;
427	uint32_t mult;
428} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
429	      { "hour", XT_HASHLIMIT_SCALE*60*60 },
430	      { "min", XT_HASHLIMIT_SCALE*60 },
431	      { "sec", XT_HASHLIMIT_SCALE } };
432
433static uint32_t print_rate(uint32_t period)
434{
435	unsigned int i;
436
437	for (i = 1; i < ARRAY_SIZE(rates); ++i)
438		if (period > rates[i].mult
439            || rates[i].mult/period < rates[i].mult%period)
440			break;
441
442	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
443	/* return in msec */
444	return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
445}
446
447static const struct {
448	const char *name;
449	uint32_t thresh;
450} units[] = {
451	{ "m", 1024 * 1024 },
452	{ "k", 1024 },
453	{ "", 1 },
454};
455
456static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
457{
458	unsigned int i;
459	unsigned long long r;
460
461	r = cost_to_bytes(avg);
462
463	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
464		if (r >= units[i].thresh &&
465		    bytes_to_cost(r & ~(units[i].thresh - 1)) == avg)
466			break;
467	printf(" %llu%sb/s", r/units[i].thresh, units[i].name);
468
469	if (burst == 0)
470		return XT_HASHLIMIT_BYTE_EXPIRE * 1000;
471
472	r *= burst;
473	printf(" %s", prefix);
474	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
475		if (r >= units[i].thresh)
476			break;
477
478	printf("burst %llu%sb", r / units[i].thresh, units[i].name);
479	return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
480}
481
482static void print_mode(unsigned int mode, char separator)
483{
484	bool prevmode = false;
485
486	putchar(' ');
487	if (mode & XT_HASHLIMIT_HASH_SIP) {
488		fputs("srcip", stdout);
489		prevmode = 1;
490	}
491	if (mode & XT_HASHLIMIT_HASH_SPT) {
492		if (prevmode)
493			putchar(separator);
494		fputs("srcport", stdout);
495		prevmode = 1;
496	}
497	if (mode & XT_HASHLIMIT_HASH_DIP) {
498		if (prevmode)
499			putchar(separator);
500		fputs("dstip", stdout);
501		prevmode = 1;
502	}
503	if (mode & XT_HASHLIMIT_HASH_DPT) {
504		if (prevmode)
505			putchar(separator);
506		fputs("dstport", stdout);
507	}
508}
509
510static void hashlimit_print(const void *ip,
511                            const struct xt_entry_match *match, int numeric)
512{
513	const struct xt_hashlimit_info *r = (const void *)match->data;
514	uint32_t quantum;
515
516	fputs(" limit: avg", stdout);
517	quantum = print_rate(r->cfg.avg);
518	printf(" burst %u", r->cfg.burst);
519	fputs(" mode", stdout);
520	print_mode(r->cfg.mode, '-');
521	if (r->cfg.size)
522		printf(" htable-size %u", r->cfg.size);
523	if (r->cfg.max)
524		printf(" htable-max %u", r->cfg.max);
525	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
526		printf(" htable-gcinterval %u", r->cfg.gc_interval);
527	if (r->cfg.expire != quantum)
528		printf(" htable-expire %u", r->cfg.expire);
529}
530
531static void
532hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
533{
534	uint32_t quantum;
535
536	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
537		fputs(" limit: above", stdout);
538	else
539		fputs(" limit: up to", stdout);
540
541	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
542		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
543	} else {
544		quantum = print_rate(info->cfg.avg);
545		printf(" burst %u", info->cfg.burst);
546	}
547	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
548	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
549		fputs(" mode", stdout);
550		print_mode(info->cfg.mode, '-');
551	}
552	if (info->cfg.size != 0)
553		printf(" htable-size %u", info->cfg.size);
554	if (info->cfg.max != 0)
555		printf(" htable-max %u", info->cfg.max);
556	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
557		printf(" htable-gcinterval %u", info->cfg.gc_interval);
558	if (info->cfg.expire != quantum)
559		printf(" htable-expire %u", info->cfg.expire);
560
561	if (info->cfg.srcmask != dmask)
562		printf(" srcmask %u", info->cfg.srcmask);
563	if (info->cfg.dstmask != dmask)
564		printf(" dstmask %u", info->cfg.dstmask);
565}
566
567static void
568hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
569                   int numeric)
570{
571	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
572
573	hashlimit_mt_print(info, 32);
574}
575
576static void
577hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
578                   int numeric)
579{
580	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
581
582	hashlimit_mt_print(info, 128);
583}
584
585static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
586{
587	const struct xt_hashlimit_info *r = (const void *)match->data;
588	uint32_t quantum;
589
590	fputs(" --hashlimit", stdout);
591	quantum = print_rate(r->cfg.avg);
592	printf(" --hashlimit-burst %u", r->cfg.burst);
593
594	fputs(" --hashlimit-mode", stdout);
595	print_mode(r->cfg.mode, ',');
596
597	printf(" --hashlimit-name %s", r->name);
598
599	if (r->cfg.size)
600		printf(" --hashlimit-htable-size %u", r->cfg.size);
601	if (r->cfg.max)
602		printf(" --hashlimit-htable-max %u", r->cfg.max);
603	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
604		printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
605	if (r->cfg.expire != quantum)
606		printf(" --hashlimit-htable-expire %u", r->cfg.expire);
607}
608
609static void
610hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
611{
612	uint32_t quantum;
613
614	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
615		fputs(" --hashlimit-above", stdout);
616	else
617		fputs(" --hashlimit-upto", stdout);
618
619	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
620		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
621	} else {
622		quantum = print_rate(info->cfg.avg);
623		printf(" --hashlimit-burst %u", info->cfg.burst);
624	}
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 != quantum)
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_mt_reg[] = {
666	{
667		.family        = NFPROTO_UNSPEC,
668		.name          = "hashlimit",
669		.version       = XTABLES_VERSION,
670		.revision      = 0,
671		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
672		.userspacesize = offsetof(struct xt_hashlimit_info, hinfo),
673		.help          = hashlimit_help,
674		.init          = hashlimit_init,
675		.x6_parse      = hashlimit_parse,
676		.x6_fcheck     = hashlimit_check,
677		.print         = hashlimit_print,
678		.save          = hashlimit_save,
679		.x6_options    = hashlimit_opts,
680		.udata_size    = sizeof(struct hashlimit_mt_udata),
681	},
682	{
683		.version       = XTABLES_VERSION,
684		.name          = "hashlimit",
685		.revision      = 1,
686		.family        = NFPROTO_IPV4,
687		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
688		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
689		.help          = hashlimit_mt_help,
690		.init          = hashlimit_mt4_init,
691		.x6_parse      = hashlimit_mt_parse,
692		.x6_fcheck     = hashlimit_mt_check,
693		.print         = hashlimit_mt4_print,
694		.save          = hashlimit_mt4_save,
695		.x6_options    = hashlimit_mt_opts,
696		.udata_size    = sizeof(struct hashlimit_mt_udata),
697	},
698	{
699		.version       = XTABLES_VERSION,
700		.name          = "hashlimit",
701		.revision      = 1,
702		.family        = NFPROTO_IPV6,
703		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
704		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
705		.help          = hashlimit_mt_help,
706		.init          = hashlimit_mt6_init,
707		.x6_parse      = hashlimit_mt_parse,
708		.x6_fcheck     = hashlimit_mt_check,
709		.print         = hashlimit_mt6_print,
710		.save          = hashlimit_mt6_save,
711		.x6_options    = hashlimit_mt_opts,
712		.udata_size    = sizeof(struct hashlimit_mt_udata),
713	},
714};
715
716void _init(void)
717{
718	xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
719}
720