summaryrefslogtreecommitdiffstats
path: root/iptables/nft-shared.c
diff options
context:
space:
mode:
authorPhil Sutter <phil@nwl.cc>2018-08-10 17:07:35 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2018-08-16 19:43:47 +0200
commit5de8dcf75941c533f2dae8a40bf8b6128b8287f3 (patch)
tree3d793f5755ce0af3ee3b2f43e82b36dd364324a6 /iptables/nft-shared.c
parent514de4801b731db471298f4508f9534bcefec006 (diff)
xtables: Use native nftables limit expression
The original issue was that for a rule with limit match added by ebtables-nft, the kernel might attempt to use xt_limit instead of ebt_limit (and fail due to that). This happens if xt_limit.ko is loaded but ebt_limit.ko is not, because the kernel prefers the family-independent variants. There are multiple ways to avoid above issue, but using neither xt_limit nor ebt_limit with nft-variants should be the most effective one. Therefore translate a created limit match in userspace into native nftables code before sending it to kernel and do the reverse translation when listing rules. Apart from the translation routines, this requires slight adjustment of nft_is_expr_compatible() since neither xt_limit nor ebt_limit support byte-based limits or inverted limit match. Signed-off-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'iptables/nft-shared.c')
-rw-r--r--iptables/nft-shared.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c
index 5b55c7c0..871037d2 100644
--- a/iptables/nft-shared.c
+++ b/iptables/nft-shared.c
@@ -22,6 +22,7 @@
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/xt_comment.h>
+#include <linux/netfilter/xt_limit.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
@@ -549,6 +550,49 @@ void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
ops->parse_immediate(jumpto, nft_goto, data);
}
+static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
+{
+ __u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
+ __u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
+ __u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
+ struct xtables_rule_match **matches;
+ struct xtables_match *match;
+ struct nft_family_ops *ops;
+ struct xt_rateinfo *rinfo;
+ size_t size;
+
+ switch (ctx->family) {
+ case NFPROTO_IPV4:
+ case NFPROTO_IPV6:
+ case NFPROTO_BRIDGE:
+ matches = &ctx->cs->matches;
+ break;
+ default:
+ fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
+ ctx->family);
+ exit(EXIT_FAILURE);
+ }
+
+ match = xtables_find_match("limit", XTF_TRY_LOAD, matches);
+ if (match == NULL)
+ return;
+
+ size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
+ match->m = xtables_calloc(1, size);
+ match->m->u.match_size = size;
+ strcpy(match->m->u.user.name, match->name);
+ match->m->u.user.revision = match->revision;
+ xs_init_match(match);
+
+ rinfo = (void *)match->m->data;
+ rinfo->avg = XT_LIMIT_SCALE * unit / rate;
+ rinfo->burst = burst;
+
+ ops = nft_family_ops_lookup(ctx->family);
+ if (ops->parse_match != NULL)
+ ops->parse_match(match, ctx->cs);
+}
+
void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
struct iptables_command_state *cs)
{
@@ -586,6 +630,8 @@ void nft_rule_to_iptables_command_state(const struct nftnl_rule *r,
nft_parse_match(&ctx, expr);
else if (strcmp(name, "target") == 0)
nft_parse_target(&ctx, expr);
+ else if (strcmp(name, "limit") == 0)
+ nft_parse_limit(&ctx, expr);
expr = nftnl_expr_iter_next(iter);
}