-
Notifications
You must be signed in to change notification settings - Fork 49
feature: rate limit #346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feature: rate limit #346
Conversation
3d56c6e to
63e5923
Compare
qdeslandes
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went for an early review, mostly for the parsing part. On the BPF side, the map will contain the runtime values for a given rule's rate limit (current burst/allowance, limit, last update time...).
src/bfcli/parser.y
Outdated
| char *tmp = in; | ||
| uint32_t limit = atoi(tmp); | ||
|
|
||
| printf("Got %d\n", limit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove
src/bfcli/parser.y
Outdated
| { | ||
| _cleanup_free_ char *in = $2; | ||
| char *tmp = in; | ||
| uint32_t limit = atoi(tmp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strtoul with overflow check would be better, see https://github.com/facebook/bpfilter/blob/main/src/libbpfilter/matcher.c#L283.
5021faf to
df768e9
Compare
|
Here is a working POC Things done:
Things remaining:
Testing: There's a bunch of doc to do and leftover comments to remove, but that should be enough to swi(t)ch this PR from draft to "ready-for-review" now that the feature is working ! |
src/bfcli/parser.y
Outdated
| BF_RULE_OPTION_MARK = 1 << 2, | ||
| BF_RULE_OPTION_LOG = 1 << 0, | ||
| BF_RULE_OPTION_COUNTER = 1 << 1, | ||
| BF_RULE_OPTION_RATELIMIT = 1 << 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New options should be added at the end, otherwise it breaks the ABI.
| bf_parse_err("ratelimit should be positive"); | ||
|
|
||
| errno = 0; | ||
| uint32_t limit = strtoul(strtok_r(tmp, "/", &saveptr), NULL, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variables should be defined at the beginning of the scope.
| $$ = (struct bf_rule_options){ | ||
| .ratelimit = limit, | ||
| .flags = BF_RULE_OPTION_RATELIMIT, | ||
| }; | ||
|
|
||
| $$ = (struct bf_rule_options){ | ||
| .ratelimit = limit, | ||
| .flags = BF_RULE_OPTION_RATELIMIT, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate.
|
|
||
| if (current_time != ratelimit->last_time) { | ||
| ratelimit->current = 0; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No braces needed.
| 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this yet.
| */ | ||
| BF_ELFSTUB_LOG, | ||
|
|
||
| // Return 0 on ACCEPT, 1 on DROP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document the full elfstub.
| EMIT(program, BPF_MOV32_IMM(BPF_REG_3, rule->ratelimit)); | ||
| EMIT_FIXUP_ELFSTUB(program, BF_ELFSTUB_RATELIMIT); | ||
|
|
||
| _clean_bf_jmpctx_ struct bf_jmpctx ctx = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variables are defined at the beginning of the scope.
| EMIT(program, | ||
| BPF_MOV64_IMM(BPF_REG_0, | ||
| program->runtime.ops->get_verdict(BF_VERDICT_DROP))); | ||
| EMIT(program, BPF_EXIT_INSN()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to think this through. With the current implementation: if the rate limit is reached, all extra packet matching the rule are dropped. What if the rule already drops the packet? Then you drop all the matching packets, meaning the rate limit actually matches because the outcome is the same.
Currently, actions (mark, log, counter) don't have any impact on whether the rule matches or not. This one does. Would this make more sense as a matcher instead? It would allow for negative matches:
rule meta.ratelimit 4pkt/s ACCEPT: if the packet is within the limit, accept itrule meta.ratelimit not 4pkt/s DROP: packets within the limit are ignored, while packets above are matched and dropped (notor another keyword that fits better).
Seems like this would be more in line with nftables. What do you think? I'm open to alternatives here.
Fix #215
Currently a WIP POC
I've started getting into adding this features.So far, I've managed to add the
ratelimit [int]/[time_unit]keyword to the cli and put it's value into abf_map.However I'm not sure about implementing a "real" rate limiting as it seems to requireEMIT(and that look scary).Added (empty for now) elfstub to handle rate limitingThe idea of the implementation so far is pretty naive (only allow the first X requests to pass through), mostly because it was easier to implement and I hadn't strong feelings on which direction to go.I'll keep this as a draft for now since it's still in its early stages.