libxt_limit.c revision 7ac405297ec38449b30e3b05fd6bf2082fd3d803
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	default:
112		return 0;
113	}
114
115	if (invert)
116		xtables_error(PARAMETER_PROBLEM,
117			   "limit does not support invert");
118
119	return 1;
120}
121
122static const struct rates
123{
124	const char *name;
125	uint32_t mult;
126} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
127	      { "hour", XT_LIMIT_SCALE*60*60 },
128	      { "min", XT_LIMIT_SCALE*60 },
129	      { "sec", XT_LIMIT_SCALE } };
130
131static void print_rate(uint32_t period)
132{
133	unsigned int i;
134
135	for (i = 1; i < ARRAY_SIZE(rates); ++i)
136		if (period > rates[i].mult
137            || rates[i].mult/period < rates[i].mult%period)
138			break;
139
140	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
141}
142
143static void
144limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
145{
146	const struct xt_rateinfo *r = (const void *)match->data;
147	printf("limit: avg "); print_rate(r->avg);
148	printf("burst %u ", r->burst);
149}
150
151static void limit_save(const void *ip, const struct xt_entry_match *match)
152{
153	const struct xt_rateinfo *r = (const void *)match->data;
154
155	printf("--limit "); print_rate(r->avg);
156	if (r->burst != XT_LIMIT_BURST)
157		printf("--limit-burst %u ", r->burst);
158}
159
160static struct xtables_match limit_match = {
161	.family		= NFPROTO_UNSPEC,
162	.name		= "limit",
163	.version	= XTABLES_VERSION,
164	.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
165	.userspacesize	= offsetof(struct xt_rateinfo, prev),
166	.help		= limit_help,
167	.init		= limit_init,
168	.parse		= limit_parse,
169	.print		= limit_print,
170	.save		= limit_save,
171	.extra_opts	= limit_opts,
172};
173
174void _init(void)
175{
176	xtables_register_match(&limit_match);
177}
178