Blame extensions/libxt_limit.c

Packit Service d1fe03
/* Shared library add-on to iptables to add limit support.
Packit Service d1fe03
 *
Packit Service d1fe03
 * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
Packit Service d1fe03
 * Hervé Eychenne    <rv@wallfire.org>
Packit Service d1fe03
 */
Packit Service d1fe03
#define _BSD_SOURCE 1
Packit Service d1fe03
#define _DEFAULT_SOURCE 1
Packit Service d1fe03
#define _ISOC99_SOURCE 1
Packit Service d1fe03
#include <errno.h>
Packit Service d1fe03
#include <getopt.h>
Packit Service d1fe03
#include <math.h>
Packit Service d1fe03
#include <stdio.h>
Packit Service d1fe03
#include <string.h>
Packit Service d1fe03
#include <stdlib.h>
Packit Service d1fe03
#include <xtables.h>
Packit Service d1fe03
#include <linux/netfilter/x_tables.h>
Packit Service d1fe03
#include <linux/netfilter/xt_limit.h>
Packit Service d1fe03
#include "iptables/nft-bridge.h"
Packit Service d1fe03
Packit Service d1fe03
#define XT_LIMIT_AVG	"3/hour"
Packit Service d1fe03
#define XT_LIMIT_BURST	5
Packit Service d1fe03
Packit Service d1fe03
enum {
Packit Service d1fe03
	O_LIMIT = 0,
Packit Service d1fe03
	O_BURST,
Packit Service d1fe03
};
Packit Service d1fe03
Packit Service d1fe03
static void limit_help(void)
Packit Service d1fe03
{
Packit Service d1fe03
	printf(
Packit Service d1fe03
"limit match options:\n"
Packit Service d1fe03
"--limit avg			max average match rate: default "XT_LIMIT_AVG"\n"
Packit Service d1fe03
"                                [Packets per second unless followed by \n"
Packit Service d1fe03
"                                /sec /minute /hour /day postfixes]\n"
Packit Service d1fe03
"--limit-burst number		number to match in a burst, default %u\n",
Packit Service d1fe03
XT_LIMIT_BURST);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static const struct xt_option_entry limit_opts[] = {
Packit Service d1fe03
	{.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING},
Packit Service d1fe03
	{.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
Packit Service d1fe03
	 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst),
Packit Service d1fe03
	 .min = 0, .max = 10000},
Packit Service d1fe03
	XTOPT_TABLEEND,
Packit Service d1fe03
};
Packit Service d1fe03
Packit Service d1fe03
static
Packit Service d1fe03
int parse_rate(const char *rate, uint32_t *val)
Packit Service d1fe03
{
Packit Service d1fe03
	const char *delim;
Packit Service d1fe03
	uint32_t r;
Packit Service d1fe03
	uint32_t mult = 1;  /* Seconds by default. */
Packit Service d1fe03
Packit Service d1fe03
	delim = strchr(rate, '/');
Packit Service d1fe03
	if (delim) {
Packit Service d1fe03
		if (strlen(delim+1) == 0)
Packit Service d1fe03
			return 0;
Packit Service d1fe03
Packit Service d1fe03
		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
Packit Service d1fe03
			mult = 1;
Packit Service d1fe03
		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
Packit Service d1fe03
			mult = 60;
Packit Service d1fe03
		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
Packit Service d1fe03
			mult = 60*60;
Packit Service d1fe03
		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
Packit Service d1fe03
			mult = 24*60*60;
Packit Service d1fe03
		else
Packit Service d1fe03
			return 0;
Packit Service d1fe03
	}
Packit Service d1fe03
	r = atoi(rate);
Packit Service d1fe03
	if (!r)
Packit Service d1fe03
		return 0;
Packit Service d1fe03
Packit Service d1fe03
	*val = XT_LIMIT_SCALE * mult / r;
Packit Service d1fe03
	if (*val == 0)
Packit Service d1fe03
		/*
Packit Service d1fe03
		 * The rate maps to infinity. (1/day is the minimum they can
Packit Service d1fe03
		 * specify, so we are ok at that end).
Packit Service d1fe03
		 */
Packit Service d1fe03
		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
Packit Service d1fe03
	return 1;
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static void limit_init(struct xt_entry_match *m)
Packit Service d1fe03
{
Packit Service d1fe03
	struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
Packit Service d1fe03
Packit Service d1fe03
	parse_rate(XT_LIMIT_AVG, &r->avg);
Packit Service d1fe03
	r->burst = XT_LIMIT_BURST;
Packit Service d1fe03
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
/* FIXME: handle overflow:
Packit Service d1fe03
	if (r->avg*r->burst/r->burst != r->avg)
Packit Service d1fe03
		xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
			   "Sorry: burst too large for that avg rate.\n");
Packit Service d1fe03
*/
Packit Service d1fe03
Packit Service d1fe03
static void limit_parse(struct xt_option_call *cb)
Packit Service d1fe03
{
Packit Service d1fe03
	struct xt_rateinfo *r = cb->data;
Packit Service d1fe03
Packit Service d1fe03
	xtables_option_parse(cb);
Packit Service d1fe03
	switch (cb->entry->id) {
Packit Service d1fe03
	case O_LIMIT:
Packit Service d1fe03
		if (!parse_rate(cb->arg, &r->avg))
Packit Service d1fe03
			xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
				   "bad rate \"%s\"'", cb->arg);
Packit Service d1fe03
		break;
Packit Service d1fe03
	}
Packit Service d1fe03
	if (cb->invert)
Packit Service d1fe03
		xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
			   "limit does not support invert");
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static const struct rates
Packit Service d1fe03
{
Packit Service d1fe03
	const char *name;
Packit Service d1fe03
	uint32_t mult;
Packit Service d1fe03
} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
Packit Service d1fe03
	      { "hour", XT_LIMIT_SCALE*60*60 },
Packit Service d1fe03
	      { "min", XT_LIMIT_SCALE*60 },
Packit Service d1fe03
	      { "sec", XT_LIMIT_SCALE } };
