Skip to content
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

Support for BPF_PROG_TYPE_EXT #127

Merged
merged 3 commits into from Jan 12, 2022
Merged

Support for BPF_PROG_TYPE_EXT #127

merged 3 commits into from Jan 12, 2022

Conversation

dave-tucker
Copy link
Member

@dave-tucker dave-tucker commented Dec 2, 2021

This PR add support for BPF_PROG_TYPE_EXT

These program types can be used to replace a function (freplace) of an already loaded BPF program.
In order to to this we must ensure that when the main program is loaded, its BTF is also loaded into the kernel.
This requires that we also provide func_info and line_info

In order to load BTF to the kernel safely, this PR also implements feature probing.
This allows us to detect BTF feature support and conditionally enable/disable parts of the loading process and/or fix the BTF provided by LLVM to work with the current running kernel.

  1. We properly adjust the func_info and line_info from the .BTF.ext ELF section, including in cases where we link functions from other ELF sections
  2. If a program contains a valid .BTF and .BTF.ext and section, we will load these in to the kernel
  3. We also set the program name in the BPF_PROG_LOAD syscall for ease of finding programs later

As a result, when loading a BPF program (written in C, with BTF) we get line information included in the output from bpftool prog dump xlated, as well as the ability to inspect a loaded programs BTF with bpftool btf dump.

The API for BPF_PROG_TYPE_EXT. The API is as follows:

  1. Get the program from the loaded BPF as usual let ext : &mut Extension = bpf.programs_mut("xdp_pass").unwrap().try_into()
  2. In order to load an Extension program, we need the BPF FD of the program we are targeting. This can be obtained from a LinkRef if we have one in scope, using LinkRef::fd(). Since it's likely that the root program may be pinned, I also implemented LinkRef::from_pinned_path().

The API for load and attach is as follows:

let prog_ref = LinkRef::from_pinned_path(path)?;
let link_fd = prog_ref.fd()?;
let pass: &mut Extension = bpf.program_mut("pass").unwrap().try_into()?;
pass.load(link_fd, "prog0".to_string())?;
pass.attach(link_fd)?;

@dave-tucker dave-tucker changed the title Load BTF with Program Load BTF into the kernel Dec 2, 2021
@dave-tucker
Copy link
Member Author

Here's a proposed API for BPF_PROG_TYPE_EXT

  1. Use BpfLoader::new().prog_freplace() which will treat all programs you try and load as BPF_PROG_TYPE_EXT. This is useful because you may write them as a normal XDP or TC program, but we convert them on load.

  2. We need a way to get RawFd from LinkRef. I added an fd() function for now, but I fully suspect this will need to change

  3. The API for load and attach will look something like this. We need a program fd on load, so we can lookup program BTF and validate the function name we're attaching to - this could actually be a the BTF Fd instead, if we had some API to get that for a given program that's been loaded. attach takes the FD of the program instance you want to attach to.

let pass: &mut Extension = bpf2.program_mut("pass").unwrap().try_into()?;
pass.load(link_fd, "prog0".to_string())?;
pass.attach(link_fd)?;

It's still not working as yet, as I've got to add func_info and line_info support to the relocations.

This is the full example:

use aya::programs::{Xdp, XdpFlags, Extension, LinkRef};
use aya::{include_bytes_aligned, Bpf, BpfLoader};

use std::{
    convert::{TryInto},
    sync::atomic::{AtomicBool, Ordering},
    sync::Arc,
    thread,
    time::Duration,
};
use structopt::StructOpt;

fn main() {
    env_logger::init();
    if let Err(e) = try_main() {
        eprintln!("error: {:#}", e);
    }
}

#[derive(Debug, StructOpt)]
struct Opt {
    #[structopt(short, long, default_value = "eth0")]
    iface: String,
}

fn try_main() -> Result<(), anyhow::Error> {
    let opt = Opt::from_args();
    let mut bpf = Bpf::load(include_bytes_aligned!("../../xdp_dispatcher.bpf.o"))?;
    let program: &mut Xdp = bpf.program_mut("dispatcher").unwrap().try_into()?;
    program.load()?;
    let path = "/sys/fs/bpf/xdp_dispatcher";
    program.attach(&opt.iface, XdpFlags::default())?;
    bpf.program_mut("dispatcher").unwrap().pin(path)?;
    
    let prog_ref = LinkRef::from_pinned_path(path)?;
    
    let link_fd = prog_ref.fd().unwrap();

    let mut bpf2 = BpfLoader::new().prog_freplace().load(include_bytes_aligned!("../../xdp_pass.bpf.o"))?;
    let pass: &mut Extension = bpf2.program_mut("pass").unwrap().try_into()?;
    pass.load(link_fd, "prog0".to_string())?;
    pass.attach(link_fd)?;
    
    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();

    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    })
    .expect("Error setting Ctrl-C handler");

    println!("Waiting for Ctrl-C...");
    while running.load(Ordering::SeqCst) {
        thread::sleep(Duration::from_millis(500))
    }
    println!("Exiting...");

    Ok(())
}

