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 <ip6tables.h>
12#include <stddef.h>
13#include <linux/netfilter_ipv6/ip6_tables.h>
14#include <linux/netfilter_ipv6/ip6t_limit.h>
15
16#define IP6T_LIMIT_AVG	"3/hour"
17#define IP6T_LIMIT_BURST	5
18
19/* Function which prints out usage message. */
20static void
21help(void)
22{
23	printf(
24"limit v%s options:\n"
25"--limit avg			max average match rate: default "IP6T_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"
29"\n", IPTABLES_VERSION, IP6T_LIMIT_BURST);
30}
31
32static struct option opts[] = {
33	{ "limit", 1, 0, '%' },
34	{ "limit-burst", 1, 0, '$' },
35	{ 0 }
36};
37
38static
39int parse_rate(const char *rate, u_int32_t *val)
40{
41	const char *delim;
42	u_int32_t r;
43	u_int32_t mult = 1;  /* Seconds by default. */
44
45	delim = strchr(rate, '/');
46	if (delim) {
47		if (strlen(delim+1) == 0)
48			return 0;
49
50		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
51			mult = 1;
52		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
53			mult = 60;
54		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
55			mult = 60*60;
56		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
57			mult = 24*60*60;
58		else
59			return 0;
60	}
61	r = atoi(rate);
62	if (!r)
63		return 0;
64
65	/* This would get mapped to infinite (1/day is minimum they
66           can specify, so we're ok at that end). */
67	if (r / mult > IP6T_LIMIT_SCALE)
68		exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
69
70	*val = IP6T_LIMIT_SCALE * mult / r;
71	return 1;
72}
73
74/* Initialize the match. */
75static void
76init(struct ip6t_entry_match *m, unsigned int *nfcache)
77{
78	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)m->data;
79
80	parse_rate(IP6T_LIMIT_AVG, &r->avg);
81	r->burst = IP6T_LIMIT_BURST;
82
83	/* Can't cache this */
84	*nfcache |= NFC_UNKNOWN;
85}
86
87
88/* Function which parses command options; returns true if it
89   ate an option */
90static int
91parse(int c, char **argv, int invert, unsigned int *flags,
92      const struct ip6t_entry *entry,
93      unsigned int *nfcache,
94      struct ip6t_entry_match **match)
95{
96	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)(*match)->data;
97	unsigned int num;
98
99	switch(c) {
100	case '%':
101		if (check_inverse(optarg, &invert, NULL, 0))
102			exit_error(PARAMETER_PROBLEM,
103				   "Unexpected `!' after --limit");
104		if (!parse_rate(optarg, &r->avg))
105			exit_error(PARAMETER_PROBLEM,
106				   "bad rate `%s'", optarg);
107		break;
108
109	case '$':
110		if (check_inverse(optarg, &invert, NULL, 0))
111			exit_error(PARAMETER_PROBLEM,
112				   "Unexpected `!' after --limit-burst");
113
114		if (string_to_number(optarg, 0, 10000, &num) == -1)
115			exit_error(PARAMETER_PROBLEM,
116				   "bad --limit-burst `%s'", optarg);
117		r->burst = num;
118		break;
119
120	default:
121		return 0;
122	}
123
124	return 1;
125}
126
127/* Final check; nothing. */
128static void final_check(unsigned int flags)
129{
130}
131
132static struct rates
133{
134	const char *name;
135	u_int32_t mult;
136} rates[] = { { "day", IP6T_LIMIT_SCALE*24*60*60 },
137	      { "hour", IP6T_LIMIT_SCALE*60*60 },
138	      { "min", IP6T_LIMIT_SCALE*60 },
139	      { "sec", IP6T_LIMIT_SCALE } };
140
141static void print_rate(u_int32_t period)
142{
143	unsigned int i;
144
145	for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
146		if (period > rates[i].mult
147		    || rates[i].mult % period != 0)
148			break;
149	}
150
151	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
152}
153
154/* Prints out the matchinfo. */
155static void
156print(const struct ip6t_ip6 *ip,
157      const struct ip6t_entry_match *match,
158      int numeric)
159{
160	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
161	printf("limit: avg "); print_rate(r->avg);
162	printf("burst %u ", r->burst);
163}
164
165static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
166{
167	struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
168
169	printf("--limit "); print_rate(r->avg);
170	if (r->burst != IP6T_LIMIT_BURST)
171		printf("--limit-burst %u ", r->burst);
172}
173
174static
175struct ip6tables_match limit
176= { NULL,
177    "limit",
178    IPTABLES_VERSION,
179    IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
180    offsetof(struct ip6t_rateinfo, prev),
181    &help,
182    &init,
183    &parse,
184    &final_check,
185    &print,
186    &save,
187    opts
188};
189
190void _init(void)
191{
192	register_match6(&limit);
193}
194