summaryrefslogtreecommitdiffstats
path: root/extensions/libxt_hashlimit.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2012-05-08 03:16:52 +0000
committerPablo Neira Ayuso <pablo@netfilter.org>2012-07-14 16:14:50 +0200
commitabdef13f36b63758f8775eb86febd96bf062df6f (patch)
tree930d0579990a21c66044e3eb53f50868552f234c /extensions/libxt_hashlimit.c
parent874d7ee3c36ba54220fd204e6aa7cbc731a66395 (diff)
libxt_hashlimit: add support for byte-based operation
allows --hashlimit-(upto|above) Xb/s [ --hashlimit-burst Yb ] to make hashlimit match when X bytes/second are exceeded; optionally, Y bytes will not be matched (i.e. bursted). [ Pablo fixed minor compilation warning in this patch with gcc-4.6 and x86_64 ] libxt_hashlimit.c: In function ‘parse_bytes’: libxt_hashlimit.c:216:6: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 3 has type ‘uint64_t’ [-Wformat] Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'extensions/libxt_hashlimit.c')
-rw-r--r--extensions/libxt_hashlimit.c171
1 files changed, 160 insertions, 11 deletions
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index da34cb22..37a31489 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -20,6 +20,10 @@
#include <linux/netfilter/xt_hashlimit.h>
#define XT_HASHLIMIT_BURST 5
+#define XT_HASHLIMIT_BURST_MAX 10000
+
+#define XT_HASHLIMIT_BYTE_EXPIRE 15
+#define XT_HASHLIMIT_BYTE_EXPIRE_BURST 60
/* miliseconds */
#define XT_HASHLIMIT_GCINTERVAL 1000
@@ -59,6 +63,7 @@ enum {
O_HTABLE_MAX,
O_HTABLE_GCINT,
O_HTABLE_EXPIRE,
+ F_BURST = 1 << O_BURST,
F_UPTO = 1 << O_UPTO,
F_ABOVE = 1 << O_ABOVE,
F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE,
@@ -90,7 +95,7 @@ static const struct xt_option_entry hashlimit_opts[] = {
{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
.type = XTTYPE_STRING},
{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
- .min = 1, .max = 10000, .flags = XTOPT_PUT,
+ .min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.burst)},
{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
@@ -122,9 +127,7 @@ static const struct xt_option_entry hashlimit_mt_opts[] = {
.type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
- {.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
- .min = 1, .max = 10000, .flags = XTOPT_PUT,
- XTOPT_POINTER(s, cfg.burst)},
+ {.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
.type = XTTYPE_UINT32, .flags = XTOPT_PUT,
XTOPT_POINTER(s, cfg.size)},
@@ -144,6 +147,82 @@ static const struct xt_option_entry hashlimit_mt_opts[] = {
};
#undef s
+static uint32_t cost_to_bytes(uint32_t cost)
+{
+ uint32_t r;
+
+ r = cost ? UINT32_MAX / cost : UINT32_MAX;
+ r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
+ return r;
+}
+
+static uint64_t bytes_to_cost(uint32_t bytes)
+{
+ uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
+ return UINT32_MAX / (r+1);
+}
+
+static uint32_t get_factor(int chr)
+{
+ switch (chr) {
+ case 'm': return 1024 * 1024;
+ case 'k': return 1024;
+ }
+ return 1;
+}
+
+static void burst_error(void)
+{
+ xtables_error(PARAMETER_PROBLEM, "bad value for option "
+ "\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
+}
+
+static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
+{
+ uintmax_t v;
+ char *end;
+
+ if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
+ (*end == 0 && v > XT_HASHLIMIT_BURST_MAX))
+ burst_error();
+
+ v *= get_factor(*end);
+ if (v > UINT32_MAX)
+ xtables_error(PARAMETER_PROBLEM, "bad value for option "
+ "\"--hashlimit-burst\", value \"%s\" too large "
+ "(max %umb).", burst, UINT32_MAX/1024/1024);
+ return v;
+}
+
+static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+{
+ unsigned int factor = 1;
+ uint64_t tmp;
+ int r;
+ const char *mode = strstr(rate, "b/s");
+ if (!mode || mode == rate)
+ return false;
+
+ mode--;
+ r = atoi(rate);
+ if (r == 0)
+ return false;
+
+ factor = get_factor(*mode);
+ tmp = (uint64_t) r * factor;
+ if (tmp > UINT32_MAX)
+ xtables_error(PARAMETER_PROBLEM,
+ "Rate value too large \"%llu\" (max %u)\n",
+ (unsigned long long)tmp, UINT32_MAX);
+
+ *val = bytes_to_cost(tmp);
+ if (*val == 0)
+ xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
+
+ ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
+ return true;
+}
+
static
int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
{
@@ -265,17 +344,24 @@ static void hashlimit_mt_parse(struct xt_option_call *cb)
xtables_option_parse(cb);
switch (cb->entry->id) {
+ case O_BURST:
+ info->cfg.burst = parse_burst(cb->arg, info);
+ break;
case O_UPTO:
if (cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
- if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+ info->cfg.mode |= XT_HASHLIMIT_BYTES;
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-upto", cb->arg);
break;
case O_ABOVE:
if (!cb->invert)
info->cfg.mode |= XT_HASHLIMIT_INVERT;
- if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+ if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+ info->cfg.mode |= XT_HASHLIMIT_BYTES;
+ else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
xtables_param_act(XTF_BAD_VALUE, "hashlimit",
"--hashlimit-above", cb->arg);
break;
@@ -315,6 +401,24 @@ static void hashlimit_mt_check(struct xt_fcheck_call *cb)
"You have to specify --hashlimit");
if (!(cb->xflags & F_HTABLE_EXPIRE))
info->cfg.expire = udata->mult * 1000; /* from s to msec */
+
+ if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+ uint32_t burst = 0;
+ if (cb->xflags & F_BURST) {
+ if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
+ xtables_error(PARAMETER_PROBLEM,
+ "burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
+
+ burst = info->cfg.burst;
+ burst /= cost_to_bytes(info->cfg.avg);
+ if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
+ burst++;
+ if (!(cb->xflags & F_HTABLE_EXPIRE))
+ info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
+ }
+ info->cfg.burst = burst;
+ } else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
+ burst_error();
}
static const struct rates
@@ -340,6 +444,41 @@ static uint32_t print_rate(uint32_t period)
return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
}
+static const struct {
+ const char *name;
+ uint32_t thresh;
+} units[] = {
+ { "m", 1024 * 1024 },
+ { "k", 1024 },
+ { "", 1 },
+};
+
+static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
+{
+ unsigned int i;
+ unsigned long long r;
+
+ r = cost_to_bytes(avg);
+
+ for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+ if (r >= units[i].thresh &&
+ bytes_to_cost(r & ~(units[i].thresh - 1)) == avg)
+ break;
+ printf(" %llu%sb/s", r/units[i].thresh, units[i].name);
+
+ if (burst == 0)
+ return XT_HASHLIMIT_BYTE_EXPIRE * 1000;
+
+ r *= burst;
+ printf(" %s", prefix);
+ for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+ if (r >= units[i].thresh)
+ break;
+
+ printf("burst %llu%sb", r / units[i].thresh, units[i].name);
+ return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
+}
+
static void print_mode(unsigned int mode, char separator)
{
bool prevmode = false;
@@ -398,8 +537,13 @@ hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
fputs(" limit: above", stdout);
else
fputs(" limit: up to", stdout);
- quantum = print_rate(info->cfg.avg);
- printf(" burst %u", info->cfg.burst);
+
+ if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+ quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
+ } else {
+ quantum = print_rate(info->cfg.avg);
+ printf(" burst %u", info->cfg.burst);
+ }
if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
fputs(" mode", stdout);
@@ -449,7 +593,7 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
fputs(" --hashlimit-mode", stdout);
print_mode(r->cfg.mode, ',');
-
+
printf(" --hashlimit-name %s", r->name);
if (r->cfg.size)
@@ -471,8 +615,13 @@ hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
fputs(" --hashlimit-above", stdout);
else
fputs(" --hashlimit-upto", stdout);
- quantum = print_rate(info->cfg.avg);
- printf(" --hashlimit-burst %u", info->cfg.burst);
+
+ if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+ quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
+ } else {
+ quantum = print_rate(info->cfg.avg);
+ printf(" --hashlimit-burst %u", info->cfg.burst);
+ }
if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {