From patchwork Thu Jul 7 23:34:52 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vishwanath Pai X-Patchwork-Id: 646223 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3rlv9c3VXYz9sDk for ; Fri, 8 Jul 2016 09:35:00 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=akamai.com header.i=@akamai.com header.b=ex33HR7b; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753550AbcGGXez (ORCPT ); Thu, 7 Jul 2016 19:34:55 -0400 Received: from prod-mail-xrelay05.akamai.com ([23.79.238.179]:13101 "EHLO prod-mail-xrelay05.akamai.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753044AbcGGXex (ORCPT ); Thu, 7 Jul 2016 19:34:53 -0400 Received: from prod-mail-xrelay05.akamai.com (localhost.localdomain [127.0.0.1]) by postfix.imss70 (Postfix) with ESMTP id 753E9423AEB; Thu, 7 Jul 2016 23:34:52 +0000 (GMT) Received: from prod-mail-relay10.akamai.com (prod-mail-relay10.akamai.com [172.27.118.251]) by prod-mail-xrelay05.akamai.com (Postfix) with ESMTP id 47631423A5F; Thu, 7 Jul 2016 23:34:52 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akamai.com; s=a1; t=1467934492; bh=RhGwqDf+m1WX8xTHmL6RD+8nUC/eX2ElHQHWNBgD9Ek=; l=26049; h=Date:From:To:Cc:From; b=ex33HR7bpqkWmMkRZbSgTIgK7JB5IpQK3U1K2t5kFyCHsHj+guQgHIW55zBVtk3kU oNhhPFch0frj5YaYMic230I4mo+7FlHJhAu44f2dGG5tM9r9PyvSY0DqD3cDz6tU/C L9/qOsG1KfYykWBHq+dgEIS74i5My+F9R0zFt4zA= Received: from bos-lpqrs.kendall.corp.akamai.com (bos-lpqrs.kendall.corp.akamai.com [172.28.13.81]) by prod-mail-relay10.akamai.com (Postfix) with ESMTP id 401D51FC94; Thu, 7 Jul 2016 23:34:52 +0000 (GMT) Received: from vpai by bos-lpqrs.kendall.corp.akamai.com with local (Exim 4.82) (envelope-from ) id 1bLIom-0001OY-6J; Thu, 07 Jul 2016 19:34:52 -0400 Date: Thu, 7 Jul 2016 19:34:52 -0400 From: Vishwanath Pai To: pablo@netfilter.org, kaber@trash.net, kadlec@blackhole.kfki.hu Cc: johunt@akamai.com, netfilter-devel@vger.kernel.org, coreteam@netfilter.org, netdev@vger.kernel.org, pai.vishwain@gmail.com Subject: [PATCH v2 2/2] libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps rates Message-ID: <20160707233452.GD5026@akamai.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org * Added two tests for 1000000pps support to libxt_hashlimit.t * cfg_copy returns -EINVAL and all callers of cfg_copy will look for the error value --- libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps rates Create a new revision for the hashlimit iptables extension module. Rev 2 will support higher pps of upto 1 million, Version 1 supports only 10k. To support this we have to increase the size of the variables avg and burst in hashlimit_cfg to 64-bit. Create two new structs hashlimit_cfg2 and xt_hashlimit_mtinfo2 and also create newer versions of all the functions for match, checkentry and destory. Signed-off-by: Vishwanath Pai Signed-off-by: Joshua Hunt --- extensions/libxt_hashlimit.c | 460 ++++++++++++++++++++++++++------- extensions/libxt_hashlimit.t | 2 + include/linux/netfilter/xt_hashlimit.h | 23 ++ 3 files changed, 394 insertions(+), 91 deletions(-) diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c index ad7fb93..1814c29 100644 --- a/extensions/libxt_hashlimit.c +++ b/extensions/libxt_hashlimit.c @@ -18,12 +18,14 @@ #include #include #include +#include #include #include #include #define XT_HASHLIMIT_BURST 5 #define XT_HASHLIMIT_BURST_MAX_v1 10000 +#define XT_HASHLIMIT_BURST_MAX 1000000 #define XT_HASHLIMIT_BYTE_EXPIRE 15 #define XT_HASHLIMIT_BYTE_EXPIRE_BURST 60 @@ -150,16 +152,70 @@ static const struct xt_option_entry hashlimit_mt_opts_v1[] = { }; #undef s -static uint32_t cost_to_bytes(uint32_t cost) +#define s struct xt_hashlimit_mtinfo2 +static const struct xt_option_entry hashlimit_mt_opts[] = { + {.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE, + .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, + {.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO, + .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, + {.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE, + .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_STRING}, + {.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE, + .type = XTTYPE_UINT32, .flags = XTOPT_PUT, + XTOPT_POINTER(s, cfg.size)}, + {.name = "hashlimit-htable-max", .id = O_HTABLE_MAX, + .type = XTTYPE_UINT32, .flags = XTOPT_PUT, + XTOPT_POINTER(s, cfg.max)}, + {.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT, + .type = XTTYPE_UINT32, .flags = XTOPT_PUT, + XTOPT_POINTER(s, cfg.gc_interval)}, + {.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE, + .type = XTTYPE_UINT32, .flags = XTOPT_PUT, + XTOPT_POINTER(s, cfg.expire)}, + {.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING}, + {.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING, + .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1}, + XTOPT_TABLEEND, +}; +#undef s + +static int +cfg_copy(struct hashlimit_cfg2 *to, const void *from, int revision) +{ + if (revision == 1) { + struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from; + + to->mode = cfg->mode; + to->avg = cfg->avg; + to->burst = cfg->burst; + to->size = cfg->size; + to->max = cfg->max; + to->gc_interval = cfg->gc_interval; + to->expire = cfg->expire; + to->srcmask = cfg->srcmask; + to->dstmask = cfg->dstmask; + } else if (revision == 2) { + memcpy(to, from, sizeof(struct hashlimit_cfg2)); + } else { + return -EINVAL; + } + + return 0; +} + +static uint64_t cost_to_bytes(uint64_t cost) { - uint32_t r; + uint64_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) +static uint64_t bytes_to_cost(uint64_t bytes) { uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT; return UINT32_MAX / (r+1); @@ -180,57 +236,78 @@ static void burst_error_v1(void) "\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX_v1); } -static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info) +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 uint64_t parse_burst(const char *burst, int revision) { uintmax_t v; char *end; - - if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) || - (*end == 0 && v > XT_HASHLIMIT_BURST_MAX_v1)) - burst_error_v1(); + uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX; + uint64_t burst_max = (revision == 1) ? + XT_HASHLIMIT_BURST_MAX_v1 : XT_HASHLIMIT_BURST_MAX; + + if (!xtables_strtoul(burst, &end, &v, 1, max) || + (*end == 0 && v > burst_max)) { + if (revision == 1) + burst_error_v1(); + else + burst_error(); + } v *= get_factor(*end); - if (v > UINT32_MAX) + if (v > max) xtables_error(PARAMETER_PROBLEM, "bad value for option " "\"--hashlimit-burst\", value \"%s\" too large " - "(max %umb).", burst, UINT32_MAX/1024/1024); + "(max %lumb).", burst, max/1024/1024); return v; } -static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud) +static bool parse_bytes(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision) { unsigned int factor = 1; - uint64_t tmp; - int r; + uint64_t tmp, r; const char *mode = strstr(rate, "b/s"); + uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX; + if (!mode || mode == rate) return false; mode--; - r = atoi(rate); + r = atoll(rate); if (r == 0) return false; factor = get_factor(*mode); tmp = (uint64_t) r * factor; - if (tmp > UINT32_MAX) + if (tmp > max) xtables_error(PARAMETER_PROBLEM, - "Rate value too large \"%llu\" (max %u)\n", - (unsigned long long)tmp, UINT32_MAX); + "Rate value too large \"%llu\" (max %lu)\n", + (unsigned long long)tmp, max); - *val = bytes_to_cost(tmp); - if (*val == 0) + tmp = bytes_to_cost(tmp); + if (tmp == 0) xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate); ud->mult = XT_HASHLIMIT_BYTE_EXPIRE; + + if(revision == 1) + *((uint32_t*)val) = tmp; + else + *((uint64_t*)val) = tmp; + return true; } static -int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud) +int parse_rate(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision) { const char *delim; - uint32_t r; + uint64_t tmp, r; + uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE_v1 : XT_HASHLIMIT_SCALE; ud->mult = 1; /* Seconds by default. */ delim = strchr(rate, '/'); @@ -249,17 +326,23 @@ int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud) else return 0; } - r = atoi(rate); + r = atoll(rate); if (!r) return 0; - *val = XT_HASHLIMIT_SCALE_v1 * ud->mult / r; - if (*val == 0) + tmp = scale * ud->mult / r; + if (tmp == 0) /* * The rate maps to infinity. (1/day is the minimum they can * specify, so we are ok at that end). */ xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); + + if(revision == 1) + *((uint32_t*)val) = tmp; + else + *((uint64_t*)val) = tmp; + return 1; } @@ -294,6 +377,28 @@ static void hashlimit_mt6_init_v1(struct xt_entry_match *match) info->cfg.dstmask = 128; } +static void hashlimit_mt4_init(struct xt_entry_match *match) +{ + struct xt_hashlimit_mtinfo2 *info = (void *)match->data; + + info->cfg.mode = 0; + info->cfg.burst = XT_HASHLIMIT_BURST; + info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; + info->cfg.srcmask = 32; + info->cfg.dstmask = 32; +} + +static void hashlimit_mt6_init(struct xt_entry_match *match) +{ + struct xt_hashlimit_mtinfo2 *info = (void *)match->data; + + info->cfg.mode = 0; + info->cfg.burst = XT_HASHLIMIT_BURST; + info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; + info->cfg.srcmask = 128; + info->cfg.dstmask = 128; +} + /* Parse a 'mode' parameter into the required bitmask */ static int parse_mode(uint32_t *mode, const char *option_arg) { @@ -330,7 +435,7 @@ static void hashlimit_parse(struct xt_option_call *cb) xtables_option_parse(cb); switch (cb->entry->id) { case O_UPTO: - if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) + if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1)) xtables_param_act(XTF_BAD_VALUE, "hashlimit", "--hashlimit-upto", cb->arg); break; @@ -349,23 +454,64 @@ static void hashlimit_mt_parse_v1(struct xt_option_call *cb) xtables_option_parse(cb); switch (cb->entry->id) { case O_BURST: - info->cfg.burst = parse_burst(cb->arg, info); + info->cfg.burst = parse_burst(cb->arg, 1); break; case O_UPTO: if (cb->invert) info->cfg.mode |= XT_HASHLIMIT_INVERT; - if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata)) + if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1)) info->cfg.mode |= XT_HASHLIMIT_BYTES; - else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) + else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1)) 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_bytes(cb->arg, &info->cfg.avg, cb->udata)) + if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1)) info->cfg.mode |= XT_HASHLIMIT_BYTES; - else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) + else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-above", cb->arg); + break; + case O_MODE: + if (parse_mode(&info->cfg.mode, cb->arg) < 0) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-mode", cb->arg); + break; + case O_SRCMASK: + info->cfg.srcmask = cb->val.hlen; + break; + case O_DSTMASK: + info->cfg.dstmask = cb->val.hlen; + break; + } +} + +static void hashlimit_mt_parse(struct xt_option_call *cb) +{ + struct xt_hashlimit_mtinfo2 *info = cb->data; + + xtables_option_parse(cb); + switch (cb->entry->id) { + case O_BURST: + info->cfg.burst = parse_burst(cb->arg, 2); + break; + case O_UPTO: + if (cb->invert) + info->cfg.mode |= XT_HASHLIMIT_INVERT; + if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2)) + info->cfg.mode |= XT_HASHLIMIT_BYTES; + else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2)) + 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_bytes(cb->arg, &info->cfg.avg, cb->udata, 2)) + info->cfg.mode |= XT_HASHLIMIT_BYTES; + else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2)) xtables_param_act(XTF_BAD_VALUE, "hashlimit", "--hashlimit-above", cb->arg); break; @@ -411,7 +557,7 @@ static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb) 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 cannot be smaller than %lub", cost_to_bytes(info->cfg.avg)); burst = info->cfg.burst; burst /= cost_to_bytes(info->cfg.avg); @@ -425,32 +571,70 @@ static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb) burst_error_v1(); } -static const struct rates_v1 +static void hashlimit_mt_check(struct xt_fcheck_call *cb) { + const struct hashlimit_mt_udata *udata = cb->udata; + struct xt_hashlimit_mtinfo2 *info = cb->data; + + if (!(cb->xflags & (F_UPTO | F_ABOVE))) + xtables_error(PARAMETER_PROBLEM, + "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 %lub", 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(); +} + +struct rates { const char *name; - uint32_t mult; -} rates_v1[] = { { "day", XT_HASHLIMIT_SCALE_v1*24*60*60 }, + uint64_t mult; +}; + +static const struct rates rates_v1[] = { { "day", XT_HASHLIMIT_SCALE_v1*24*60*60 }, { "hour", XT_HASHLIMIT_SCALE_v1*60*60 }, { "min", XT_HASHLIMIT_SCALE_v1*60 }, { "sec", XT_HASHLIMIT_SCALE_v1 } }; -static uint32_t print_rate(uint32_t period) +static const struct rates rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 }, + { "hour", XT_HASHLIMIT_SCALE*60*60 }, + { "min", XT_HASHLIMIT_SCALE*60 }, + { "sec", XT_HASHLIMIT_SCALE } }; + +static uint32_t print_rate(uint32_t period, int revision) { unsigned int i; + const struct rates *_rates = (revision == 1) ? rates_v1 : rates; + uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE_v1 : XT_HASHLIMIT_SCALE; if (period == 0) { printf(" %f", INFINITY); return 0; } - for (i = 1; i < ARRAY_SIZE(rates_v1); ++i) - if (period > rates_v1[i].mult - || rates_v1[i].mult/period < rates_v1[i].mult%period) + for (i = 1; i < ARRAY_SIZE(rates); ++i) + if (period > _rates[i].mult + || _rates[i].mult/period < _rates[i].mult%period) break; - printf(" %u/%s", rates_v1[i-1].mult / period, rates_v1[i-1].name); + printf(" %lu/%s", _rates[i-1].mult / period, _rates[i-1].name); /* return in msec */ - return rates_v1[i-1].mult / XT_HASHLIMIT_SCALE_v1 * 1000; + return _rates[i-1].mult / scale * 1000; } static const struct { @@ -462,7 +646,7 @@ static const struct { { "", 1 }, }; -static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix) +static uint32_t print_bytes(uint64_t avg, uint64_t burst, const char *prefix) { unsigned int i; unsigned long long r; @@ -523,7 +707,7 @@ static void hashlimit_print(const void *ip, uint32_t quantum; fputs(" limit: avg", stdout); - quantum = print_rate(r->cfg.avg); + quantum = print_rate(r->cfg.avg, 1); printf(" burst %u", r->cfg.burst); fputs(" mode", stdout); print_mode(r->cfg.mode, '-'); @@ -538,39 +722,39 @@ static void hashlimit_print(const void *ip, } static void -hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) +hashlimit_mt_print(const struct hashlimit_cfg2 *cfg, unsigned int dmask, int revision) { uint32_t quantum; - if (info->cfg.mode & XT_HASHLIMIT_INVERT) + if (cfg->mode & XT_HASHLIMIT_INVERT) fputs(" limit: above", stdout); else fputs(" limit: up to", stdout); - if (info->cfg.mode & XT_HASHLIMIT_BYTES) { - quantum = print_bytes(info->cfg.avg, info->cfg.burst, ""); + if (cfg->mode & XT_HASHLIMIT_BYTES) { + quantum = print_bytes(cfg->avg, cfg->burst, ""); } else { - quantum = print_rate(info->cfg.avg); - printf(" burst %u", info->cfg.burst); + quantum = print_rate(cfg->avg, revision); + printf(" burst %llu", cfg->burst); } - if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | + if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { fputs(" mode", stdout); - print_mode(info->cfg.mode, '-'); + print_mode(cfg->mode, '-'); } - if (info->cfg.size != 0) - printf(" htable-size %u", info->cfg.size); - if (info->cfg.max != 0) - printf(" htable-max %u", info->cfg.max); - if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) - printf(" htable-gcinterval %u", info->cfg.gc_interval); - if (info->cfg.expire != quantum) - printf(" htable-expire %u", info->cfg.expire); - - if (info->cfg.srcmask != dmask) - printf(" srcmask %u", info->cfg.srcmask); - if (info->cfg.dstmask != dmask) - printf(" dstmask %u", info->cfg.dstmask); + if (cfg->size != 0) + printf(" htable-size %u", cfg->size); + if (cfg->max != 0) + printf(" htable-max %u", cfg->max); + if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf(" htable-gcinterval %u", cfg->gc_interval); + if (cfg->expire != quantum) + printf(" htable-expire %u", cfg->expire); + + if (cfg->srcmask != dmask) + printf(" srcmask %u", cfg->srcmask); + if (cfg->dstmask != dmask) + printf(" dstmask %u", cfg->dstmask); } static void @@ -578,8 +762,15 @@ hashlimit_mt4_print_v1(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + struct hashlimit_cfg2 cfg; + int ret; + + ret = cfg_copy(&cfg, (const void *)&info->cfg, 1); - hashlimit_mt_print(info, 32); + if (ret) + xtables_error(OTHER_PROBLEM, "unknown revision"); + + hashlimit_mt_print(&cfg, 32, 1); } static void @@ -587,8 +778,33 @@ hashlimit_mt6_print_v1(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + struct hashlimit_cfg2 cfg; + int ret; + + ret = cfg_copy(&cfg, (const void *)&info->cfg, 1); - hashlimit_mt_print(info, 128); + if (ret) + xtables_error(OTHER_PROBLEM, "unknown revision"); + + hashlimit_mt_print(&cfg, 128, 1); +} + +static void +hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data; + + hashlimit_mt_print(&info->cfg, 32, 2); +} + +static void +hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data; + + hashlimit_mt_print(&info->cfg, 128, 2); } static void hashlimit_save(const void *ip, const struct xt_entry_match *match) @@ -597,7 +813,7 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match) uint32_t quantum; fputs(" --hashlimit", stdout); - quantum = print_rate(r->cfg.avg); + quantum = print_rate(r->cfg.avg, 1); printf(" --hashlimit-burst %u", r->cfg.burst); fputs(" --hashlimit-mode", stdout); @@ -616,59 +832,89 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match) } static void -hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) +hashlimit_mt_save(const struct hashlimit_cfg2 *cfg, const char* name, unsigned int dmask, int revision) { uint32_t quantum; - if (info->cfg.mode & XT_HASHLIMIT_INVERT) + if (cfg->mode & XT_HASHLIMIT_INVERT) fputs(" --hashlimit-above", stdout); else fputs(" --hashlimit-upto", stdout); - if (info->cfg.mode & XT_HASHLIMIT_BYTES) { - quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-"); + if (cfg->mode & XT_HASHLIMIT_BYTES) { + quantum = print_bytes(cfg->avg, cfg->burst, "--hashlimit-"); } else { - quantum = print_rate(info->cfg.avg); - printf(" --hashlimit-burst %u", info->cfg.burst); + quantum = print_rate(cfg->avg, revision); + printf(" --hashlimit-burst %llu", cfg->burst); } - if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | + if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { fputs(" --hashlimit-mode", stdout); - print_mode(info->cfg.mode, ','); + print_mode(cfg->mode, ','); } - printf(" --hashlimit-name %s", info->name); - - if (info->cfg.size != 0) - printf(" --hashlimit-htable-size %u", info->cfg.size); - if (info->cfg.max != 0) - printf(" --hashlimit-htable-max %u", info->cfg.max); - if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) - printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval); - if (info->cfg.expire != quantum) - printf(" --hashlimit-htable-expire %u", info->cfg.expire); - - if (info->cfg.srcmask != dmask) - printf(" --hashlimit-srcmask %u", info->cfg.srcmask); - if (info->cfg.dstmask != dmask) - printf(" --hashlimit-dstmask %u", info->cfg.dstmask); + printf(" --hashlimit-name %s", name); + + if (cfg->size != 0) + printf(" --hashlimit-htable-size %u", cfg->size); + if (cfg->max != 0) + printf(" --hashlimit-htable-max %u", cfg->max); + if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf(" --hashlimit-htable-gcinterval %u", cfg->gc_interval); + if (cfg->expire != quantum) + printf(" --hashlimit-htable-expire %u", cfg->expire); + + if (cfg->srcmask != dmask) + printf(" --hashlimit-srcmask %u", cfg->srcmask); + if (cfg->dstmask != dmask) + printf(" --hashlimit-dstmask %u", cfg->dstmask); } static void hashlimit_mt4_save_v1(const void *ip, const struct xt_entry_match *match) { const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + struct hashlimit_cfg2 cfg; + int ret; + + ret = cfg_copy(&cfg, (const void *)&info->cfg, 1); - hashlimit_mt_save(info, 32); + if (ret) + xtables_error(OTHER_PROBLEM, "unknown revision"); + + hashlimit_mt_save(&cfg, info->name, 32, 1); } static void hashlimit_mt6_save_v1(const void *ip, const struct xt_entry_match *match) { const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + struct hashlimit_cfg2 cfg; + int ret; + + ret = cfg_copy(&cfg, (const void *)&info->cfg, 1); + + if (ret) + xtables_error(OTHER_PROBLEM, "unknown revision"); + + hashlimit_mt_save(&cfg, info->name, 128, 1); +} - hashlimit_mt_save(info, 128); +static void +hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data; + + hashlimit_mt_save(&info->cfg, info->name, 32, 2); +} + +static void +hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data; + + hashlimit_mt_save(&info->cfg, info->name, 128, 2); } static struct xtables_match hashlimit_mt_reg[] = { @@ -720,6 +966,38 @@ static struct xtables_match hashlimit_mt_reg[] = { .x6_options = hashlimit_mt_opts_v1, .udata_size = sizeof(struct hashlimit_mt_udata), }, + { + .version = XTABLES_VERSION, + .name = "hashlimit", + .revision = 2, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)), + .userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), + .help = hashlimit_mt_help, + .init = hashlimit_mt4_init, + .x6_parse = hashlimit_mt_parse, + .x6_fcheck = hashlimit_mt_check, + .print = hashlimit_mt4_print, + .save = hashlimit_mt4_save, + .x6_options = hashlimit_mt_opts, + .udata_size = sizeof(struct hashlimit_mt_udata), + }, + { + .version = XTABLES_VERSION, + .name = "hashlimit", + .revision = 2, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)), + .userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), + .help = hashlimit_mt_help, + .init = hashlimit_mt6_init, + .x6_parse = hashlimit_mt_parse, + .x6_fcheck = hashlimit_mt_check, + .print = hashlimit_mt6_print, + .save = hashlimit_mt6_save, + .x6_options = hashlimit_mt_opts, + .udata_size = sizeof(struct hashlimit_mt_udata), + }, }; void _init(void) diff --git a/extensions/libxt_hashlimit.t b/extensions/libxt_hashlimit.t index 59d6613..d27c861 100644 --- a/extensions/libxt_hashlimit.t +++ b/extensions/libxt_hashlimit.t @@ -1,10 +1,12 @@ :INPUT,FORWARD,OUTPUT -m hashlimit --hashlimit-above 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK +-m hashlimit --hashlimit-above 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK -m hashlimit --hashlimit-above 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK -m hashlimit --hashlimit-above 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK # kernel says "xt_hashlimit: overflow, try lower: 864000000/5" -m hashlimit --hashlimit-above 1/day --hashlimit-burst 5 --hashlimit-name mini1;;FAIL -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK +-m hashlimit --hashlimit-upto 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK -m hashlimit --hashlimit-upto 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK -m hashlimit --hashlimit-upto 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK # kernel says "xt_hashlimit: overflow, try lower: 864000000/5" diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h index 0af5477..e493fc1 100644 --- a/include/linux/netfilter/xt_hashlimit.h +++ b/include/linux/netfilter/xt_hashlimit.h @@ -5,6 +5,7 @@ /* timings are in milliseconds. */ #define XT_HASHLIMIT_SCALE_v1 10000 +#define XT_HASHLIMIT_SCALE 1000000llu /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 seconds, or one packet every 59 hours. */ @@ -61,6 +62,20 @@ struct hashlimit_cfg1 { __u8 srcmask, dstmask; }; +struct hashlimit_cfg2 { + __u32 mode; /* bitmask of XT_HASHLIMIT_HASH_* */ + __u64 avg; /* Average secs between packets * scale */ + __u64 burst; /* Period multiplier for upper limit. */ + + /* user specified */ + __u32 size; /* how many buckets */ + __u32 max; /* max number of entries */ + __u32 gc_interval; /* gc interval */ + __u32 expire; /* when do entries expire? */ + + __u8 srcmask, dstmask; +}; + struct xt_hashlimit_mtinfo1 { char name[IFNAMSIZ]; struct hashlimit_cfg1 cfg; @@ -69,4 +84,12 @@ struct xt_hashlimit_mtinfo1 { struct xt_hashlimit_htable *hinfo __attribute__((aligned(8))); }; +struct xt_hashlimit_mtinfo2 { + char name[NAME_MAX]; + struct hashlimit_cfg2 cfg; + + /* Used internally by the kernel */ + struct xt_hashlimit_htable *hinfo __attribute__((aligned(8))); +}; + #endif /*_XT_HASHLIMIT_H*/