• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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