[0/3] Enable flavor specific matching#456
Conversation
src/bpfilter/cgen/matcher/meta.h
Outdated
| * @param matcher Matcher to generate comparison for. Can't be NULL. | ||
| * @return 0 on success, negative errno on error. | ||
| */ | ||
| int bf_matcher_generate_meta_mark(struct bf_program *program, |
There was a problem hiding this comment.
I am slightly torn between two approaches here (but I don't think it is too consequential, especially as bf_matcher_generate_meta_mark is no longer needed after #464):
- The approach currently in this PR - make
bf_matcher_generate_meta_mark/flow_hashpublic and have the variousgen_inline_matcherfunctions (i.e._bf_cgroup_skb_gen_inline_matcher) call them directly. Havebf_matcher_generate_packeterror out on those meta matchers. - We can keep
bf_matcher_generate_meta_mark/flow_hashprivate and thegen_inline_matchercallbacksEMITthe necessary instructions and then callbf_matcher_generate_packet->bf_matcher_generate_meta->_bf_matcher_generate_meta_mark/flow_hash.
Approach 2 would add less to the public API and tries to stay as similar to the current architecture, while approach 1 is more explicit in how it handles flavor-specific matchers. I'm currently leaning towards approach 1, especially given future plans to move to something along the lines of a table for per-matcher callbacks, but am open to thoughts. Thanks!
There was a problem hiding this comment.
The issue I have with the current suggestion is that bf_matcher_generate_meta_mark and bf_matcher_generate_meta_flow_hash expects the packet's mark to be in r1 already. It should be self contained and not expect specific register values (except for r6 to r9 registers which are callee saved).
There was a problem hiding this comment.
This probably can use some further discussion - my current thoughts, as described in #464, are that matchers have three stages of codegen, two of which are important here:
- data loading - each flavor will/can have it's own way to load the data into the proper registers
- comparison - once the values are in the right registers, the comparisons are flavor-agnostic
Given this, my thought is it would make sense to generalize the comparison stage when we can and then have each flavor define it's own data loading step. The idea here is similar, but as mentioned, it does require that we assume previous instructions loaded the correct value into specific registers.
For now, I renamed the functions to be more explicit that they are only generating the comparison bytecode, but that still runs into the problem you've mentioned (the expectation that data is loaded into certain registers).
There was a problem hiding this comment.
The design is sound, my comment is mostly about the (private) API we define than the actual logic. I would like the code to be more expressive about the fact r1 is expected to contain the processed value. However, it should be fine in this specific PR and is probably something to refine in the next one (#1/3). I'll add a comment there for more details.
50d0705 to
db3e0f6
Compare
| * | ||
| * @param verdict Verdict to convert. Must be valid. | ||
| * @return TC return code corresponding to the verdict, as an integer. | ||
| * @return Cgroup return code corresponding to the verdict, as an integer. |
There was a problem hiding this comment.
Drive-by fix here and same thing in nf.c
1811213 to
aef2e84
Compare
|
Claude: review of facebook/bpfilter #456 (6af7c7f) Must fix
Suggestions
Nits
CLAUDE.md improvements
|
aef2e84 to
1b1be91
Compare
43f3d72 to
44c34f9
Compare
src/bpfilter/cgen/matcher/meta.h
Outdated
| * @param matcher Matcher to generate comparison for. Can't be NULL. | ||
| * @return 0 on success, negative errno on error. | ||
| */ | ||
| int bf_matcher_generate_meta_mark(struct bf_program *program, |
There was a problem hiding this comment.
The issue I have with the current suggestion is that bf_matcher_generate_meta_mark and bf_matcher_generate_meta_flow_hash expects the packet's mark to be in r1 already. It should be self contained and not expect specific register values (except for r6 to r9 registers which are callee saved).
44c34f9 to
02568b1
Compare
Replace gen_inline_get_mark and gen_inline_get_skb in
bf_flavor_ops with a single gen_inline_matcher callback that gives
each flavor full control over matcher codegen. Packet-based
flavors intercept matcher types they handle specially (e.g.,
meta.mark on __sk_buff-based flavors) and delegate the rest to
bf_matcher_generate_packet().
Extract bf_matcher_generate_packet() from program.c into cgen/
matcher/packet.{h,c} where it belongs alongside the matcher
codegen it dispatches to.
Export bf_matcher_generate_meta_mark() and
bf_matcher_generate_meta_flow_hash() as comparison-only
helpers. Callers load the value into BPF_REG_1 before calling.
This is a prerequisite for CGROUP_SOCK_ADDR support, where the
flavor needs to intercept matchers like ip4.daddr and generate
bytecode that reads from bpf_sock_addr fields instead of packet
headers.
02568b1 to
6af7c7f
Compare
| if (rule->disabled) | ||
| return 0; | ||
|
|
||
| assert(program->runtime.ops->gen_inline_matcher); |
There was a problem hiding this comment.
Claude: suggestion — gen_inline_set_mark is guarded with a runtime check (if (!program->runtime.ops->gen_inline_set_mark) return bf_err_r(-ENOTSUP, ...)) a few lines below, but gen_inline_matcher uses assert() which is compiled out in release builds. If a new flavor is added without setting this callback, the assert won't fire in release and this becomes a NULL dereference.
Since gen_inline_matcher is documented as "Required for all flavors", the assert is defensible as a debug-time invariant check. But for consistency with how gen_inline_set_mark is guarded, consider a runtime check:
if (!program->runtime.ops->gen_inline_matcher)
return bf_err_r(-ENOTSUP, "gen_inline_matcher not set for %s",
program->runtime.chain->name);bpfilter/src/bpfilter/cgen/program.c
Line 296 in 6af7c7f
Stacked PRs:
CGROUP_SOCK_ADDRinitial chain support #459CGROUP_SOCK_ADDRmatcher support #465Currently, matcher codegen is hardcoded in a flavor-agnostic switch. With this refactor, each flavor controls its own matcher codegen through a callback, allowing a new flavor to intercept matchers and generate context-appropriate bytecode.
The
gen_inline_matchercallback generalizes the ad-hocgen_inline_get_markandgen_inline_get_skbcallbacks. Instead of having dedicated callbacks for each flavor-sensitive value, every flavor controls its full matcher codegen through a single dispatch point. Packet-based flavors delegate most matchers to a sharedbf_matcher_generate_packet()function (the extracted switch), overriding only the matchers that need flavor-specific context access.Here is an overview of the architectural changes:
Current Architecture
bf_flavor_ops (7 callbacks):
Problem: There is no simple mechanism for a flavor to provide alternative bytecode for the same matcher type. The only two flavor-aware matchers (
meta.markandmeta.flow_hash) use ad-hoc callbacks rather than a general dispatch mechanism.New Architecture (After)
bf_flavor_ops (6 callbacks):
Notes: