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

aya: bloom_filter: contains is not working #953

Open
jkoeppeler opened this issue May 26, 2024 · 0 comments
Open

aya: bloom_filter: contains is not working #953

jkoeppeler opened this issue May 26, 2024 · 0 comments

Comments

@jkoeppeler
Copy link

Hi,
I am having trouble to successfully execute the example code of the bloom_filter map:

// snippet
let mut bloom_filter = BloomFilter::try_from(bpf.map_mut("BLOOM_FILTER").unwrap())?;
bloom_filter.insert(1, 0)?;
assert!(bloom_filter.contains(&1, 0).is_ok());

The assert states that the item is not found. I want to use the bloom_filter it in an XDP program.

Steps to reproduce:

cargo generate --name myapp -d program_type=xdp https://github.com/aya-rs/aya-template

eBPF program:

#![no_std]
#![no_main]

use aya_ebpf::{bindings::xdp_action, macros::{map,xdp}, programs::XdpContext, maps::bloom_filter::BloomFilter};
use aya_log_ebpf::info;
#[map]
static mut MYFILTER: BloomFilter<u32> = BloomFilter::with_max_entries(1024,0);

#[xdp]
pub fn myapp(ctx: XdpContext) -> u32 {
    match try_myapp(ctx) {
        Ok(ret) => ret,
        Err(_) => xdp_action::XDP_ABORTED,
    }
}

fn try_myapp(ctx: XdpContext) -> Result<u32, u32> {
    Ok(xdp_action::XDP_PASS)
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    unsafe { core::hint::unreachable_unchecked() }
}

Userspace program:

use anyhow::Context;
use aya::programs::{Xdp, XdpFlags};
use aya::maps::bloom_filter::BloomFilter;
use aya::{include_bytes_aligned, Bpf};
use aya_log::BpfLogger;
use clap::Parser;
use log::{info, warn, debug};
use tokio::signal;

#[derive(Debug, Parser)]
struct Opt {
    #[clap(short, long, default_value = "enp2s0")]
    iface: String,
}

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
    let opt = Opt::parse();

    env_logger::init();

    // Bump the memlock rlimit. This is needed for older kernels that don't use the
    // new memcg based accounting, see https://lwn.net/Articles/837122/
    let rlim = libc::rlimit {
        rlim_cur: libc::RLIM_INFINITY,
        rlim_max: libc::RLIM_INFINITY,
    };
    let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) };
    if ret != 0 {
        debug!("remove limit on locked memory failed, ret is: {}", ret);
    }

    // This will include your eBPF object file as raw bytes at compile-time and load it at
    // runtime. This approach is recommended for most real-world use cases. If you would
    // like to specify the eBPF program at runtime rather than at compile-time, you can
    // reach for `Bpf::load_file` instead.
    #[cfg(debug_assertions)]
    let mut bpf = Bpf::load(include_bytes_aligned!(
        "../../target/bpfel-unknown-none/debug/myapp"
    ))?;
    #[cfg(not(debug_assertions))]
    let mut bpf = Bpf::load(include_bytes_aligned!(
        "../../target/bpfel-unknown-none/release/myapp"
    ))?;
    if let Err(e) = BpfLogger::init(&mut bpf) {
        // This can happen if you remove all log statements from your eBPF program.
        warn!("failed to initialize eBPF logger: {}", e);
    }
    let program: &mut Xdp = bpf.program_mut("myapp").unwrap().try_into()?;
    program.load()?;
    program.attach(&opt.iface, XdpFlags::default())
        .context("failed to attach the XDP program with default flags - try changing XdpFlags::default() to XdpFlags::SKB_MODE")?;
    info!("Loading bloom filter");
    let mut bloom_filter = BloomFilter::try_from(bpf.map_mut("MYFILTER").unwrap())?;
    bloom_filter.insert(1,0)?;
    assert!(bloom_filter.contains(&1, 0).is_ok());

    info!("Waiting for Ctrl-C...");
    signal::ctrl_c().await?;
    info!("Exiting...");

    Ok(())
}

What I figured out so far is that the kernel does not read the correct value from the provided user space address. I have no idea why this is happening..
I also tested to add a value from the ebpf-program to the bloom filter and check from the userspace if the value is present which worked! Also inserting from userspace and checking from the ebpf-program if the value is present works as well. The only combination that did not work is inserting from userspace and calling contains from userspace.
I found a temporary work-around for me by adding a second version of bpf_map_lookup_elem_ptr function and adjusting the bloom_filter contains function.

// In aya/src/sys/bpf.rs
pub(crate) fn bpf_map_lookup_elem_ptr1<K: Pod, V>(
    fd: BorrowedFd<'_>,
    key: Option<&K>,
    value: *const V,   // Note the const here - the only difference to the original function 
    flags: u64,
) -> SysResult<Option<()>> {
    let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
    let u = unsafe { &mut attr.__bindgen_anon_2 };
    u.map_fd = fd.as_raw_fd() as u32;
    if let Some(key) = key {
        u.key = key as *const _ as u64;
    }
    u.__bindgen_anon_1.value = value as u64;
    u.flags = flags;

    match sys_bpf(bpf_cmd::BPF_MAP_LOOKUP_ELEM, &mut attr) {
        Ok(_) => Ok(Some(())),
        Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
        Err(e) => Err(e),
    }
}

// In aya/src/maps/bloom_filter.rs
    pub fn contains(&self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
        let fd = self.inner.borrow().fd().as_fd();
        bpf_map_lookup_elem_ptr1::<u32, _>(fd, None, value.borrow(), flags)
            .map_err(|(_, io_error)| SyscallError {
                call: "bpf_map_lookup_elem",
                io_error,
            })?.ok_or(MapError::ElementNotFound)?;
        Ok(())
    }

I would be grateful if someone could confirm this bug or point out what I am doing wrong.
Also any ideas how to fix this or how to debug it are very welcome!

Thanks a lot <3

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

No branches or pull requests

1 participant