]> git.ipfire.org Git - thirdparty/iptables.git/commitdiff
extensions: libxt_hashlimit: Add translation to nft
authorElise Lennion <elise.lennion@gmail.com>
Mon, 27 Feb 2017 17:43:08 +0000 (14:43 -0300)
committerPablo Neira Ayuso <pablo@netfilter.org>
Tue, 28 Feb 2017 11:20:09 +0000 (12:20 +0100)
Hashlimit has similar functionality to flow tables in nftables. Some
usage examples are:

$ iptables-translate -A OUTPUT -m tcp -p tcp --dport 443 -m hashlimit \
--hashlimit-above 20kb/s --hashlimit-burst 1mb --hashlimit-mode dstip \
--hashlimit-name https --hashlimit-dstmask 24 -m state --state NEW \
-j DROP

nft add rule ip filter OUTPUT tcp dport 443 flow table https { ip \
daddr and 255.255.255.0 timeout 60s limit rate over 20 kbytes/second \
burst 1 mbytes} ct state new  counter drop

$ iptables-translate -A OUTPUT -m tcp -p tcp --dport 443 -m hashlimit \
--hashlimit-upto 300 --hashlimit-burst 15 --hashlimit-mode \
srcip,dstip --hashlimit-name https --hashlimit-htable-expire 300000 \
-m state --state NEW -j DROP

nft add rule ip filter OUTPUT tcp dport 443 flow table https { ip \
daddr . ip saddr timeout 300s limit rate 300/second burst 15 packets} \
ct state new  counter drop

The translation isn't supported when --hashlimit-mode isn't specified.
Also, the following options don't apply to flow tables:

--hashlimit-htable-size
--hashlimit-htable-max
--hashlimit-htable-gcinterval

Signed-off-by: Elise Lennion <elise.lennion@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
extensions/libxt_hashlimit.c

index 7a1b37a9898c9f2f5c92ceede3b74394c79ea41a..52fc4fa17e8cc3e457690f89b16c5731c1cfb895 100644 (file)
@@ -916,6 +916,225 @@ hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
        hashlimit_mt_save(&info->cfg, info->name, 128, 2);
 }
 
