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_REDIRECT_IFACE
%s STATE_REDIRECT_DIR
Expand Down Expand Up @@ -104,6 +105,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
39 changes: 35 additions & 4 deletions src/bfcli/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,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 @@ -101,9 +103,10 @@
%token CHAIN
%token RULE
%token SET
%token LOG COUNTER MARK
%token LOG COUNTER RATELIMIT MARK
%token REDIRECT_TOKEN
%token <sval> LOG_HEADERS
%token <sval> RATELIMIT_VAL
%token <sval> SET_TYPE
%token <sval> SET_RAW_PAYLOAD
%token <sval> STRING
Expand Down Expand Up @@ -297,6 +300,7 @@ rule : RULE matchers rule_options rule_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 @@ -351,6 +355,26 @@ rule_option : LOG LOG_HEADERS
.flags = BF_RULE_OPTION_COUNTER,
};
}
| RATELIMIT RATELIMIT_VAL
{
_cleanup_free_ char *in = $2;
char *tmp = in;
char *saveptr;
uint32_t limit;

if (tmp[0] == '-')
bf_parse_err("ratelimit should be positive");

errno = 0;
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,
};
}
| MARK STRING
{
_cleanup_free_ const char *raw_mark = $2;
Expand Down Expand Up @@ -387,6 +411,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
33 changes: 33 additions & 0 deletions src/bpfilter/bpf/ratelimit.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#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);
}
14 changes: 14 additions & 0 deletions src/bpfilter/cgen/elfstub.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ enum bf_elfstub_id
*/
BF_ELFSTUB_LOG,

/**
* Drop packets when `limit` number of packets have already been seen in the last unit of time
*
* `__u8 bf_ratelimit(void *map, __u64 key, __u64 limit)`
*
* **Parameters**
* - `map`: address of the ratelimit map.
* - `key`: key of the map to update.
* - `limit`: number of packets allowed to pass in one unit of time.
*
* **Return** 0 on accept, or 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
3 changes: 3 additions & 0 deletions src/bpfilter/cgen/prog/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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 @@ -172,6 +173,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 @@ -310,6 +312,7 @@ 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:
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
Loading