libxt_limit.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add limit support.
2 *
3 * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
4 * Hervé Eychenne    <rv@wallfire.org>
5 */
6#include <stdbool.h>
7#include <stdio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <getopt.h>
11#include <xtables.h>
12#include <stddef.h>
13#include <linux/netfilter/x_tables.h>
14/* For 64bit kernel / 32bit userspace */
15#include <linux/netfilter/xt_limit.h>
16
17#define XT_LIMIT_AVG	"3/hour"
18#define XT_LIMIT_BURST	5
19
20static void limit_help(void)
21{
22	printf(
23"limit match options:\n"
24"--limit avg			max average match rate: default "XT_LIMIT_AVG"\n"
25"                                [Packets per second unless followed by \n"
26"                                /sec /minute /hour /day postfixes]\n"
27"--limit-burst number		number to match in a burst, default %u\n",
28XT_LIMIT_BURST);
29}
30
31static const struct option limit_opts[] = {
32	{.name = "limit",       .has_arg = true, .val = '%'},
33	{.name = "limit-burst", .has_arg = true, .val = '$'},
34	XT_GETOPT_TABLEEND,
35};
36
37static
38int parse_rate(const char *rate, uint32_t *val)
39{
40	const char *delim;
41	uint32_t r;
42	uint32_t mult = 1;  /* Seconds by default. */
43
44	delim = strchr(rate, '/');
45	if (delim) {
46		if (strlen(delim+1) == 0)
47			return 0;
48
49		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
50			mult = 1;
51		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
52			mult = 60;
53		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
54			mult = 60*60;
55		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
56			mult = 24*60*60;
57		else
58			return 0;
59	}
60	r = atoi(rate);
61	if (!r)
62		return 0;
63
64	/* This would get mapped to infinite (1/day is minimum they
65           can specify, so we're ok at that end). */
66	if (r / mult > XT_LIMIT_SCALE)
67		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
68
69	*val = XT_LIMIT_SCALE * mult / r;
70	return 1;
71}
72
73static void limit_init(struct xt_entry_match *m)
74{
75	struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
76
77	parse_rate(XT_LIMIT_AVG, &r->avg);
78	r->burst = XT_LIMIT_BURST;
79
80}
81
82/* FIXME: handle overflow:
83	if (r->avg*r->burst/r->burst != r->avg)
84		xtables_error(PARAMETER_PROBLEM,
85			   "Sorry: burst too large for that avg rate.\n");
86*/
87
88static int
89limit_parse(int c, char **argv, int invert, unsigned int *flags,
90            const void *entry, struct xt_entry_match **match)
91{
92	struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
93	unsigned int num;
94
95	switch(c) {
96	case '%':
97		if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break;
98		if (!parse_rate(optarg, &r->avg))
99			xtables_error(PARAMETER_PROBLEM,
100				   "bad rate `%s'", optarg);
101		break;
102
103	case '$':
104		if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break;
105		if (!xtables_strtoui(optarg, NULL, &num, 0, 10000))
106			xtables_error(PARAMETER_PROBLEM,
107				   "bad --limit-burst `%s'", optarg);
108		r->burst = num;
109		break;
110	}
111
112	if (invert)
113		xtables_error(PARAMETER_PROBLEM,
114			   "limit does not support invert");
115
116	return 1;
117}
118
119static const struct rates
120{
121	const char *name;
122	uint32_t mult;
123} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
124	      { "hour", XT_LIMIT_SCALE*60*60 },
125	      { "min", XT_LIMIT_SCALE*60 },
126	      { "sec", XT_LIMIT_SCALE } };
127
128static void print_rate(uint32_t period)
129{
130	unsigned int i;
131
132	for (i = 1; i < ARRAY_SIZE(rates); ++i)
133		if (period > rates[i].mult
134            || rates[i].mult/period < rates[i].mult%period)
135			break;
136
137	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
138}
139
140static void
141limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
142{
143	const struct xt_rateinfo *r = (const void *)match->data;
144	printf(" limit: avg"); print_rate(r->avg);
145	printf(" burst %u", r->burst);
146}
147
148static void limit_save(const void *ip, const struct xt_entry_match *match)
149{
150	const struct xt_rateinfo *r = (const void *)match->data;
151
152	printf(" --limit"); print_rate(r->avg);
153	if (r->burst != XT_LIMIT_BURST)
154		printf(" --limit-burst %u", r->burst);
155}
156
157static struct xtables_match limit_match = {
158	.family		= NFPROTO_UNSPEC,
159	.name		= "limit",
160	.version	= XTABLES_VERSION,
161	.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
162	.userspacesize	= offsetof(struct xt_rateinfo, prev),
163	.help		= limit_help,
164	.init		= limit_init,
165	.parse		= limit_parse,
166	.print		= limit_print,
167	.save		= limit_save,
168	.extra_opts	= limit_opts,
169};
170
171void _init(void)
172{
173	xtables_register_match(&limit_match);
174}
175