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