blob: 23924a7f3242d4a32bf92d6f8c9ba39d0f210507 [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;
Harald Welteb4719762001-07-23 02:14:22 +0000101 unsigned int num;
Marc Bouchere6869a82000-03-20 06:03:29 +0000102
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
Harald Welteb4719762001-07-23 02:14:22 +0000118 if (string_to_number(optarg, 0, 10000, &num) <= 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000119 exit_error(PARAMETER_PROBLEM,
120 "bad --limit-burst `%s'", optarg);
121 r->burst = num;
122 break;
123
124 default:
125 return 0;
126 }
127
128 return 1;
129}
130
131/* Final check; nothing. */
132static void final_check(unsigned int flags)
133{
134}
135
136static struct rates
137{
138 const char *name;
139 u_int32_t mult;
140} rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
141 { "hour", IPT_LIMIT_SCALE*60*60 },
142 { "min", IPT_LIMIT_SCALE*60 },
143 { "sec", IPT_LIMIT_SCALE } };
144
145static void print_rate(u_int32_t period)
146{
147 unsigned int i;
148
149 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
150 if (period > rates[i].mult
151 || rates[i].mult % period != 0)
152 break;
153 }
154
155 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
156}
157
158/* Prints out the matchinfo. */
159static void
160print(const struct ipt_ip *ip,
161 const struct ipt_entry_match *match,
162 int numeric)
163{
164 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
165 printf("limit: avg "); print_rate(r->avg);
166 printf("burst %u ", r->burst);
167}
168
169/* FIXME: Make minimalist: only print rate if not default --RR */
170static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
171{
172 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
173
174 printf("--limit "); print_rate(r->avg);
175 if (r->burst != IPT_LIMIT_BURST)
176 printf("--limit-burst %u ", r->burst);
177}
178
179struct iptables_match limit
180= { NULL,
181 "limit",
182 NETFILTER_VERSION,
Rusty Russell73f72f52000-07-03 10:17:57 +0000183 IPT_ALIGN(sizeof(struct ipt_rateinfo)),
Rusty Russelledf14cf2000-04-19 11:26:44 +0000184 offsetof(struct ipt_rateinfo, prev),
Marc Bouchere6869a82000-03-20 06:03:29 +0000185 &help,
186 &init,
187 &parse,
188 &final_check,
189 &print,
190 &save,
191 opts
192};
193
194void _init(void)
195{
196 register_match(&limit);
197}