foniod / redbpf

Rust library for building and running BPF/eBPF modules
Apache License 2.0
1.71k stars 136 forks source link

Error Loading BPF program #359

Open sebastiaoamaro opened 1 year ago

sebastiaoamaro commented 1 year ago

Hi, I am trying to load this BPF program, however, I get the error: thread 'tokio-runtime-worker' panicked at 'error loading BPF program: LoadError("measure_tcp_lifetime", BPF)', In RedBPF as far as I am aware tracks to this block https://github.com/foniod/redbpf/blob/55331987dc31a26feb192e4a668bf9bfc336e1e0/redbpf/src/lib.rs#L727 This is the code in the program

#![no_std]
#![no_main]
use core::mem::{self, MaybeUninit};
use memoffset::offset_of;

use redbpf_probes::socket_filter::prelude::*;

use monitor::usage::{SocketAddr,message};

//map to hold perf_events
#[map(link_section = "maps")]
static mut perf_events: PerfMap<message> = PerfMap::with_max_entries(512);

//map to hold the bytes sent
#[map(link_section = "maps")]
static mut usage: HashMap<u32, u128> = HashMap::with_max_entries(4096);

//map to hold time that has passed
#[map(link_section = "maps")]
static mut time: HashMap<u32, u64> = HashMap::with_max_entries(4096);

program!(0xFFFFFFFE, "GPL");
#[socket_filter]
fn measure_tcp_lifetime(skb: SkBuff) -> SkBuffResult {
    let eth_len = mem::size_of::<ethhdr>();
    let eth_proto = skb.load::<__be16>(offset_of!(ethhdr, h_proto))? as u32;
    if eth_proto != ETH_P_IP {
        return Ok(SkBuffAction::Ignore);
    }

    let ip_proto = skb.load::<__u8>(eth_len + offset_of!(iphdr, protocol))? as u32;
    if ip_proto != IPPROTO_TCP {
        return Ok(SkBuffAction::Ignore);
    }

    let mut ip_hdr = unsafe { MaybeUninit::<iphdr>::zeroed().assume_init() };
    ip_hdr._bitfield_1 = __BindgenBitfieldUnit::new([skb.load::<u8>(eth_len)?]);
    if ip_hdr.version() != 4 {
        return Ok(SkBuffAction::Ignore);
    }
    //Retrieve IPs from skbuff
    let ihl = ip_hdr.ihl() as usize;
    let dst = SocketAddr::new(
        skb.load::<__be32>(eth_len + offset_of!(iphdr, daddr))?,
        skb.load::<__be16>(eth_len + ihl * 4 + offset_of!(tcphdr, dest))?,
    );
    //We send new values every 25 milli seconds for a given ip, via perf_events
    unsafe{
        //retrieve size of packet
        let len:u128 = ((*skb.skb).len).into();
        let currenttime = bpf_ktime_get_ns();
        match time.get(&dst.addr){
            None=>{
                time.set(&dst.addr,&currenttime);
            },
            Some(oldtime)=>{
                if currenttime-oldtime > 25000000{
                //if currenttime-oldtime > 0{
                    match usage.get(&dst.addr){
                        None => usage.set(&dst.addr,&len),
                        Some(value) =>{
                            let newvalue:u128 = value + len;
                            usage.set(&dst.addr,&newvalue);
                            perf_events.insert(skb.skb as *mut __sk_buff,&message {dst:dst.addr,bytes:newvalue,}, );
                            time.set(&dst.addr,&currenttime);
                        }
                    }   
                }else{
                    match usage.get(&dst.addr){
                        None => usage.set(&dst.addr,&len),
                        Some(value) =>{
                            let newvalue:u128 = value + len;
                            usage.set(&dst.addr,&newvalue);
                        }
                    }
                }
            }
        }
    }

    Ok(SkBuffAction::Ignore)
}

I previously managed to get this program correctly loaded when I used u32 instead of u128, however, I need to change it now. Is there anything related to u128 that the kernel does not allow? Thank you in advance.

rsdy commented 1 year ago

That's correct, you can't use 128 bit wide fields in eBPF code. Should you need that, I think you will need to model it using 4 32 bit-wide fields.

sebastiaoamaro commented 1 year ago

Hi, thanks for the quick reply! And what about u64 are they allowed?

rsdy commented 1 year ago

@sebastiaoamaro I don't believe they are permitted either. The best way would be to test.

sebastiaoamaro commented 1 year ago

I am gonna test this when I have the time I think it is interesting to know. Should the cargo bpf tool allow the declaration of u128 if they are not allowed in the kernel? Do not know if this is hard to implement but it could maybe give a warning or something. Thanks for the replies.

bendahl commented 1 year ago

@sebastiaoamaro, @rsdy,

I think this depends on the kernel version. I've certainly used u128 in maps when dealing with IPv6 addresses. This patch set also suggests that support for 128 bit integers was added some time ago: https://www.spinics.net/lists/netdev/msg544476.html. For reference, I'm using a Kernel version 5.19 and have also performed some testing on 5.10. Hope this helps.

Cheers, Ben