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 #define _BSD_SOURCE 1
7 #define _DEFAULT_SOURCE 1
8 #define _ISOC99_SOURCE 1
9 #include <errno.h>
10 #include <getopt.h>
11 #include <math.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <xtables.h>
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_limit.h>
18 #include "iptables/nft-bridge.h"
19
20 #define XT_LIMIT_AVG "3/hour"
21 #define XT_LIMIT_BURST 5
22
23 enum {
24 O_LIMIT = 0,
25 O_BURST,
26 };
27
limit_help(void)28 static void limit_help(void)
29 {
30 printf(
31 "limit match options:\n"
32 "--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
33 " [Packets per second unless followed by \n"
34 " /sec /minute /hour /day postfixes]\n"
35 "--limit-burst number number to match in a burst, default %u\n",
36 XT_LIMIT_BURST);
37 }
38
39 static const struct xt_option_entry limit_opts[] = {
40 {.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING},
41 {.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
42 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst),
43 .min = 0, .max = 10000},
44 XTOPT_TABLEEND,
45 };
46
47 static
parse_rate(const char * rate,uint32_t * val)48 int parse_rate(const char *rate, uint32_t *val)
49 {
50 const char *delim;
51 uint32_t r;
52 uint32_t mult = 1; /* Seconds by default. */
53
54 delim = strchr(rate, '/');
55 if (delim) {
56 if (strlen(delim+1) == 0)
57 return 0;
58
59 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
60 mult = 1;
61 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
62 mult = 60;
63 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
64 mult = 60*60;
65 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
66 mult = 24*60*60;
67 else
68 return 0;
69 }
70 r = atoi(rate);
71 if (!r)
72 return 0;
73
74 *val = XT_LIMIT_SCALE * mult / r;
75 if (*val == 0)
76 /*
77 * The rate maps to infinity. (1/day is the minimum they can
78 * specify, so we are ok at that end).
79 */
80 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
81 return 1;
82 }
83
limit_init(struct xt_entry_match * m)84 static void limit_init(struct xt_entry_match *m)
85 {
86 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
87
88 parse_rate(XT_LIMIT_AVG, &r->avg);
89 r->burst = XT_LIMIT_BURST;
90
91 }
92
93 /* FIXME: handle overflow:
94 if (r->avg*r->burst/r->burst != r->avg)
95 xtables_error(PARAMETER_PROBLEM,
96 "Sorry: burst too large for that avg rate.\n");
97 */
98
limit_parse(struct xt_option_call * cb)99 static void limit_parse(struct xt_option_call *cb)
100 {
101 struct xt_rateinfo *r = cb->data;
102
103 xtables_option_parse(cb);
104 switch (cb->entry->id) {
105 case O_LIMIT:
106 if (!parse_rate(cb->arg, &r->avg))
107 xtables_error(PARAMETER_PROBLEM,
108 "bad rate \"%s\"'", cb->arg);
109 break;
110 }
111 if (cb->invert)
112 xtables_error(PARAMETER_PROBLEM,
113 "limit does not support invert");
114 }
115
116 static const struct rates
117 {
118 const char *name;
119 uint32_t mult;
120 } rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
121 { "hour", XT_LIMIT_SCALE*60*60 },
122 { "min", XT_LIMIT_SCALE*60 },
123 { "sec", XT_LIMIT_SCALE } };
124
print_rate(uint32_t period)125 static void print_rate(uint32_t period)
126 {
127 unsigned int i;
128
129 if (period == 0) {
130 printf(" %f", INFINITY);
131 return;
132 }
133
134 for (i = 1; i < ARRAY_SIZE(rates); ++i)
135 if (period > rates[i].mult
136 || rates[i].mult/period < rates[i].mult%period)
137 break;
138
139 printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
140 }
141
142 static void
limit_print(const void * ip,const struct xt_entry_match * match,int numeric)143 limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
144 {
145 const struct xt_rateinfo *r = (const void *)match->data;
146 printf(" limit: avg"); print_rate(r->avg);
147 printf(" burst %u", r->burst);
148 }
149
limit_save(const void * ip,const struct xt_entry_match * match)150 static void limit_save(const void *ip, const struct xt_entry_match *match)
151 {
152 const struct xt_rateinfo *r = (const void *)match->data;
153
154 printf(" --limit"); print_rate(r->avg);
155 if (r->burst != XT_LIMIT_BURST)
156 printf(" --limit-burst %u", r->burst);
157 }
158
159 static const struct rates rates_xlate[] = {
160 { "day", XT_LIMIT_SCALE * 24 * 60 * 60 },
161 { "hour", XT_LIMIT_SCALE * 60 * 60 },
162 { "minute", XT_LIMIT_SCALE * 60 },
163 { "second", XT_LIMIT_SCALE }
164 };
165
print_rate_xlate(uint32_t period,struct xt_xlate * xl)166 static void print_rate_xlate(uint32_t period, struct xt_xlate *xl)
167 {
168 unsigned int i;
169
170 if (period == 0) {
171 xt_xlate_add(xl, " %f", INFINITY);
172 return;
173 }
174
175 for (i = 1; i < ARRAY_SIZE(rates); ++i)
176 if (period > rates_xlate[i].mult ||
177 rates_xlate[i].mult / period < rates_xlate[i].mult % period)
178 break;
179
180 xt_xlate_add(xl, " %u/%s", rates_xlate[i - 1].mult / period,
181 rates_xlate[i - 1].name);
182 }
183
limit_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)184 static int limit_xlate(struct xt_xlate *xl,
185 const struct xt_xlate_mt_params *params)
186 {
187 const struct xt_rateinfo *r = (const void *)params->match->data;
188
189 xt_xlate_add(xl, "limit rate");
190 print_rate_xlate(r->avg, xl);
191 if (r->burst != 0)
192 xt_xlate_add(xl, " burst %u packets", r->burst);
193
194 return 1;
195 }
196
limit_xlate_eb(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)197 static int limit_xlate_eb(struct xt_xlate *xl,
198 const struct xt_xlate_mt_params *params)
199 {
200 limit_xlate(xl, params);
201 xt_xlate_add(xl, " ");
202 return 1;
203 }
204
205 #define FLAG_LIMIT 0x01
206 #define FLAG_LIMIT_BURST 0x02
207 #define ARG_LIMIT '1'
208 #define ARG_LIMIT_BURST '2'
209
brlimit_parse(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_match ** match)210 static int brlimit_parse(int c, char **argv, int invert, unsigned int *flags,
211 const void *entry, struct xt_entry_match **match)
212 {
213 struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
214 uintmax_t num;
215
216 switch (c) {
217 case ARG_LIMIT:
218 EBT_CHECK_OPTION(flags, FLAG_LIMIT);
219 if (invert)
220 xtables_error(PARAMETER_PROBLEM,
221 "Unexpected `!' after --limit");
222 if (!parse_rate(optarg, &r->avg))
223 xtables_error(PARAMETER_PROBLEM,
224 "bad rate `%s'", optarg);
225 break;
226 case ARG_LIMIT_BURST:
227 EBT_CHECK_OPTION(flags, FLAG_LIMIT_BURST);
228 if (invert)
229 xtables_error(PARAMETER_PROBLEM,
230 "Unexpected `!' after --limit-burst");
231 if (!xtables_strtoul(optarg, NULL, &num, 0, 10000))
232 xtables_error(PARAMETER_PROBLEM,
233 "bad --limit-burst `%s'", optarg);
234 r->burst = num;
235 break;
236 default:
237 return 0;
238 }
239
240 return 1;
241 }
242
brlimit_print(const void * ip,const struct xt_entry_match * match,int numeric)243 static void brlimit_print(const void *ip, const struct xt_entry_match *match,
244 int numeric)
245 {
246 const struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
247
248 printf("--limit");
249 print_rate(r->avg);
250 printf(" --limit-burst %u ", r->burst);
251 }
252
253 static const struct option brlimit_opts[] =
254 {
255 { .name = "limit", .has_arg = true, .val = ARG_LIMIT },
256 { .name = "limit-burst",.has_arg = true, .val = ARG_LIMIT_BURST },
257 XT_GETOPT_TABLEEND,
258 };
259
260 static struct xtables_match limit_match[] = {
261 {
262 .family = NFPROTO_UNSPEC,
263 .name = "limit",
264 .version = XTABLES_VERSION,
265 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
266 .userspacesize = offsetof(struct xt_rateinfo, prev),
267 .help = limit_help,
268 .init = limit_init,
269 .x6_parse = limit_parse,
270 .print = limit_print,
271 .save = limit_save,
272 .x6_options = limit_opts,
273 .xlate = limit_xlate,
274 },
275 {
276 .family = NFPROTO_BRIDGE,
277 .name = "limit",
278 .version = XTABLES_VERSION,
279 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
280 .userspacesize = offsetof(struct xt_rateinfo, prev),
281 .help = limit_help,
282 .init = limit_init,
283 .parse = brlimit_parse,
284 .print = brlimit_print,
285 .extra_opts = brlimit_opts,
286 .xlate = limit_xlate_eb,
287 },
288 };
289
_init(void)290 void _init(void)
291 {
292 xtables_register_matches(limit_match, ARRAY_SIZE(limit_match));
293 }
294