Skip to content

Conversation

@secDre4mer
Copy link
Contributor

Add support for more link inspection types, e.g. to query a uprobe's symbol.

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch 2 times, most recently from 26a871d to 9b0dcdd Compare November 7, 2025 08:59
@secDre4mer secDre4mer marked this pull request as ready for review November 7, 2025 08:59
@secDre4mer secDre4mer requested review from a team and mmat11 as code owners November 7, 2025 08:59
Copy link
Contributor

@mmat11 mmat11 left a 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?

if umi.offsets == nil {
return nil, fmt.Errorf("no offsets available")
}
ex, err := OpenExecutable(umi.path)
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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

Copy link
Collaborator

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.

@secDre4mer
Copy link
Contributor Author

looks mostly good to me, left a few comments; can this be split in multiple commits?

Sure, I'll try. Any ideas on how the commits should be structured? E.g. one focused on uprobe, one focused on tracepoint, ...?

@mmat11
Copy link
Contributor

mmat11 commented Nov 10, 2025

looks mostly good to me, left a few comments; can this be split in multiple commits?

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

@github-actions github-actions bot added the breaking-change Changes exported API label Nov 10, 2025
@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch 2 times, most recently from d0b8794 to 5f0a645 Compare November 10, 2025 08:16
@secDre4mer
Copy link
Contributor Author

Split is done.

@secDre4mer secDre4mer requested a review from mmat11 November 11, 2025 13:52
@mmat11
Copy link
Contributor

mmat11 commented Nov 24, 2025

@secDre4mer sorry for the delay, I was on vacation; I ran CI and some tests are failing, could you please take a look?

@secDre4mer
Copy link
Contributor Author

@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 make locally, as suggested, does not change any generated files. Maybe it's due to differing kernel versions? I'm running 6.17.8 locally.

@mmat11
Copy link
Contributor

mmat11 commented Nov 24, 2025

@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 make locally, as suggested, does not change any generated files. Maybe it's due to differing kernel versions? I'm running 6.17.8 locally.

hmm I think you would need to update CI_MAX_KERNEL_VERSION https://github.com/cilium/ebpf/blob/main/.github/workflows/ci.yml#L10 and run ./scripts/update-kernel-deps.sh

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

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from 5f0a645 to ab1e2f4 Compare November 25, 2025 08:01
@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from ab1e2f4 to fe34173 Compare November 25, 2025 08:06
@secDre4mer
Copy link
Contributor Author

@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 make update-external-deps.

Now there are some issues with connecting to git.kernel.org, but those look unrelated to the changes I've made.

@dylandreimerink
Copy link
Member

Re-triggered the tests, seems like we got some legit failures now

@secDre4mer
Copy link
Contributor Author

I'll take a look.

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch 3 times, most recently from cb8c71f to 5bc41fd Compare November 25, 2025 12:41
@secDre4mer
Copy link
Contributor Author

@dylandreimerink Do you have any idea why the error with Netfilter / XDP occurs here? I'm a little stumped.
The direct source is clear: The kernel uses EBUSY to indicate that another eBPF program is already attached.
However, I don't get why this is the case. While both TestAttachNetfilter and TestNetfilterInfo use the same hook / priority, they are executed sequentially and each should remove their program / link at the end.
Also, this seems to happen quite reliably in the CI on kernel 6.12; but when I run 6.12 locally, I can't reproduce it.

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from 5bc41fd to b4d3c9b Compare November 28, 2025 12:39
@ti-mo
Copy link
Collaborator

ti-mo commented Jan 6, 2026

@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! 🙏

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from b4d3c9b to 8901588 Compare January 6, 2026 15:32
@secDre4mer
Copy link
Contributor Author

@ti-mo sure, done.

@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from 8901588 to f495b64 Compare January 6, 2026 15:36
@mmat11
Copy link
Contributor

mmat11 commented Jan 7, 2026

@mmat11 We're a little short on review cycles over here (what's new) so I'd appreciate if could take another look. Thanks! 🙏

I restarted the failing tests and the CI looks ok now, the code lgtm!

mmat11
mmat11 previously approved these changes Jan 7, 2026
Copy link
Member

@dylandreimerink dylandreimerink left a 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.

dylandreimerink
dylandreimerink previously approved these changes Jan 8, 2026
Copy link
Member

@dylandreimerink dylandreimerink left a 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!

Copy link
Collaborator

@ti-mo ti-mo left a 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.


type NetfilterAttachFlags uint32

type NetfilterHook uint32
Copy link
Collaborator

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:

{"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.

Copy link
Contributor Author

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.

Copy link
Collaborator

@ti-mo ti-mo left a 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
Copy link
Collaborator

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 {
Copy link
Collaborator

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.

Copy link
Contributor Author

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?

@secDre4mer
Copy link
Contributor Author

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.
@ti-mo Gladly; I noticed that it was inconsistent when implementing the PR and wasn't sure how to work with it.

@secDre4mer
Copy link
Contributor Author

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.
I presume you have an application that does this kind of symbol resolution? Would you be willing to carry this feature locally?

Certainly. I understand your point that the Symbol() methods on uprobe / uprobemulti infos are possibly beyond the scope of this library, especially with the caveats (mount namespaces, potentially deleted / replaced files, ...)
Ultimately, it's your call on whether you want to include this. We could also separate this into a different merge request to keep the changes simpler / easier to discuss.
If it's not included, I'd like to keep the "look up a symbol for an offset" logic in Executable, though, since it is necessary to do this locally.

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]>
@secDre4mer secDre4mer force-pushed the feat/add-link-info-types branch from a19ee40 to 726999d Compare January 27, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change Changes exported API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants