summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElise Lennion <elise.lennion@gmail.com>2017-02-27 14:43:08 -0300
committerPablo Neira Ayuso <pablo@netfilter.org>2017-02-28 12:20:09 +0100
commiteb66632d219a45497e7348918984e8cea6acdca2 (patch)
tree77946abdc998c40fc8f765a33805af848ed1b5ec
parent72bb3dbf0ecdf3ec96aee80e5d152c8be4394da1 (diff)
extensions: libxt_hashlimit: Add translation to nft
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>
-rw-r--r--extensions/libxt_hashlimit.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index 7a1b37a9..52fc4fa1 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -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,
},
};