summaryrefslogtreecommitdiffstats
path: root/iptables/nft.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.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.c')
-rw-r--r--iptables/nft.c53
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;
}