-
Notifications
You must be signed in to change notification settings - Fork 823
feat: more link inspection support #1896
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?
Conversation
26a871d to
9b0dcdd
Compare
mmat11
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.
looks mostly good to me, left a few comments; can this be split in multiple commits?
link/link_other.go
Outdated
| if umi.offsets == nil { | ||
| return nil, fmt.Errorf("no offsets available") | ||
| } | ||
| ex, err := OpenExecutable(umi.path) |
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.
hmm I wonder if this is safe to do; should we just return addresses like for kprobeMulti?
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'm not sure, that's why there are the different Offsets and Symbols functions. Offsets returns the raw offsets, and only Symbols tries to resolve them.
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 think this method needs better documentation (something more than a one-liner is often useful for public API). If a process iterates all links on a system, this won't work if the calling process is in another mount namespace (e.g. a container) from the process that initially created the link, since that's typically when the path string is copied to kernel memory.
To me, this method falls more into convenience territory, since the user can accomplish the same using exported APIs. We tend to not include stuff like this, it goes a bit beyond the information returned by the kernel.
@secDre4mer I presume you have an application that does this kind of symbol resolution? Would you be willing to carry this feature locally?
| missed uint64 | ||
| address uint64 | ||
| missed uint64 | ||
| Function string |
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.
should we have these unexported with a getter?
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'm ambivalent to that; based on the kernel code I read, they should always be filled in (unlike address, which may be missing), so a getter doesn't provide more information. But I can understand wanting to keep it contiguous in a single struct.
Which way would you prefer?
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 think it's fine keeping as is as long as it's always filled
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.
Sorry to walk this back, but consistency is key. We've ended up with a random mix of methods and exported fields, adding to user confusion.
I've also never been a fan of those methods that only end up doing return kp.address, kp.address > 0, yeah no shit the field is zero. 😛 We initially went this way to leave the door open for real feature probes (e.g. bool true == kernel supports the field, even if it's zero), but given the amount of fields and permutations of features, this clearly never happened. I think we're getting more use out of them in ProgInfo, though, so we'll keep those around.
So, as a guide: if it's a simple field, let's just export it. If it's an array that needs more finessing or validation, let's put it behind a method. Same thing for when the kernel outputs a pointer and we need a type assertion (like Info.Tracing() and friends below), obviously we can't leave this up to the user to figure out.
@secDre4mer I like what you've done with kmi.Addresses() above, that's a good example of where providing an accessor is useful. Would you be up for removing kp.Address() and kp.Missed() below and exporting the fields instead? Now would be a good time to change this as we're touching surrounding functionality.
Sure, I'll try. Any ideas on how the commits should be structured? E.g. one focused on uprobe, one focused on tracepoint, ...? |
I guess you could split code generation and the different link types in different commits |
d0b8794 to
5f0a645
Compare
|
Split is done. |
|
@secDre4mer sorry for the delay, I was on vacation; I ran CI and some tests are failing, could you please take a look? |
Don't worry about the delay, I hope your vacation was nice! The CI complains that some generated files are out of date, see here; however, running |
hmm I think you would need to update the script has been added after last time I tried to update kernel types so I am not sure this is the correct thing to do -- /cc @lmb |
5f0a645 to
ab1e2f4
Compare
ab1e2f4 to
fe34173
Compare
|
@mmat11 Thanks for the hints, the solution turned out to be somewhat easier than expected; main was already on 6.16, so I only had to rebase the branch and re-run Now there are some issues with connecting to git.kernel.org, but those look unrelated to the changes I've made. |
|
Re-triggered the tests, seems like we got some legit failures now |
|
I'll take a look. |
cb8c71f to
5bc41fd
Compare
|
@dylandreimerink Do you have any idea why the error with Netfilter / XDP occurs here? I'm a little stumped. |
5bc41fd to
b4d3c9b
Compare
|
@secDre4mer Sorry for letting this languish, would you be able to rebase and resolve conflicts? Can't see the failures you were mentioning. @mmat11 We're a little short on review cycles over here (what's new) so I'd appreciate if could take another look. Thanks! 🙏 |
Signed-off-by: Max Altgelt <[email protected]>
b4d3c9b to
8901588
Compare
|
@ti-mo sure, done. |
8901588 to
f495b64
Compare
I restarted the failing tests and the CI looks ok now, the code lgtm! |
dylandreimerink
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.
Sorry for letting this linger, like Timo mentioned, review cycles have been short. But better late than never.
Overall this looks good, 2 minor points of interest.
Thanks for your work on this.
f495b64 to
d5e58f9
Compare
dylandreimerink
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 think this is in a good state now. Thank!
ti-mo
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.
Sorry for the long wait, there's too much stuff to review. Haven't made it through the full PR, but I've suggested something that could help with maintainability.
link/netfilter.go
Outdated
|
|
||
| type NetfilterAttachFlags uint32 | ||
|
|
||
| type NetfilterHook uint32 |
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.
These constants already exist in package x/sys/unix. These are pretty tedious to maintain by hand, so let's not do that. Unfortunately, nf_inet_hooks in particular has a wrong identifier (0x5 should be NF_INET_INGRESS, but it only appears as NF_INET_NUMHOOKS). Go's tooling to generate these isn't perfect and the uapi conventions on this are kind of all over the place. Fortunately, these enums both appear in kernel BTF, so our gentypes tool can generate Go enums from them, and we can re-export their values through the link package, similar to what you've added here.
type NetfilterInetHook uint32
const (
NetfilterInetPreRouting NetfilterInetHook = sys.NF_INET_PRE_ROUTING
NetfilterInetLocalIn NetfilterInetHook = sys.NF_INET_LOCAL_IN
...
)For nf_inet_hooks, the case is straightforward: add a record to the enums slice in gentypes, since it's a named enum:
ebpf/internal/cmd/gentypes/main.go
Line 224 in 61c4efe
| {"PerfEventType", "bpf_perf_event_type"}, |
The NFPROTO_* ones are more tricky since they're in an anonymous enum. It would be nice if we could add a small feature to gentypes that collects all enum values matching a prefix and emits them as a Go 'enum', e.g.:
{"NetfilterProtocolFamily", "NFPROTO_"},
would match and collect these values:
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_INET = 1,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_NETDEV = 5,
NFPROTO_BRIDGE = 7,
NFPROTO_IPV6 = 10,
NFPROTO_DECNET = 12,
NFPROTO_NUMPROTO,
};and output the following definition:
type NetfilterProtocolFamily uint32
const (
NFPROTO_UNSPEC NetfilterProtocolFamily = 0
NFPROTO_INET NetfilterProtocolFamily = 1
...
)Alternatively, we could add an extra condition to the strings.HasPrefix(..., "BPF_") filter at the top, but it would be nice to have something proper as it lowers the cost of future additions.
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.
Added the collection of unnamed enums to gentypes and used the new types / constants for the user-facing ones.
d5e58f9 to
a19ee40
Compare
ti-mo
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.
Thanks for making the gentypes changes so quickly, they're exactly what I had in mind. Finally managed to get through it all. There are a few changes I'd like to make to the exported API.
Calling OpenExecutable on behalf of a user is a potential surprise, so the fact that those methods depend on the executable being where they were when the link was created needs to be well-documented, and the resulting error needs to be very clear that this is expected when doing cross-namespace stuff.
In one of my comments I've called for removing umi.Symbols(), but I'm not opposed to merging it as long as the UX is good. Thanks!
| missed uint64 | ||
| address uint64 | ||
| missed uint64 | ||
| Function string |
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.
Sorry to walk this back, but consistency is key. We've ended up with a random mix of methods and exported fields, adding to user confusion.
I've also never been a fan of those methods that only end up doing return kp.address, kp.address > 0, yeah no shit the field is zero. 😛 We initially went this way to leave the door open for real feature probes (e.g. bool true == kernel supports the field, even if it's zero), but given the amount of fields and permutations of features, this clearly never happened. I think we're getting more use out of them in ProgInfo, though, so we'll keep those around.
So, as a guide: if it's a simple field, let's just export it. If it's an array that needs more finessing or validation, let's put it behind a method. Same thing for when the kernel outputs a pointer and we need a type assertion (like Info.Tracing() and friends below), obviously we can't leave this up to the user to figure out.
@secDre4mer I like what you've done with kmi.Addresses() above, that's a good example of where providing an accessor is useful. Would you be up for removing kp.Address() and kp.Missed() below and exporting the fields instead? Now would be a good time to change this as we're touching surrounding functionality.
| RefCtrOffset uint64 | ||
| } | ||
|
|
||
| type UprobeSymbol struct { |
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.
This isn't necessarily specific to uprobe functionality since this is returned from Executable.Symbol() as well. I'd simply name this Symbol.
Also, this could use a docstring.
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.
Arguably, UprobeSymbol / Symbol is incorrect since this does not describe a symbol, but an offset within a symbol. Do you have a suggestion on how to name this?
|
Signed-off-by: Max Altgelt <[email protected]>
Certainly. I understand your point that the |
Signed-off-by: Max Altgelt <[email protected]>
The netfilter options / info required / contained protocol family and hook, with little information for the user what values were expected. Add explicit types with constants to clarify what values are supported. Signed-off-by: Max Altgelt <[email protected]>
Signed-off-by: Max Altgelt <[email protected]>
Signed-off-by: Max Altgelt <[email protected]>
Signed-off-by: Max Altgelt <[email protected]>
a19ee40 to
726999d
Compare
Add support for more link inspection types, e.g. to query a uprobe's symbol.