diff --git a/src/bfcli/lexer.l b/src/bfcli/lexer.l index 7fa9c77b..40e3f505 100644 --- a/src/bfcli/lexer.l +++ b/src/bfcli/lexer.l @@ -22,6 +22,7 @@ %s STATE_HOOK_OPTS %s STATE_LOG_OPTS +%s STATE_RATELIMIT_OPTS %s STATE_MARK_OPTS %s STATE_MATCHER_SET %s STATE_MATCHER_META_IFACE @@ -85,6 +86,17 @@ log { BEGIN(STATE_LOG_OPTS); return LOG; } } } + /* Rate limit */ +ratelimit { BEGIN(STATE_RATELIMIT_OPTS); return RATELIMIT; } +{ + /* -?[0-9]+(\.[0-9]+)*\/[a-zA-z]+ { */ /* ex: -13.99/abc */ + -?[0-9]+\/[a-zA-z]+ { /* ex: 13/abcd */ + BEGIN(INITIAL); + yylval.sval = strdup(yytext); + return RATELIMIT_VAL; + } +} + /* Sets */ \([ \t\n\r\f\v]*([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)([ \t\n\r\f\v]*,[ \t\n\r\f\v]*([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+))*[ \t\n\r\f\v]*\) { BEGIN(STATE_MATCHER_SET); diff --git a/src/bfcli/parser.y b/src/bfcli/parser.y index d6e93c1a..e8281099 100644 --- a/src/bfcli/parser.y +++ b/src/bfcli/parser.y @@ -55,9 +55,10 @@ }) enum bf_rule_option_flag { - BF_RULE_OPTION_LOG = 1 << 0, - BF_RULE_OPTION_COUNTER = 1 << 1, - BF_RULE_OPTION_MARK = 1 << 2, + BF_RULE_OPTION_LOG = 1 << 0, + BF_RULE_OPTION_COUNTER = 1 << 1, + BF_RULE_OPTION_MARK = 1 << 2, + BF_RULE_OPTION_RATELIMIT = 1 << 3, }; struct bf_rule_options { @@ -65,6 +66,7 @@ uint8_t log; bool counter; + uint32_t ratelimit; uint32_t mark; }; } @@ -93,8 +95,9 @@ %token CHAIN %token RULE %token SET -%token LOG COUNTER MARK +%token LOG COUNTER RATELIMIT MARK %token LOG_HEADERS +%token RATELIMIT_VAL %token SET_TYPE %token SET_RAW_PAYLOAD %token STRING @@ -253,6 +256,7 @@ rule : RULE matchers rule_options verdict bf_parse_err("failed to create a new bf_rule\n"); rule->log = $3.flags & BF_RULE_OPTION_LOG ? $3.log : 0; + rule->ratelimit = $3.flags & BF_RULE_OPTION_RATELIMIT ? $3.ratelimit : 0; rule->counters = $3.flags & BF_RULE_OPTION_COUNTER ? $3.counter : false; if ($3.flags & BF_RULE_OPTION_MARK) @@ -305,6 +309,30 @@ rule_option : LOG LOG_HEADERS .flags = BF_RULE_OPTION_COUNTER, }; } + | RATELIMIT RATELIMIT_VAL + { + _cleanup_free_ char *in = $2; + char *tmp = in; + char *saveptr; + + if (tmp[0] == '-') + bf_parse_err("ratelimit should be positive"); + + errno = 0; + uint32_t limit = strtoul(strtok_r(tmp, "/", &saveptr), NULL, 0); + if (errno != 0) + bf_parse_err("ratelimit value is too large"); + + $$ = (struct bf_rule_options){ + .ratelimit = limit, + .flags = BF_RULE_OPTION_RATELIMIT, + }; + + $$ = (struct bf_rule_options){ + .ratelimit = limit, + .flags = BF_RULE_OPTION_RATELIMIT, + }; + } | MARK STRING { _cleanup_free_ const char *raw_mark = $2; @@ -341,6 +369,13 @@ rule_options : %empty { $$ = (struct bf_rule_options){}; } $1.counter = $2.counter; } + if ($2.flags & BF_RULE_OPTION_RATELIMIT) { + if ($1.flags & BF_RULE_OPTION_RATELIMIT) + bf_parse_err("duplicate keyword \"ratelimit\" in rule"); + $1.flags |= BF_RULE_OPTION_RATELIMIT; + $1.ratelimit = $2.ratelimit; + } + if ($2.flags & BF_RULE_OPTION_MARK) { if ($1.flags & BF_RULE_OPTION_MARK) bf_parse_err("duplicate keyword \"mark\" in rule"); diff --git a/src/bpfilter/CMakeLists.txt b/src/bpfilter/CMakeLists.txt index 610605f8..1aaffb5e 100644 --- a/src/bpfilter/CMakeLists.txt +++ b/src/bpfilter/CMakeLists.txt @@ -62,6 +62,7 @@ bf_target_add_elfstubs(bpfilter "parse_ipv6_nh" "update_counters" "log" + "ratelimit" ) target_compile_definitions(bpfilter diff --git a/src/bpfilter/bpf/ratelimit.bpf.c b/src/bpfilter/bpf/ratelimit.bpf.c new file mode 100644 index 00000000..7695a56c --- /dev/null +++ b/src/bpfilter/bpf/ratelimit.bpf.c @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +#define BF_TIME_S 1000000000 + +struct bf_ratelimit +{ + __u64 current; + __u64 last_time; +}; + +__u8 bf_ratelimit(void *map, __u64 key, __u64 limit) +{ + struct bf_ratelimit *ratelimit; + __u64 current_time = bpf_ktime_get_ns() / BF_TIME_S; + + ratelimit = bpf_map_lookup_elem(map, &key); + if (!ratelimit) { + bpf_printk("failed to fetch the rule's ratelimit"); + return 1; + } + + if (current_time != ratelimit->last_time) { + ratelimit->current = 0; + } + + ratelimit->current++; + ratelimit->last_time = current_time; + + return (ratelimit->current > limit); +} diff --git a/src/bpfilter/cgen/elfstub.h b/src/bpfilter/cgen/elfstub.h index 348d510e..d82ff293 100644 --- a/src/bpfilter/cgen/elfstub.h +++ b/src/bpfilter/cgen/elfstub.h @@ -135,6 +135,9 @@ enum bf_elfstub_id */ BF_ELFSTUB_LOG, + // Return 0 on ACCEPT, 1 on DROP + BF_ELFSTUB_RATELIMIT, + _BF_ELFSTUB_MAX, }; diff --git a/src/bpfilter/cgen/fixup.c b/src/bpfilter/cgen/fixup.c index 9c980126..06d98a33 100644 --- a/src/bpfilter/cgen/fixup.c +++ b/src/bpfilter/cgen/fixup.c @@ -44,6 +44,7 @@ static const char *_bf_fixup_type_to_str(enum bf_fixup_type type) static const char *str[] = { [BF_FIXUP_TYPE_JMP_NEXT_RULE] = "BF_FIXUP_TYPE_JMP_NEXT_RULE", [BF_FIXUP_TYPE_COUNTERS_MAP_FD] = "BF_FIXUP_TYPE_COUNTERS_MAP_FD", + [BF_FIXUP_TYPE_RATELIMIT_MAP_FD] = "BF_FIXUP_TYPE_RATELIMIT_MAP_FD", [BF_FIXUP_TYPE_PRINTER_MAP_FD] = "BF_FIXUP_TYPE_PRINTER_MAP_FD", [BF_FIXUP_TYPE_SET_MAP_FD] = "BF_FIXUP_TYPE_SET_MAP_FD", [BF_FIXUP_ELFSTUB_CALL] = "BF_FIXUP_ELFSTUB_CALL", @@ -70,6 +71,7 @@ void bf_fixup_dump(const struct bf_fixup *fixup, prefix_t *prefix) switch (fixup->type) { case BF_FIXUP_TYPE_JMP_NEXT_RULE: case BF_FIXUP_TYPE_COUNTERS_MAP_FD: + case BF_FIXUP_TYPE_RATELIMIT_MAP_FD: case BF_FIXUP_TYPE_PRINTER_MAP_FD: // No specific value to dump break; diff --git a/src/bpfilter/cgen/fixup.h b/src/bpfilter/cgen/fixup.h index bf43a846..11babc75 100644 --- a/src/bpfilter/cgen/fixup.h +++ b/src/bpfilter/cgen/fixup.h @@ -32,6 +32,8 @@ enum bf_fixup_type BF_FIXUP_TYPE_JMP_NEXT_RULE, /// Set the counters map file descriptor in the @c BPF_LD_MAP_FD instruction. BF_FIXUP_TYPE_COUNTERS_MAP_FD, + /// Set the ratelimit map file descriptor in the @c BPF_LD_MAP_FD instruction. + BF_FIXUP_TYPE_RATELIMIT_MAP_FD, /// Set the printer map file descriptor in the @c BPF_LD_MAP_FD instruction. BF_FIXUP_TYPE_PRINTER_MAP_FD, /// Set the log map file descriptor in the @c BPF_LD_MAP_FD instruction. diff --git a/src/bpfilter/cgen/prog/map.c b/src/bpfilter/cgen/prog/map.c index bf48c30c..4109899d 100644 --- a/src/bpfilter/cgen/prog/map.c +++ b/src/bpfilter/cgen/prog/map.c @@ -61,6 +61,7 @@ int bf_map_new(struct bf_map **map, const char *name, enum bf_map_type type, { static enum bf_bpf_map_type _map_type_to_bpf[_BF_MAP_TYPE_MAX] = { [BF_MAP_TYPE_COUNTERS] = BF_BPF_MAP_TYPE_ARRAY, + [BF_MAP_TYPE_RATELIMIT] = BF_BPF_MAP_TYPE_ARRAY, [BF_MAP_TYPE_PRINTER] = BF_BPF_MAP_TYPE_ARRAY, [BF_MAP_TYPE_LOG] = BF_BPF_MAP_TYPE_RINGBUF, }; @@ -170,6 +171,7 @@ static const char *_bf_map_type_to_str(enum bf_map_type type) { static const char *type_strs[] = { [BF_MAP_TYPE_COUNTERS] = "BF_MAP_TYPE_COUNTERS", + [BF_MAP_TYPE_RATELIMIT] = "BF_MAP_TYPE_RATELIMIT", [BF_MAP_TYPE_PRINTER] = "BF_MAP_TYPE_PRINTER", [BF_MAP_TYPE_LOG] = "BF_MAP_TYPE_LOG", [BF_MAP_TYPE_SET] = "BF_MAP_TYPE_SET", @@ -308,6 +310,12 @@ static struct bf_btf *_bf_map_make_btf(const struct bf_map *map) btf__add_field(kbtf, "packets", 1, 0, 0); btf__add_field(kbtf, "bytes", 1, 64, 0); break; + case BF_MAP_TYPE_RATELIMIT: // I have no clue what I'm doing here + btf__add_int(kbtf, "u64", 8, 0); + btf->key_type_id = btf__add_int(kbtf, "u32", 4, 0); + btf->value_type_id = btf__add_struct(kbtf, "bf_", 16); + btf__add_field(kbtf, "limit", 1, 0, 0); + break; case BF_MAP_TYPE_PRINTER: case BF_MAP_TYPE_SET: case BF_MAP_TYPE_LOG: diff --git a/src/bpfilter/cgen/prog/map.h b/src/bpfilter/cgen/prog/map.h index 6e911780..b3f6ee54 100644 --- a/src/bpfilter/cgen/prog/map.h +++ b/src/bpfilter/cgen/prog/map.h @@ -18,6 +18,7 @@ enum bf_map_type { BF_MAP_TYPE_COUNTERS, + BF_MAP_TYPE_RATELIMIT, BF_MAP_TYPE_PRINTER, BF_MAP_TYPE_LOG, BF_MAP_TYPE_SET, diff --git a/src/bpfilter/cgen/program.c b/src/bpfilter/cgen/program.c index b6a9bd1e..968df600 100644 --- a/src/bpfilter/cgen/program.c +++ b/src/bpfilter/cgen/program.c @@ -36,8 +36,11 @@ #include #include +#include "bpf/bpf_helpers.h" +#include "bpfilter/ratelimit.h" #include "cgen/cgroup.h" #include "cgen/dump.h" +#include "cgen/elfstub.h" #include "cgen/fixup.h" #include "cgen/jmp.h" #include "cgen/matcher/icmp.h" @@ -117,6 +120,11 @@ int bf_program_new(struct bf_program **program, const struct bf_chain *chain) if (r < 0) return bf_err_r(r, "failed to create the counters bf_map object"); + r = bf_map_new(&_program->rmap, "ratelimit_map", BF_MAP_TYPE_RATELIMIT, + sizeof(uint32_t), sizeof(struct bf_ratelimit), 1); + if (r < 0) + return bf_err_r(r, "failed to create the ratelimit bf_map object"); + r = bf_map_new(&_program->pmap, "printer_map", BF_MAP_TYPE_PRINTER, sizeof(uint32_t), BF_MAP_VALUE_SIZE_UNKNOWN, 1); if (r < 0) @@ -185,6 +193,14 @@ int bf_program_new_from_pack(struct bf_program **program, if (r) return r; + bf_map_free(&_program->rmap); + r = bf_rpack_kv_obj(node, "rmap", &child); + if (r) + return bf_rpack_key_err(r, "bf_program.rmap"); + r = bf_map_new_from_pack(&_program->rmap, dir_fd, child); + if (r) + return r; + bf_map_free(&_program->pmap); r = bf_rpack_kv_obj(node, "pmap", &child); if (r) @@ -269,6 +285,7 @@ void bf_program_free(struct bf_program **program) closep(&(*program)->runtime.prog_fd); bf_map_free(&(*program)->cmap); + bf_map_free(&(*program)->rmap); bf_map_free(&(*program)->pmap); bf_map_free(&(*program)->lmap); bf_list_clean(&(*program)->sets); @@ -288,6 +305,10 @@ int bf_program_pack(const struct bf_program *program, bf_wpack_t *pack) bf_map_pack(program->cmap, pack); bf_wpack_close_object(pack); + bf_wpack_open_object(pack, "rmap"); + bf_map_pack(program->rmap, pack); + bf_wpack_close_object(pack); + bf_wpack_open_object(pack, "pmap"); bf_map_pack(program->pmap, pack); bf_wpack_close_object(pack); @@ -328,6 +349,11 @@ void bf_program_dump(const struct bf_program *program, prefix_t *prefix) bf_map_dump(program->cmap, bf_dump_prefix_last(prefix)); bf_dump_prefix_pop(prefix); + DUMP(prefix, "rmap: struct bf_map *"); + bf_dump_prefix_push(prefix); + bf_map_dump(program->rmap, bf_dump_prefix_last(prefix)); + bf_dump_prefix_pop(prefix); + DUMP(prefix, "pmap: struct bf_map *"); bf_dump_prefix_push(prefix); bf_map_dump(program->pmap, bf_dump_prefix_last(prefix)); @@ -454,6 +480,10 @@ static int _bf_program_fixup(struct bf_program *program, insn_type = BF_FIXUP_INSN_IMM; value = program->cmap->fd; break; + case BF_FIXUP_TYPE_RATELIMIT_MAP_FD: + insn_type = BF_FIXUP_INSN_IMM; + value = program->rmap->fd; + break; case BF_FIXUP_TYPE_PRINTER_MAP_FD: insn_type = BF_FIXUP_INSN_IMM; value = program->pmap->fd; @@ -600,6 +630,21 @@ static int _bf_program_generate_rule(struct bf_program *program, EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_UPDATE_COUNTERS); } + if (rule->ratelimit) { + EMIT_LOAD_RATELIMIT_FD_FIXUP(program, BPF_REG_1); + EMIT(program, BPF_MOV32_IMM(BPF_REG_2, rule->index)); + EMIT(program, BPF_MOV32_IMM(BPF_REG_3, rule->ratelimit)); + EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_RATELIMIT); + + _clean_bf_jmpctx_ struct bf_jmpctx ctx = + bf_jmpctx_get(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0)); + + EMIT(program, + BPF_MOV64_IMM(BPF_REG_0, + program->runtime.ops->get_verdict(BF_VERDICT_DROP))); + EMIT(program, BPF_EXIT_INSN()); + } + switch (rule->verdict) { case BF_VERDICT_ACCEPT: case BF_VERDICT_DROP: @@ -893,6 +938,26 @@ static int _bf_program_load_printer_map(struct bf_program *program) return 0; } +static int _bf_program_load_ratelimit_map(struct bf_program *program) +{ + _cleanup_close_ int _fd = -1; + int r; + + bf_assert(program); + + r = bf_map_create(program->rmap); + if (r < 0) + return r; + + r = _bf_program_fixup(program, BF_FIXUP_TYPE_RATELIMIT_MAP_FD); + if (r < 0) { + bf_map_destroy(program->rmap); + return bf_err_r(r, "failed to fixup ratelimit map FD"); + } + + return 0; +} + static int _bf_program_load_counters_map(struct bf_program *program) { _cleanup_close_ int _fd = -1; @@ -1021,6 +1086,10 @@ int bf_program_load(struct bf_program *prog) if (r) return r; + r = _bf_program_load_ratelimit_map(prog); + if (r) + return r; + r = _bf_program_load_printer_map(prog); if (r) return r; @@ -1083,6 +1152,7 @@ void bf_program_unload(struct bf_program *prog) closep(&prog->runtime.prog_fd); bf_link_detach(prog->link); bf_map_destroy(prog->cmap); + bf_map_destroy(prog->rmap); bf_map_destroy(prog->pmap); bf_map_destroy(prog->lmap); bf_list_foreach (&prog->sets, map_node) @@ -1134,6 +1204,12 @@ int bf_program_pin(struct bf_program *prog, int dir_fd) goto err_unpin_all; } + r = bf_map_pin(prog->rmap, dir_fd); + if (r) { + bf_err_r(r, "failed to pin BPF ratelimit map for '%s'", name); + goto err_unpin_all; + } + r = bf_map_pin(prog->pmap, dir_fd); if (r) { bf_err_r(r, "failed to pin BPF printer map for '%s'", name); @@ -1175,6 +1251,7 @@ void bf_program_unpin(struct bf_program *prog, int dir_fd) bf_assert(prog); bf_map_unpin(prog->cmap, dir_fd); + bf_map_unpin(prog->rmap, dir_fd); bf_map_unpin(prog->pmap, dir_fd); bf_map_unpin(prog->lmap, dir_fd); diff --git a/src/bpfilter/cgen/program.h b/src/bpfilter/cgen/program.h index e4361f31..def96c2e 100644 --- a/src/bpfilter/cgen/program.h +++ b/src/bpfilter/cgen/program.h @@ -146,6 +146,18 @@ return __r; \ }) +#define EMIT_LOAD_RATELIMIT_FD_FIXUP(program, reg) \ + ({ \ + const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)}; \ + int __r = bf_program_emit_fixup( \ + (program), BF_FIXUP_TYPE_RATELIMIT_MAP_FD, ld_insn[0], NULL); \ + if (__r < 0) \ + return __r; \ + __r = bf_program_emit((program), ld_insn[1]); \ + if (__r < 0) \ + return __r; \ + }) + #define EMIT_LOAD_LOG_FD_FIXUP(program, reg) \ ({ \ const struct bpf_insn ld_insn[2] = {BPF_LD_MAP_FD(reg, 0)}; \ @@ -200,6 +212,8 @@ struct bf_program /// Counters map struct bf_map *cmap; + /// Rate limit map + struct bf_map *rmap; /// Printer map struct bf_map *pmap; /// Log map diff --git a/src/libbpfilter/include/bpfilter/ratelimit.h b/src/libbpfilter/include/bpfilter/ratelimit.h new file mode 100644 index 00000000..eeb83f70 --- /dev/null +++ b/src/libbpfilter/include/bpfilter/ratelimit.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Meta Platforms, Inc. and affiliates. + */ + +#pragma once + +#include + +#include + +/** + * @struct bf_ratelimit + * + * Rate limit assigned to each rule + * + * @var bf_ratelimit::current + * Packets allowed to pass in the current unit of time. + * @var bf_ratelimit::last_time + * Timestamp of the last packet. + */ +struct bf_ratelimit +{ + uint64_t current; + uint64_t last_time; +}; + +#define _free_bf_ratelimit_ __attribute__((__cleanup__(bf_ratelimit_free))) + +/** + * Create a new @ref bf_ratelimit with the given limit. + * + * On success, @p ratelimit is set to the newly allocated @ref bf_ratelimit, + * owned by the caller. + * + * @param ratelimit Output pointer for the new @ref bf_ratelimit. Can't be NULL. + * @param current Packets allowed to pass in the current unit of time. + * @param last_time Timestamp of the last packet. + * @return 0 on success, negative errno on error. + */ +int bf_ratelimit_new(struct bf_ratelimit **ratelimit, uint64_t current, + uint64_t last_time); + +/** + * @brief Allocate and initialize a new ratelimit from serialized data. + * + * @param ratelimit Rate limit object to allocate and initialize from the serialized + * data. The caller will own the object. On failure, `*ratelimit` is + * unchanged. Can't be NULL. + * @param node Node containing the serialized ratelimit. Can't be NULL. + * @return 0 on success, or a negative errno value on failure. + */ +int bf_ratelimit_new_from_pack(struct bf_ratelimit **ratelimit, + bf_rpack_node_t node); + +/** + * Free a @ref bf_ratelimit structure. + * + * If @p ratelimit is NULL, nothing is done. + * + * @param ratelimit Rate limit to free. Can't be NULL. + */ +void bf_ratelimit_free(struct bf_ratelimit **ratelimit); + +/** + * @brief Serialize a ratelimit. + * + * @param ratelimit Rate limit to serialize. Can't be NULL. + * @param pack `bf_wpack_t` object to serialize the ratelimit into. Can't be NULL. + * @return 0 on success, or a negative error value on failure. + */ +int bf_ratelimit_pack(const struct bf_ratelimit *ratelimit, bf_wpack_t *pack); diff --git a/src/libbpfilter/include/bpfilter/rule.h b/src/libbpfilter/include/bpfilter/rule.h index 4d809668..1a23bbf2 100644 --- a/src/libbpfilter/include/bpfilter/rule.h +++ b/src/libbpfilter/include/bpfilter/rule.h @@ -57,6 +57,7 @@ struct bf_rule uint32_t index; bf_list matchers; uint8_t log; + uint32_t ratelimit; /** Mark to set to the packet's `sk_buff`. Only support for some hooks. * The leftmost 32 bits are set to 1 if a mark is defined, or 0 otherwise. diff --git a/src/libbpfilter/ratelimit.c b/src/libbpfilter/ratelimit.c new file mode 100644 index 00000000..4833977d --- /dev/null +++ b/src/libbpfilter/ratelimit.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Meta Platforms, Inc. and affiliates. + */ +#include "bpfilter/ratelimit.h" + +#include +#include +#include +#include + +#include "bpfilter/helper.h" + +int bf_ratelimit_new(struct bf_ratelimit **ratelimit, uint64_t current, + uint64_t last_time) +{ + _cleanup_free_ struct bf_ratelimit *_ratelimit = NULL; + + bf_assert(ratelimit); + + _ratelimit = malloc(sizeof(*_ratelimit)); + if (!_ratelimit) + return -ENOMEM; + + _ratelimit->current = current; + _ratelimit->last_time = last_time; + + *ratelimit = TAKE_PTR(_ratelimit); + + return 0; +} + +int bf_ratelimit_new_from_pack(struct bf_ratelimit **ratelimit, + bf_rpack_node_t node) +{ + _free_bf_ratelimit_ struct bf_ratelimit *_ratelimit = NULL; + int r; + + bf_assert(ratelimit); + + r = bf_ratelimit_new(&_ratelimit, 0, 0); + if (r) + return r; + + r = bf_rpack_kv_u64(node, "current", &_ratelimit->current); + if (r) + return bf_rpack_key_err(r, "bf_ratelimit.current"); + + r = bf_rpack_kv_u64(node, "last_time", &_ratelimit->last_time); + if (r) + return bf_rpack_key_err(r, "bf_ratelimit.last_time"); + + *ratelimit = TAKE_PTR(_ratelimit); + + return 0; +} + +void bf_ratelimit_free(struct bf_ratelimit **ratelimit) +{ + bf_assert(ratelimit); + + if (!*ratelimit) + return; + + freep((void *)ratelimit); +} + +int bf_ratelimit_pack(const struct bf_ratelimit *ratelimit, bf_wpack_t *pack) +{ + bf_assert(ratelimit); + bf_assert(pack); + + bf_wpack_kv_u64(pack, "current", ratelimit->current); + bf_wpack_kv_u64(pack, "last_time", ratelimit->last_time); + + return bf_wpack_is_valid(pack) ? 0 : -EINVAL; +} diff --git a/src/libbpfilter/rule.c b/src/libbpfilter/rule.c index e630b491..e043c056 100644 --- a/src/libbpfilter/rule.c +++ b/src/libbpfilter/rule.c @@ -89,6 +89,10 @@ int bf_rule_new_from_pack(struct bf_rule **rule, bf_rpack_node_t node) if (r) return bf_rpack_key_err(r, "bf_rule.counters"); + r = bf_rpack_kv_u32(node, "ratelimit", &_rule->ratelimit); + if (r) + return bf_rpack_key_err(r, "bf_rule.ratelimit"); + r = bf_rpack_kv_u64(node, "mark", &_rule->mark); if (r) return bf_rpack_key_err(r, "bf_rule.mark"); @@ -137,6 +141,7 @@ int bf_rule_pack(const struct bf_rule *rule, bf_wpack_t *pack) bf_wpack_kv_u32(pack, "index", rule->index); bf_wpack_kv_u8(pack, "log", rule->log); bf_wpack_kv_bool(pack, "counters", rule->counters); + bf_wpack_kv_u32(pack, "ratelimit", rule->ratelimit); bf_wpack_kv_u64(pack, "mark", rule->mark); bf_wpack_kv_int(pack, "verdict", rule->verdict); @@ -171,6 +176,7 @@ void bf_rule_dump(const struct bf_rule *rule, prefix_t *prefix) DUMP(prefix, "log: %02x", rule->log); DUMP(prefix, "counters: %s", rule->counters ? "yes" : "no"); + DUMP(prefix, "ratelimit: 0x%" PRIx32, rule->ratelimit); DUMP(prefix, "mark: 0x%" PRIx64, rule->mark); DUMP(bf_dump_prefix_last(prefix), "verdict: %s", bf_verdict_to_str(rule->verdict));