Packit Service d1fe03
Packit Service d1fe03
static void print_rate(uint32_t period)
Packit Service d1fe03
{
Packit Service d1fe03
	unsigned int i;
Packit Service d1fe03
Packit Service d1fe03
	if (period == 0) {
Packit Service d1fe03
		printf(" %f", INFINITY);
Packit Service d1fe03
		return;
Packit Service d1fe03
	}
Packit Service d1fe03
Packit Service d1fe03
	for (i = 1; i < ARRAY_SIZE(rates); ++i)
Packit Service d1fe03
		if (period > rates[i].mult
Packit Service d1fe03
            || rates[i].mult/period < rates[i].mult%period)
Packit Service d1fe03
			break;
Packit Service d1fe03
Packit Service d1fe03
	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static void
Packit Service d1fe03
limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
Packit Service d1fe03
{
Packit Service d1fe03
	const struct xt_rateinfo *r = (const void *)match->data;
Packit Service d1fe03
	printf(" limit: avg"); print_rate(r->avg);
Packit Service d1fe03
	printf(" burst %u", r->burst);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static void limit_save(const void *ip, const struct xt_entry_match *match)
Packit Service d1fe03
{
Packit Service d1fe03
	const struct xt_rateinfo *r = (const void *)match->data;
Packit Service d1fe03
Packit Service d1fe03
	printf(" --limit"); print_rate(r->avg);
Packit Service d1fe03
	if (r->burst != XT_LIMIT_BURST)
Packit Service d1fe03
		printf(" --limit-burst %u", r->burst);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static const struct rates rates_xlate[] = {
Packit Service d1fe03
	{ "day",	XT_LIMIT_SCALE * 24 * 60 * 60 },
Packit Service d1fe03
	{ "hour",	XT_LIMIT_SCALE * 60 * 60 },
Packit Service d1fe03
	{ "minute",	XT_LIMIT_SCALE * 60 },
Packit Service d1fe03
	{ "second",	XT_LIMIT_SCALE }
Packit Service d1fe03
};
Packit Service d1fe03
Packit Service d1fe03
static void print_rate_xlate(uint32_t period, struct xt_xlate *xl)
Packit Service d1fe03
{
Packit Service d1fe03
	unsigned int i;
Packit Service d1fe03
Packit Service d1fe03
	if (period == 0) {
Packit Service d1fe03
		xt_xlate_add(xl, " %f", INFINITY);
Packit Service d1fe03
		return;
Packit Service d1fe03
	}
Packit Service d1fe03
Packit Service d1fe03
	for (i = 1; i < ARRAY_SIZE(rates); ++i)
Packit Service d1fe03
		if (period > rates_xlate[i].mult ||
Packit Service d1fe03
		    rates_xlate[i].mult / period < rates_xlate[i].mult % period)
Packit Service d1fe03
			break;
Packit Service d1fe03
Packit Service d1fe03
	xt_xlate_add(xl, " %u/%s", rates_xlate[i - 1].mult / period,
Packit Service d1fe03
		   rates_xlate[i - 1].name);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static int limit_xlate(struct xt_xlate *xl,
Packit Service d1fe03
		       const struct xt_xlate_mt_params *params)
Packit Service d1fe03
{
Packit Service d1fe03
	const struct xt_rateinfo *r = (const void *)params->match->data;
Packit Service d1fe03
Packit Service d1fe03
	xt_xlate_add(xl, "limit rate");
Packit Service d1fe03
	print_rate_xlate(r->avg, xl);
Packit Service d1fe03
	if (r->burst != 0)
Packit Service d1fe03
		xt_xlate_add(xl, " burst %u packets", r->burst);
Packit Service d1fe03
Packit Service d1fe03
	return 1;
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static int limit_xlate_eb(struct xt_xlate *xl,
Packit Service d1fe03
			  const struct xt_xlate_mt_params *params)
Packit Service d1fe03
{
Packit Service d1fe03
	limit_xlate(xl, params);
Packit Service d1fe03
	xt_xlate_add(xl, " ");
Packit Service d1fe03
	return 1;
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
#define FLAG_LIMIT		0x01
Packit Service d1fe03
#define FLAG_LIMIT_BURST	0x02
Packit Service d1fe03
#define ARG_LIMIT		'1'
Packit Service d1fe03
#define ARG_LIMIT_BURST		'2'
Packit Service d1fe03
Packit Service d1fe03
static int brlimit_parse(int c, char **argv, int invert, unsigned int *flags,
Packit Service d1fe03
			 const void *entry, struct xt_entry_match **match)
Packit Service d1fe03
{
Packit Service d1fe03
	struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
Packit Service d1fe03
	uintmax_t num;
Packit Service d1fe03
Packit Service d1fe03
	switch (c) {
Packit Service d1fe03
	case ARG_LIMIT:
Packit Service d1fe03
		EBT_CHECK_OPTION(flags, FLAG_LIMIT);
Packit Service d1fe03
		if (invert)
Packit Service d1fe03
			xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
				      "Unexpected `!' after --limit");
Packit Service d1fe03
		if (!parse_rate(optarg, &r->avg))
Packit Service d1fe03
			xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
				      "bad rate `%s'", optarg);
Packit Service d1fe03
		break;
Packit Service d1fe03
	case ARG_LIMIT_BURST:
Packit Service d1fe03
		EBT_CHECK_OPTION(flags, FLAG_LIMIT_BURST);
Packit Service d1fe03
		if (invert)
Packit Service d1fe03
			xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
				      "Unexpected `!' after --limit-burst");
Packit Service d1fe03
		if (!xtables_strtoul(optarg, NULL, &num, 0, 10000))
Packit Service d1fe03
			xtables_error(PARAMETER_PROBLEM,
Packit Service d1fe03
				      "bad --limit-burst `%s'", optarg);
Packit Service d1fe03
		r->burst = num;
Packit Service d1fe03
		break;
Packit Service d1fe03
	default:
Packit Service d1fe03
		return 0;
Packit Service d1fe03
	}
Packit Service d1fe03
Packit Service d1fe03
	return 1;
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static void brlimit_print(const void *ip, const struct xt_entry_match *match,
Packit Service d1fe03
			  int numeric)
Packit Service d1fe03
{
Packit Service d1fe03
	const struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
Packit Service d1fe03
Packit Service d1fe03
	printf("--limit");
Packit Service d1fe03
	print_rate(r->avg);
Packit Service d1fe03
	printf(" --limit-burst %u ", r->burst);
Packit Service d1fe03
}
Packit Service d1fe03
Packit Service d1fe03
static const struct option brlimit_opts[] =
Packit Service d1fe03
{
Packit Service d1fe03
	{ .name = "limit",	.has_arg = true,	.val = ARG_LIMIT },
Packit Service d1fe03
	{ .name = "limit-burst",.has_arg = true,	.val = ARG_LIMIT_BURST },
Packit Service d1fe03
	XT_GETOPT_TABLEEND,
Packit Service d1fe03
};
Packit Service d1fe03
Packit Service d1fe03
static struct xtables_match limit_match[] = {
Packit Service d1fe03
	{
Packit Service d1fe03
		.family		= NFPROTO_UNSPEC,
Packit Service d1fe03
		.name		= "limit",
Packit Service d1fe03
		.version	= XTABLES_VERSION,
Packit Service d1fe03
		.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
Packit Service d1fe03
		.userspacesize	= offsetof(struct xt_rateinfo, prev),
Packit Service d1fe03
		.help		= limit_help,
Packit Service d1fe03
		.init		= limit_init,
Packit Service d1fe03
		.x6_parse	= limit_parse,
Packit Service d1fe03
		.print		= limit_print,
Packit Service d1fe03
		.save		= limit_save,
Packit Service d1fe03
		.x6_options	= limit_opts,
Packit Service d1fe03
		.xlate		= limit_xlate,
Packit Service d1fe03
	},
Packit Service d1fe03
	{
Packit Service d1fe03
		.family		= NFPROTO_BRIDGE,
Packit Service d1fe03
		.name		= "limit",
Packit Service d1fe03
		.version	= XTABLES_VERSION,
Packit Service d1fe03
		.size		= XT_ALIGN(sizeof(struct xt_rateinfo)),
Packit Service d1fe03
		.userspacesize	= offsetof(struct xt_rateinfo, prev),
Packit Service d1fe03
		.help		= limit_help,
Packit Service d1fe03
		.init		= limit_init,
Packit Service d1fe03
		.parse		= brlimit_parse,
Packit Service d1fe03
		.print		= brlimit_print,
Packit Service d1fe03
		.extra_opts	= brlimit_opts,
Packit Service d1fe03
		.xlate		= limit_xlate_eb,
Packit Service d1fe03
	},
Packit Service d1fe03
};
Packit Service d1fe03
Packit Service d1fe03
void _init(void)
Packit Service d1fe03
{
Packit Service d1fe03
	xtables_register_matches(limit_match, ARRAY_SIZE(limit_match));
Packit Service d1fe03
}