libxt_limit.c revision 2c69b55e55f2efc5a334b87ccdceaa9de0ecb658
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Shared library add-on to iptables to add limit support.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Hervé Eychenne    <rv@wallfire.org>
568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) */
668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h>
968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <stdlib.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <getopt.h>
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <xtables.h>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stddef.h>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <linux/netfilter/x_tables.h>
1468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)/* For 64bit kernel / 32bit userspace */
1558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <linux/netfilter/xt_limit.h>
1658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define XT_LIMIT_AVG	"3/hour"
1868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#define XT_LIMIT_BURST	5
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)static void limit_help(void)
2158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles){
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)	printf(
2368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)"limit match options:\n"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"--limit avg			max average match rate: default "XT_LIMIT_AVG"\n"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"                                [Packets per second unless followed by \n"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"                                /sec /minute /hour /day postfixes]\n"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"--limit-burst number		number to match in a burst, default %u\n",
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)XT_LIMIT_BURST);
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)static const struct option limit_opts[] = {
32	{ "limit", 1, NULL, '%' },
33	{ "limit-burst", 1, NULL, '$' },
34	{ .name = NULL }
35};
36
37static
38int parse_rate(const char *rate, u_int32_t *val)
39{
40	const char *delim;
41	u_int32_t r;
42	u_int32_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(argv[optind-1], &invert, &optind, 0)) 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(argv[optind-1], &invert, &optind, 0)) 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	u_int32_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(u_int32_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		= AF_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