Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/bfcli/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -85,6 +86,17 @@ log { BEGIN(STATE_LOG_OPTS); return LOG; }
}
}

/* Rate limit */
ratelimit { BEGIN(STATE_RATELIMIT_OPTS); return RATELIMIT; }
<STATE_RATELIMIT_OPTS>{
/* -?[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);
Expand Down
43 changes: 39 additions & 4 deletions src/bfcli/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,18 @@
})

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 {
uint8_t flags;

uint8_t log;
bool counter;
uint32_t ratelimit;
uint32_t mark;
};
}
Expand Down Expand Up @@ -93,8 +95,9 @@
%token CHAIN
%token RULE
%token SET
%token LOG COUNTER MARK
%token LOG COUNTER RATELIMIT MARK
%token <sval> LOG_HEADERS
%token <sval> RATELIMIT_VAL
%token <sval> SET_TYPE
%token <sval> SET_RAW_PAYLOAD
%token <sval> STRING
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions src/bpfilter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ bf_target_add_elfstubs(bpfilter
"parse_ipv6_nh"
"update_counters"
"log"
"ratelimit"
)

target_compile_definitions(bpfilter
Expand Down
34 changes: 34 additions & 0 deletions src/bpfilter/bpf/ratelimit.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <linux/bpf.h>

#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include <stddef.h>

#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);
}
3 changes: 3 additions & 0 deletions src/bpfilter/cgen/elfstub.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ enum bf_elfstub_id
*/
BF_ELFSTUB_LOG,

// Return 0 on ACCEPT, 1 on DROP
BF_ELFSTUB_RATELIMIT,

_BF_ELFSTUB_MAX,
};

Expand Down
2 changes: 2 additions & 0 deletions src/bpfilter/cgen/fixup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/bpfilter/cgen/fixup.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/bpfilter/cgen/prog/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions src/bpfilter/cgen/prog/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
77 changes: 77 additions & 0 deletions src/bpfilter/cgen/program.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@
#include <bpfilter/set.h>
#include <bpfilter/verdict.h>

#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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
Loading