diff options
author | Phil Sutter <phil@nwl.cc> | 2018-08-10 17:07:35 +0200 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-08-16 19:43:47 +0200 |
commit | 5de8dcf75941c533f2dae8a40bf8b6128b8287f3 (patch) | |
tree | 3d793f5755ce0af3ee3b2f43e82b36dd364324a6 /iptables/nft.c | |
parent | 514de4801b731db471298f4508f9534bcefec006 (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.c')
-rw-r--r-- | iptables/nft.c | 53 |
1 files changed, 49 insertions, 4 deletions
diff --git a/iptables/nft.c b/iptables/nft.c index 120039ed..8c0746dd 100644 --- a/iptables/nft.c +++ b/iptables/nft.c @@ -38,6 +38,8 @@ #include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables_compat.h> +#include <linux/netfilter/xt_limit.h> + #include <libmnl/libmnl.h> #include <libnftnl/table.h> #include <libnftnl/chain.h> @@ -896,11 +898,50 @@ static int __add_match(struct nftnl_expr *e, struct xt_entry_match *m) return 0; } +static int add_nft_limit(struct nftnl_rule *r, struct xt_entry_match *m) +{ + struct xt_rateinfo *rinfo = (void *)m->data; + static const uint32_t mult[] = { + XT_LIMIT_SCALE*24*60*60, /* day */ + XT_LIMIT_SCALE*60*60, /* hour */ + XT_LIMIT_SCALE*60, /* min */ + XT_LIMIT_SCALE, /* sec */ + }; + struct nftnl_expr *expr; + int i; + + expr = nftnl_expr_alloc("limit"); + if (!expr) + return -ENOMEM; + + for (i = 1; i < ARRAY_SIZE(mult); i++) { + if (rinfo->avg > mult[i] || + mult[i] / rinfo->avg < mult[i] % rinfo->avg) + break; + } + + nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_TYPE, NFT_LIMIT_PKTS); + nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_FLAGS, 0); + + nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_RATE, + mult[i - 1] / rinfo->avg); + nftnl_expr_set_u64(expr, NFTNL_EXPR_LIMIT_UNIT, + mult[i - 1] / XT_LIMIT_SCALE); + + nftnl_expr_set_u32(expr, NFTNL_EXPR_LIMIT_BURST, rinfo->burst); + + nftnl_rule_add_expr(r, expr); + return 0; +} + int add_match(struct nftnl_rule *r, struct xt_entry_match *m) { struct nftnl_expr *expr; int ret; + if (!strcmp(m->u.user.name, "limit")) + return add_nft_limit(r, m); + expr = nftnl_expr_alloc("match"); if (expr == NULL) return -ENOMEM; @@ -2996,8 +3037,9 @@ static const char *supported_exprs[NFT_COMPAT_EXPR_MAX] = { }; -static int nft_is_expr_compatible(const char *name) +static int nft_is_expr_compatible(const struct nftnl_expr *expr) { + const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); int i; for (i = 0; i < NFT_COMPAT_EXPR_MAX; i++) { @@ -3005,6 +3047,11 @@ static int nft_is_expr_compatible(const char *name) return 0; } + if (!strcmp(name, "limit") && + nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_TYPE) == NFT_LIMIT_PKTS && + nftnl_expr_get_u32(expr, NFTNL_EXPR_LIMIT_FLAGS) == 0) + return 0; + return 1; } @@ -3020,9 +3067,7 @@ static bool nft_is_rule_compatible(struct nftnl_rule *rule) expr = nftnl_expr_iter_next(iter); while (expr != NULL) { - const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); - - if (nft_is_expr_compatible(name) == 0) { + if (nft_is_expr_compatible(expr) == 0) { expr = nftnl_expr_iter_next(iter); continue; } |