+static const struct rates rates_v1_xlate[] = {
+       { "day", XT_HASHLIMIT_SCALE * 24 * 60 * 60 },
+       { "hour", XT_HASHLIMIT_SCALE * 60 * 60 },
+       { "minute", XT_HASHLIMIT_SCALE * 60 },
+       { "second", XT_HASHLIMIT_SCALE } };
+
+static const struct rates rates_xlate[] = {
+       { "day", XT_HASHLIMIT_SCALE_v2 * 24 * 60 * 60 },
+       { "hour", XT_HASHLIMIT_SCALE_v2 * 60 * 60 },
+       { "minute", XT_HASHLIMIT_SCALE_v2 * 60 },
+       { "second", XT_HASHLIMIT_SCALE_v2 } };
+
+static void print_packets_rate_xlate(struct xt_xlate *xl, uint64_t avg,
+                                    uint64_t burst, int revision)
+{
+       unsigned int i;
+       const struct rates *_rates = (revision == 1) ?
+               rates_v1_xlate : rates_xlate;
+
+       for (i = 1; i < ARRAY_SIZE(rates); ++i)
+               if (avg > _rates[i].mult ||
+                   _rates[i].mult / avg < _rates[i].mult % avg)
+                       break;
+
+       xt_xlate_add(xl, " %llu/%s burst %lu packets",
+                    _rates[i-1].mult / avg, _rates[i-1].name, burst);
+}
+
+static void print_bytes_rate_xlate(struct xt_xlate *xl,
+                                  const struct hashlimit_cfg2 *cfg)
+{
+       unsigned int i;
+       unsigned long long r;
+
+       r = cost_to_bytes(cfg->avg);
+
+       for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+               if (r >= units[i].thresh &&
+                   bytes_to_cost(r & ~(units[i].thresh - 1)) == cfg->avg)
+                       break;
+
+       xt_xlate_add(xl, " %llu %sbytes/second", r / units[i].thresh,
+                    units[i].name);
+
+       r *= cfg->burst;
+       for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
+               if (r >= units[i].thresh)
+                       break;
+
+       if (cfg->burst > 0)
+               xt_xlate_add(xl, " burst %llu %sbytes", r / units[i].thresh,
+                            units[i].name);
+}
+
+static void hashlimit_print_subnet_xlate(struct xt_xlate *xl,
+                                        uint32_t nsub, int family)
+{
+       char sep = (family == NFPROTO_IPV4) ? '.' : ':';
+       char *fmt = (family == NFPROTO_IPV4) ? "%u" : "%04x";
+       unsigned int nblocks = (family == NFPROTO_IPV4) ? 4 : 8;
+       unsigned int nbits = (family == NFPROTO_IPV4) ? 8 : 16;
+       unsigned int acm, i;
+
+       xt_xlate_add(xl, " and ");
+       while (nblocks--) {
+               acm = 0;
+
+               for (i = 0; i < nbits; i++) {
+                       acm <<= 1;
+
+                       if (nsub > 0) {
+                               acm++;
+                               nsub--;
+                       }
+               }
+
+               xt_xlate_add(xl, fmt, acm);
+               if (nblocks > 0)
+                       xt_xlate_add(xl, "%c", sep);
+       }
+}
+
+static const char *const hashlimit_modes4_xlate[] = {
+       [XT_HASHLIMIT_HASH_DIP] = "ip daddr",
+       [XT_HASHLIMIT_HASH_DPT] = "tcp dport",
+       [XT_HASHLIMIT_HASH_SIP] = "ip saddr",
+       [XT_HASHLIMIT_HASH_SPT] = "tcp sport",
+};
+
+static const char *const hashlimit_modes6_xlate[] = {
+       [XT_HASHLIMIT_HASH_DIP] = "ip6 daddr",
+       [XT_HASHLIMIT_HASH_DPT] = "tcp dport",
+       [XT_HASHLIMIT_HASH_SIP] = "ip6 saddr",
+       [XT_HASHLIMIT_HASH_SPT] = "tcp sport",
+};
+
+static int hashlimit_mode_xlate(struct xt_xlate *xl,
+                               uint32_t mode, int family,
+                               unsigned int nsrc, unsigned int ndst)
+{
+       const char * const *_modes = (family == NFPROTO_IPV4) ?
+               hashlimit_modes4_xlate : hashlimit_modes6_xlate;
+       bool prevopt = false;
+       unsigned int mask;
+
+       mode &= ~XT_HASHLIMIT_INVERT & ~XT_HASHLIMIT_BYTES;
+
+       for (mask = 1; mode > 0; mask <<= 1) {
+               if (!(mode & mask))
+                       continue;
+
+               if (!prevopt) {
+                       xt_xlate_add(xl, " ");
+                       prevopt = true;
+               }
+               else {
+                       xt_xlate_add(xl, " . ");
+               }
+
+               xt_xlate_add(xl, "%s", _modes[mask]);
+
+               if (mask == XT_HASHLIMIT_HASH_DIP &&
+                   ((family == NFPROTO_IPV4 && ndst != 32) ||
+                    (family == NFPROTO_IPV6 && ndst != 128)))
+                       hashlimit_print_subnet_xlate(xl, ndst, family);
+               else if (mask == XT_HASHLIMIT_HASH_SIP &&
+                        ((family == NFPROTO_IPV4 && nsrc != 32) ||
+                         (family == NFPROTO_IPV6 && nsrc != 128)))
+                       hashlimit_print_subnet_xlate(xl, nsrc, family);
+
+               mode &= ~mask;
+       }
+
+       return prevopt;
+}
+
+static int hashlimit_mt_xlate(struct xt_xlate *xl, const char *name,
+                             const struct hashlimit_cfg2 *cfg,
+                             int revision, int family)
+{
+       int ret = 1;
+
+       xt_xlate_add(xl, "flow table %s {", name);
+       ret = hashlimit_mode_xlate(xl, cfg->mode, family,
+                                  cfg->srcmask, cfg->dstmask);
+       xt_xlate_add(xl, " timeout %us limit rate", cfg->expire / 1000);
+
+       if (cfg->mode & XT_HASHLIMIT_INVERT)
+               xt_xlate_add(xl, " over");
+
+       if (cfg->mode & XT_HASHLIMIT_BYTES)
+               print_bytes_rate_xlate(xl, cfg);
+       else
+               print_packets_rate_xlate(xl, cfg->avg, cfg->burst, revision);
+
+       xt_xlate_add(xl, "}");
+
+       return ret;
+}
+
+static int hashlimit_xlate(struct xt_xlate *xl,
+                          const struct xt_xlate_mt_params *params)
+{
+       const struct xt_hashlimit_info *info = (const void *)params->match->data;
+       int ret = 1;
+
+       xt_xlate_add(xl, "flow table %s {", info->name);
+       ret = hashlimit_mode_xlate(xl, info->cfg.mode, NFPROTO_IPV4, 32, 32);
+       xt_xlate_add(xl, " timeout %us limit rate", info->cfg.expire / 1000);
+       print_packets_rate_xlate(xl, info->cfg.avg, info->cfg.burst, 1);
+       xt_xlate_add(xl, "}");
+
+       return ret;
+}
+
+static int hashlimit_mt4_xlate_v1(struct xt_xlate *xl,
+                                 const struct xt_xlate_mt_params *params)
+{
+       const struct xt_hashlimit_mtinfo1 *info =
+               (const void *)params->match->data;
+       struct hashlimit_cfg2 cfg;
+
+       if (cfg_copy(&cfg, (const void *)&info->cfg, 1))
+               xtables_error(OTHER_PROBLEM, "unknown revision");
+
+       return hashlimit_mt_xlate(xl, info->name, &cfg, 1, NFPROTO_IPV4);
+}
+
+static int hashlimit_mt6_xlate_v1(struct xt_xlate *xl,
+                                 const struct xt_xlate_mt_params *params)
+{
+       const struct xt_hashlimit_mtinfo1 *info =
+               (const void *)params->match->data;
+       struct hashlimit_cfg2 cfg;
+
+       if (cfg_copy(&cfg, (const void *)&info->cfg, 1))
+               xtables_error(OTHER_PROBLEM, "unknown revision");
+
+       return hashlimit_mt_xlate(xl, info->name, &cfg, 1, NFPROTO_IPV6);
+}
+
+static int hashlimit_mt4_xlate(struct xt_xlate *xl,
+                              const struct xt_xlate_mt_params *params)
+{
+       const struct xt_hashlimit_mtinfo2 *info =
+               (const void *)params->match->data;
+
+       return hashlimit_mt_xlate(xl, info->name, &info->cfg, 2, NFPROTO_IPV4);
+}
+
+static int hashlimit_mt6_xlate(struct xt_xlate *xl,
+                              const struct xt_xlate_mt_params *params)
+{
+       const struct xt_hashlimit_mtinfo2 *info =
+               (const void *)params->match->data;
+
+       return hashlimit_mt_xlate(xl, info->name, &info->cfg, 2, NFPROTO_IPV6);
+}
+
 static struct xtables_match hashlimit_mt_reg[] = {
        {
                .family        = NFPROTO_UNSPEC,
@@ -932,6 +1151,7 @@ static struct xtables_match hashlimit_mt_reg[] = {
                .save          = hashlimit_save,
                .x6_options    = hashlimit_opts,
                .udata_size    = sizeof(struct hashlimit_mt_udata),
+               .xlate         = hashlimit_xlate,
        },
        {
                .version       = XTABLES_VERSION,
@@ -948,6 +1168,7 @@ static struct xtables_match hashlimit_mt_reg[] = {
                .save          = hashlimit_mt4_save_v1,
                .x6_options    = hashlimit_mt_opts_v1,
                .udata_size    = sizeof(struct hashlimit_mt_udata),
+               .xlate         = hashlimit_mt4_xlate_v1,
        },
        {
                .version       = XTABLES_VERSION,
@@ -964,6 +1185,7 @@ static struct xtables_match hashlimit_mt_reg[] = {
                .save          = hashlimit_mt6_save_v1,
                .x6_options    = hashlimit_mt_opts_v1,
                .udata_size    = sizeof(struct hashlimit_mt_udata),
+               .xlate         = hashlimit_mt6_xlate_v1,
        },
        {
                .version       = XTABLES_VERSION,
@@ -980,6 +1202,7 @@ static struct xtables_match hashlimit_mt_reg[] = {
                .save          = hashlimit_mt4_save,
                .x6_options    = hashlimit_mt_opts,
                .udata_size    = sizeof(struct hashlimit_mt_udata),
+               .xlate         = hashlimit_mt4_xlate,
        },
        {
                .version       = XTABLES_VERSION,
@@ -996,6 +1219,7 @@ static struct xtables_match hashlimit_mt_reg[] = {
                .save          = hashlimit_mt6_save,
                .x6_options    = hashlimit_mt_opts,
                .udata_size    = sizeof(struct hashlimit_mt_udata),
+               .xlate         = hashlimit_mt6_xlate,
        },
 };