@dave-tucker dave-tucker changed the title Load BTF into the kernel Support for BPF_PROG_TYPE_EXT Dec 8, 2021
@dave-tucker dave-tucker marked this pull request as ready for review December 8, 2021 14:46
@dave-tucker
Copy link
Member Author

Updated PR with a better description since this is 90% done now. Will push one more commit to do more fixups before loading to the kernel. We'll have to probe for kernel support since I guess it's possible that some distro vendors might backport some of these features at somepoint.

@dave-tucker
Copy link
Member Author

@alessandrod

  1. Rebased on Update libbpf to 19656636a9b9a2de1f71fa3135709295c16701cc #145 to pick up codegen changes
  2. Removed BpfLoader::new().programs_as_extensions()
  3. Implemented TryFrom<&'a Program> and TryFrom<&'a mut Program> for Extension
  4. Implemented AsRef<Extension> and AsMut<Extension> to perform reference to reference conversion, since this is required for the TryFrom<&'a Program> implementation

@dave-tucker
Copy link
Member Author

Rebased!

Copy link
Collaborator

@alessandrod alessandrod left a comment

Choose a reason for hiding this comment

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

This is incredible, you're a hero

aya/Cargo.toml Outdated Show resolved Hide resolved
aya/src/bpf.rs Outdated Show resolved Hide resolved
aya/src/obj/mod.rs Outdated Show resolved Hide resolved
aya/src/bpf.rs Outdated Show resolved Hide resolved
aya/src/programs/mod.rs Show resolved Hide resolved
aya/src/obj/btf/types.rs Outdated Show resolved Hide resolved
aya/src/obj/btf/btf.rs Outdated Show resolved Hide resolved
aya/src/obj/btf/btf.rs Outdated Show resolved Hide resolved
// Start DataSec Fixups
let sec_name = self.type_name(t)?.ok_or(BtfError::InvalidTypeInfo)?;
let name = sec_name.to_string();
// There are cases when the compiler does indeed populate the
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you have a test case that repros when LLVM sets the size vs when it doesn't? I'm happy to fix this in LLVM if it's a bug

Copy link
Member Author

Choose a reason for hiding this comment

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

It's expected behaviour. See https://reviews.llvm.org/D59441

At the time of debug info emission, the data section
size is unknown, so the btf_type.size = 0 for
BTF_KIND_DATASEC.

I don't remember if I saw a comment in libbpf or LLVM that triggered me to add this conditional

aya/src/obj/btf/btf.rs Show resolved Hide resolved
@dave-tucker
Copy link
Member Author

@alessandrod comments addressed, docs improved and here's a working example https://github.com/dave-tucker/aya-ext-test

aya/src/programs/mod.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@alessandrod alessandrod left a comment

Choose a reason for hiding this comment

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

See the doc comments (including #127 (comment) which vscode put in the old review omg) and the AsRef stuff

@dave-tucker dave-tucker added feature A PR that implements a new feature or enhancement aya This is about aya (userspace) aya-bpf This is about aya-bpf (kernel) labels Jan 10, 2022
@dave-tucker
Copy link
Member Author

@alessandrod addressed docs comments and removed the AsRef stuff. Now you need to use BpfLoader::new().extension("myfunc") to mark programs as being an extension...

aya/src/obj/btf/btf.rs Outdated Show resolved Hide resolved
This allows for parsed BTF to be re-encoded such that it could be loaded
in to the kernel. It moves bytes_of to the utils package. We could use
Object::bytes_of, but this requires the impl of the Pod trait on
generated code.

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
aya/src/sys/bpf.rs Outdated Show resolved Hide resolved
aya/src/sys/bpf.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@alessandrod alessandrod left a comment

Choose a reason for hiding this comment

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

Some minor changes, and we're finally done!

This requires loading the BTF to kernel when loading all programs as
well as implementing Extension program type

Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
Signed-off-by: Dave Tucker <dave@dtucker.co.uk>
@alessandrod
Copy link
Collaborator

Thank you so much this PR is 🔥

@alessandrod alessandrod merged commit c5a10f8 into aya-rs:main Jan 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aya This is about aya (userspace) aya-bpf This is about aya-bpf (kernel) feature A PR that implements a new feature or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants