libxt_limit.c revision 69f564e3890976461de0016cd81171ff8bfa8353
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
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	{ "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 < sizeof(rates)/sizeof(struct rates); i++) {
136		if (period > rates[i].mult
137            || rates[i].mult/period < rates[i].mult%period)
138			break;
139	}
140
141	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
142}
143
144static void
145limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
146{
147	const struct xt_rateinfo *r = (const void *)match->data;
148	printf("limit: avg "); print_rate(r->avg);
149	printf("burst %u ", r->burst);
150}
151
152static void limit_save(const void *ip, const struct xt_entry_match *match)
153{
154	const struct xt_rateinfo *r = (const void *)match->data;
155
156	printf("--limit "); print_rate(r->avg);
157	if (r->burst != XT_LIMIT_BURST)
158		printf("--limit-burst %u ", r->burst);
159}
160
161static struct xtables_match limit_match = {
162	.family		= AF_UNSPEC,
163	.name		= "limit",
164	.version	= XTABLES_VERSION,
165	.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
166	.userspacesize	= offsetof(struct xt_rateinfo, prev),
167	.help		= limit_help,
168	.init		= limit_init,
169	.parse		= limit_parse,
170	.print		= limit_print,
171	.save		= limit_save,
172	.extra_opts	= limit_opts,
173};
174
175void _init(void)
176{
177	xtables_register_match(&limit_match);
178}
179