diff options
-rw-r--r-- | iptables/nft-shared.c | 46 | ||||
-rw-r--r-- | iptables/nft.c | 53 |
2 files changed, 95 insertions, 4 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); } 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; } |