libxt_limit.c revision 32b8e61e4e5bd405d9ad07bf9468498dfbb19f9e
15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/* Shared library add-on to iptables to add limit support.
2ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch *
3ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Hervé Eychenne    <rv@wallfire.org>
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdbool.h>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <string.h>
968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <stdlib.h>
1068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <getopt.h>
113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <xtables.h>
123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <stddef.h>
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <linux/netfilter/x_tables.h>
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/* For 64bit kernel / 32bit userspace */
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <linux/netfilter/xt_limit.h>
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#define XT_LIMIT_AVG	"3/hour"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define XT_LIMIT_BURST	5
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static void limit_help(void)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles){
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)	printf(
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"limit match options:\n"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"--limit avg			max average match rate: default "XT_LIMIT_AVG"\n"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"                                [Packets per second unless followed by \n"
26ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch"                                /sec /minute /hour /day postfixes]\n"
27ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch"--limit-burst number		number to match in a burst, default %u\n",
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)XT_LIMIT_BURST);
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static const struct option limit_opts[] = {
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	{.name = "limit",       .has_arg = true, .val = '%'},
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	{.name = "limit-burst", .has_arg = true, .val = '$'},
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	XT_GETOPT_TABLEEND,
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)static
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int parse_rate(const char *rate, u_int32_t *val)
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	const char *delim;
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	u_int32_t r;
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	u_int32_t mult = 1;  /* Seconds by default. */
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	delim = strchr(rate, '/');
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	if (delim) {
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci		if (strlen(delim+1) == 0)
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci			return 0;
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)			mult = 1;
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)			mult = 60;
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)			mult = 60*60;
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			mult = 24*60*60;
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)		else
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)			return 0;
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	}
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	r = atoi(rate);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!r)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return 0;
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	/* This would get mapped to infinite (1/day is minimum they
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           can specify, so we're ok at that end). */
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (r / mult > XT_LIMIT_SCALE)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	*val = XT_LIMIT_SCALE * mult / r;
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	return 1;
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void limit_init(struct xt_entry_match *m)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	parse_rate(XT_LIMIT_AVG, &r->avg);
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)	r->burst = XT_LIMIT_BURST;
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
80010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* FIXME: handle overflow:
838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)	if (r->avg*r->burst/r->burst != r->avg)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			   "Sorry: burst too large for that avg rate.\n");
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)*/
878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)static int
893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)limit_parse(int c, char **argv, int invert, unsigned int *flags,
903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)            const void *entry, struct xt_entry_match **match)
91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch{
923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int num;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	switch(c) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	case '%':
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (!parse_rate(optarg, &r->avg))
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)				   "bad rate `%s'", optarg);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		break;
1023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)	case '$':
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)		if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break;
105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)		if (!xtables_strtoui(optarg, NULL, &num, 0, 10000))
1063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
107868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)				   "bad --limit-burst `%s'", optarg);
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)		r->burst = num;
109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)		break;
1103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)	default:
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)		return 0;
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)	}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (invert)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM,
1173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)			   "limit does not support invert");
11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return 1;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct rates
1233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles){
1243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	const char *name;
1253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)	u_int32_t mult;
1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	      { "hour", XT_LIMIT_SCALE*60*60 },
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	      { "min", XT_LIMIT_SCALE*60 },
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	      { "sec", XT_LIMIT_SCALE } };
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void print_rate(u_int32_t period)
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{
1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	unsigned int i;
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	for (i = 1; i < ARRAY_SIZE(rates); ++i)
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci		if (period > rates[i].mult
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            || rates[i].mult/period < rates[i].mult%period)
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci			break;
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccilimit_print(const void *ip, const struct xt_entry_match *match, int numeric)
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	const struct xt_rateinfo *r = (const void *)match->data;
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	printf("limit: avg "); print_rate(r->avg);
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	printf("burst %u ", r->burst);
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
151010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static void limit_save(const void *ip, const struct xt_entry_match *match)
152424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles){
153424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	const struct xt_rateinfo *r = (const void *)match->data;
154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
155424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	printf("--limit "); print_rate(r->avg);
156424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	if (r->burst != XT_LIMIT_BURST)
157424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)		printf("--limit-burst %u ", r->burst);
158424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
159424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)static struct xtables_match limit_match = {
161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.family		= NFPROTO_UNSPEC,
1624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)	.name		= "limit",
163424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.version	= XTABLES_VERSION,
164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)	.userspacesize	= offsetof(struct xt_rateinfo, prev),
166424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.help		= limit_help,
167424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.init		= limit_init,
168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.parse		= limit_parse,
169424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.print		= limit_print,
170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.save		= limit_save,
171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	.extra_opts	= limit_opts,
172424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)};
173424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void _init(void)
175424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles){
176424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)	xtables_register_match(&limit_match);
177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
178424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)