|
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 |
}
|