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