-
Notifications
You must be signed in to change notification settings - Fork 60
lib: cgen: group same-key maps using bitmasks #520
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?
Changes from all commits
ee9173c
2b52f63
24f35a9
7f30d9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,31 +5,70 @@ | |
|
|
||
| #include "cgen/matcher/set.h" | ||
|
|
||
| #include <limits.h> | ||
| #include <stdint.h> | ||
|
|
||
| #include <bpfilter/chain.h> | ||
| #include <bpfilter/matcher.h> | ||
| #include <bpfilter/set.h> | ||
|
|
||
| #include "cgen/program.h" | ||
| #include "cgen/stub.h" | ||
|
|
||
| /** | ||
| * @brief Emit bitmask check instructions after a map lookup. | ||
| * | ||
| * Set map values are bitmasks, sized to fit one bit per set in the | ||
| * group. After map_lookup_elem returns a non-NULL pointer in BPF_REG_0, | ||
| * this function emits instructions to: | ||
| * 1. Load the byte containing this set's bit from the pointer. | ||
| * 2. AND it with the set's bit. | ||
| * 3. Jump to the next rule if the bit is not set. | ||
| */ | ||
| static int _bf_emit_bitmask_check(struct bf_program *program, size_t bit_index) | ||
| { | ||
| assert(program); | ||
|
|
||
| /* r0 points to the map value. Load the byte containing our bit. */ | ||
| EMIT(program, BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, | ||
| (int16_t)(bit_index / CHAR_BIT))); | ||
|
|
||
| /* AND with the set's bit within that byte. */ | ||
| EMIT(program, | ||
|
pzmarzly marked this conversation as resolved.
|
||
| BPF_ALU32_IMM(BPF_AND, BPF_REG_0, 1U << (bit_index % CHAR_BIT))); | ||
|
|
||
| /* Jump to next rule if the result is 0 (bit not set). */ | ||
| EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0)); | ||
|
|
||
| return 0; | ||
| } | ||
|
Comment on lines
+18
to
+44
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really know BPF bytecode, this function is vibecoded. Can you check if it looks right? |
||
|
|
||
| static int _bf_matcher_generate_set_trie(struct bf_program *program, | ||
| const struct bf_matcher *matcher) | ||
| { | ||
| assert(program); | ||
| assert(matcher); | ||
|
|
||
| uint32_t set_index = *(uint32_t *)bf_matcher_payload(matcher); | ||
| const struct bf_set *set = | ||
| bf_chain_get_set_for_matcher(program->runtime.chain, matcher); | ||
| enum bf_matcher_type type = set->key[0]; | ||
| const struct bf_matcher_meta *meta = bf_matcher_get_meta(type); | ||
| enum bf_matcher_type type; | ||
| const struct bf_matcher_meta *meta; | ||
| size_t bit_index; | ||
| int r; | ||
|
|
||
| if (!set) { | ||
| return bf_err_r(-ENOENT, "set #%u not found in %s", | ||
| *(uint32_t *)bf_matcher_payload(matcher), | ||
| return bf_err_r(-ENOENT, "set #%u not found in %s", set_index, | ||
| program->runtime.chain->name); | ||
| } | ||
|
|
||
| type = set->key[0]; | ||
| meta = bf_matcher_get_meta(type); | ||
|
|
||
| r = bf_program_set_bit_index(program, set, &bit_index); | ||
| if (r) | ||
| return bf_err_r(r, "set '%s' not assigned to any group", set->name); | ||
|
|
||
| r = bf_stub_rule_check_protocol(program, meta); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Claude: suggestion: |
||
| if (r) | ||
| return bf_err_r(r, "failed to check for protocol"); | ||
|
|
@@ -67,15 +106,15 @@ static int _bf_matcher_generate_set_trie(struct bf_program *program, | |
| bf_matcher_type_to_str(type), type); | ||
| } | ||
|
|
||
| EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, | ||
| *(uint32_t *)bf_matcher_payload(matcher)); | ||
| EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, set); | ||
| EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10)); | ||
| EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(4))); | ||
| EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem)); | ||
|
|
||
| // Jump to the next rule if map_lookup_elem returned 0 | ||
| // Jump to the next rule if map_lookup_elem returned NULL | ||
| EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0)); | ||
| return 0; | ||
|
|
||
| return _bf_emit_bitmask_check(program, bit_index); | ||
| } | ||
|
|
||
| int bf_matcher_generate_set(struct bf_program *program, | ||
|
|
@@ -86,18 +125,23 @@ int bf_matcher_generate_set(struct bf_program *program, | |
|
|
||
| const struct bf_set *set = | ||
| bf_chain_get_set_for_matcher(program->runtime.chain, matcher); | ||
| uint32_t set_index = *(uint32_t *)bf_matcher_payload(matcher); | ||
| size_t bit_index; | ||
| size_t offset = 0; | ||
| int r; | ||
|
|
||
| if (!set) { | ||
| return bf_err_r(-ENOENT, "set #%u not found in %s", | ||
| *(uint32_t *)bf_matcher_payload(matcher), | ||
| return bf_err_r(-ENOENT, "set #%u not found in %s", set_index, | ||
| program->runtime.chain->name); | ||
| } | ||
|
|
||
| if (set->use_trie) | ||
| return _bf_matcher_generate_set_trie(program, matcher); | ||
|
|
||
| r = bf_program_set_bit_index(program, set, &bit_index); | ||
| if (r) | ||
| return bf_err_r(r, "set '%s' not assigned to any group", set->name); | ||
|
|
||
| // Ensure the packet uses the required protocols | ||
| for (size_t i = 0; i < set->n_comps; ++i) { | ||
| enum bf_matcher_type type = set->key[i]; | ||
|
|
@@ -136,14 +180,13 @@ int bf_matcher_generate_set(struct bf_program *program, | |
| offset += meta->hdr_payload_size; | ||
| } | ||
|
|
||
| EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, | ||
| *(uint32_t *)bf_matcher_payload(matcher)); | ||
| EMIT_LOAD_SET_FD_FIXUP(program, BPF_REG_1, set); | ||
| EMIT(program, BPF_MOV64_REG(BPF_REG_2, BPF_REG_10)); | ||
| EMIT(program, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, BF_PROG_SCR_OFF(0))); | ||
| EMIT(program, BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem)); | ||
|
|
||
| // Jump to the next rule if map_lookup_elem returned 0 | ||
| // Jump to the next rule if map_lookup_elem returned NULL | ||
| EMIT_FIXUP_JMP_NEXT_RULE(program, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 0)); | ||
|
|
||
| return 0; | ||
| return _bf_emit_bitmask_check(program, bit_index); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -71,10 +71,13 @@ int bf_map_new(struct bf_map **map, const char *name, enum bf_map_type type, | |
| * also as filename when pinning the map to the system. Can't be NULL or | ||
| * empty. | ||
| * @param set Set to create the map from. Can't be NULL. | ||
| * @param set_size Number of elements to reserve room for in the map. | ||
|
pzmarzly marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Claude: nit: The |
||
| * @param value_size Size (in bytes) of the value field. | ||
| * @return 0 on success, or a negative error value on error. | ||
| */ | ||
| int bf_map_new_from_set(struct bf_map **map, const char *name, | ||
| const struct bf_set *set); | ||
| const struct bf_set *set, size_t set_size, | ||
| size_t value_size); | ||
|
|
||
| /** | ||
| * @brief Allocate and initialize a new map from serialized data. | ||
|
|
||
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.
Claude: suggestion:
fixup->attr.set_ptr->namecan be NULL for anonymous sets. The ternary guards againstset_ptrbeing NULL but notnamebeing NULL. Usefixup->attr.set_ptr->name ?: "<anonymous>"to match the convention inbf_set_dump(set.c:364).