blob: e7dda7bc64fc96f44b9a15ac865d97a326c13edb [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* 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 <eychenne@info.enserb.u-bordeaux.fr>
5 */
6#include <stdio.h>
7#include <string.h>
8#include <stdlib.h>
9#include <getopt.h>
10#include <iptables.h>
Rusty Russell849779c2000-04-23 15:51:51 +000011#include <stddef.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000012#include <linux/netfilter_ipv4/ip_tables.h>
13#include <linux/netfilter_ipv4/ipt_limit.h>
14
15#define IPT_LIMIT_AVG "3/hour"
16#define IPT_LIMIT_BURST 5
17
18/* Function which prints out usage message. */
19static void
20help(void)
21{
22 printf(
23"limit v%s options:\n"
24"--limit avg max average match rate: default "IPT_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"
28"\n", NETFILTER_VERSION, IPT_LIMIT_BURST);
29}
30
31static struct option opts[] = {
32 { "limit", 1, 0, '%' },
33 { "limit-burst", 1, 0, '$' },
34 { 0 }
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 > IPT_LIMIT_SCALE)
67 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
68
69 *val = IPT_LIMIT_SCALE * mult / r;
70 return 1;
71}
72
73/* Initialize the match. */
74static void
75init(struct ipt_entry_match *m, unsigned int *nfcache)
76{
77 struct ipt_rateinfo *r = (struct ipt_rateinfo *)m->data;
78
79 parse_rate(IPT_LIMIT_AVG, &r->avg);
80 r->burst = IPT_LIMIT_BURST;
81
82 /* Can't cache this */
83 *nfcache |= NFC_UNKNOWN;
84}
85
86/* FIXME: handle overflow:
87 if (r->avg*r->burst/r->burst != r->avg)
88 exit_error(PARAMETER_PROBLEM,
89 "Sorry: burst too large for that avg rate.\n");
90*/
91
92/* Function which parses command options; returns true if it
93 ate an option */
94static int
95parse(int c, char **argv, int invert, unsigned int *flags,
96 const struct ipt_entry *entry,
97 unsigned int *nfcache,
98 struct ipt_entry_match **match)
99{
100 struct ipt_rateinfo *r = (struct ipt_rateinfo *)(*match)->data;
101 int num;
102
103 switch(c) {
104 case '%':
105 if (check_inverse(optarg, &invert))
106 exit_error(PARAMETER_PROBLEM,
107 "Unexpected `!' after --limit");
108 if (!parse_rate(optarg, &r->avg))
109 exit_error(PARAMETER_PROBLEM,
110 "bad rate `%s'", optarg);
Rusty Russell7e53bf92000-03-20 07:03:28 +0000111 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000112
113 case '$':
114 if (check_inverse(optarg, &invert))
115 exit_error(PARAMETER_PROBLEM,
116 "Unexpected `!' after --limit-burst");
117
118 num = string_to_number(optarg, 0, 10000);
119 if (num <= 0)
120 exit_error(PARAMETER_PROBLEM,
121 "bad --limit-burst `%s'", optarg);
122 r->burst = num;
123 break;
124
125 default:
126 return 0;
127 }
128
129 return 1;
130}
131
132/* Final check; nothing. */
133static void final_check(unsigned int flags)
134{
135}
136
137static struct rates
138{
139 const char *name;
140 u_int32_t mult;
141} rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
142 { "hour", IPT_LIMIT_SCALE*60*60 },
143 { "min", IPT_LIMIT_SCALE*60 },
144 { "sec", IPT_LIMIT_SCALE } };
145
146static void print_rate(u_int32_t period)
147{
148 unsigned int i;
149
150 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
151 if (period > rates[i].mult
152 || rates[i].mult % period != 0)
153 break;
154 }
155
156 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
157}
158
159/* Prints out the matchinfo. */
160static void
161print(const struct ipt_ip *ip,
162 const struct ipt_entry_match *match,
163 int numeric)
164{
165 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
166 printf("limit: avg "); print_rate(r->avg);
167 printf("burst %u ", r->burst);
168}
169
170/* FIXME: Make minimalist: only print rate if not default --RR */
171static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
172{
173 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
174
175 printf("--limit "); print_rate(r->avg);
176 if (r->burst != IPT_LIMIT_BURST)
177 printf("--limit-burst %u ", r->burst);
178}
179
180struct iptables_match limit
181= { NULL,
182 "limit",
183 NETFILTER_VERSION,
184 sizeof(struct ipt_rateinfo),
Rusty Russelledf14cf2000-04-19 11:26:44 +0000185 offsetof(struct ipt_rateinfo, prev),
Marc Bouchere6869a82000-03-20 06:03:29 +0000186 &help,
187 &init,
188 &parse,
189 &final_check,
190 &print,
191 &save,
192 opts
193};
194
195void _init(void)
196{
197 register_match(&limit);
198}