Skip to content

Conversation

@SRodi
Copy link
Member

@SRodi SRodi commented Dec 19, 2025

Description

This PR implements runtime generation of vmlinux.h using bpftool to ensure eBPF programs are compiled against the host kernel's BTF information. This addresses issues where static vmlinux.h headers cause CO-RE relocation failures due to kernel structure offset mismatches.

Changes

  • Dockerfile: Added linux-tools-common and linux-tools-generic to install bpftool.
  • pkg/loader: Added GenerateVmlinuxH helper to dump BTF from /sys/kernel/btf/vmlinux.
  • Plugins: Updated dropreason, packetforward, and packetparser to generate and use the runtime vmlinux.h during compilation.

Fixes #1777

Related Issue

If this pull request is related to any issue, please mention it here. Additionally, make sure that the issue is assigned to you before submitting this pull request.

Checklist

  • I have read the contributing documentation.
  • I signed and signed-off the commits (git commit -S -s ...). See this documentation on signing commits.
  • I have correctly attributed the author(s) of the code.
  • I have tested the changes locally.
  • I have followed the project's style guidelines.
  • I have updated the documentation, if necessary.
  • I have added tests, if applicable.

Screenshots (if applicable) or Testing Completed

image

Additional Notes

Add any additional notes or context about the pull request here.


Please refer to the CONTRIBUTING.md file for more information on how to contribute to this project.

@SRodi SRodi added this to the 1.1 milestone Dec 19, 2025
@SRodi SRodi self-assigned this Dec 19, 2025
@SRodi SRodi requested a review from a team as a code owner December 19, 2025 15:04
Copy link
Collaborator

@rbtr rbtr 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 a good idea already, but there's same ambiguity to me around when/if the header could be regenerated instead of always. Kernel drift underneath us is a concern currently, since we can live-upgrade kernels.

// Check if vmlinux.h already exists to avoid regenerating it unnecessarily?
// However, if the pod restarts on a different node (unlikely for same pod instance but possible if volume persisted?),
// or if we want to be sure.
// Given the startup time is not critical and it's fast, let's generate it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how fast?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm thinking we should write it to disk (/tmp/retina like you've already referenced below) and skip regenerating it. but what happens if the kernel is updated? how can we tell when the kernel has drifted from our cached vmlinux.h? if there was an easy way to do that, i would say fully persist it to disk and only regenerate when that happens...but i'm not sure we can do that.

Comment on lines +122 to +125
runtimeHeaderDir := "/tmp/retina/include"
if err = loader.GenerateVmlinuxH(ctx, runtimeHeaderDir); err != nil {
dr.l.Warn("Failed to generate vmlinux.h, falling back to static headers", zap.Error(err))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should invoke it from every plugin. Seems like GenerateVmlinuxH should be invoked before plugins are started, and the path should be a constant plugin can refer to.

l.Error("Failed to generate vmlinux.h", zap.Error(err))
// If bpftool fails (e.g. /sys/kernel/btf/vmlinux doesn't exist), we might want to fallback or error out.
// If it fails, the compilation will likely fail later if we rely on this header.
return fmt.Errorf("failed to run bpftool: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this error message better - l.Error("Failed to generate vmlinux.h", zap.Error(err)) . Seems more accurate as bpftool can run and fail for myriad reasons.
Also, no need to log it here as caller would probably do that.

// Generate vmlinux.h
runtimeHeaderDir := "/tmp/retina/include"
if err = loader.GenerateVmlinuxH(ctx, runtimeHeaderDir); err != nil {
dr.l.Warn("Failed to generate vmlinux.h, falling back to static headers", zap.Error(err))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious about the consequence of this compilation failure. Seems like subsequent datapath errors can be attributed to this failure, but we won't have any idea if the warning log rotates after some time.

tar
RUN mkdir -p /tmp/bin
RUN arr="clang tcpdump ip ss iptables-legacy iptables-legacy-save iptables-nft iptables-nft-save cp uname" ;\
RUN arr="clang bpftool tcpdump ip ss iptables-legacy iptables-legacy-save iptables-nft iptables-nft-save cp uname" ;\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather have an init image with bpftool that can compile the vmlinux, and we can mount the directory to the agent image (optional). This way agent image only has the essentials.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

compile eBPF against a runtime-generated vmlinux.h

4 participants