aya-rs / aya

Aya is an eBPF library for the Rust programming language, built with a focus on developer experience and operability.
https://aya-rs.dev/book/
Apache License 2.0
3.2k stars 286 forks source link

Issue with Ingress Packets Not Reflecting Changes #950

Open FedericoRinaudi opened 5 months ago

FedericoRinaudi commented 5 months ago

I am currently working on a project where I need to remove padding from ingress packets using an ingress classifier. The logs indicate that the packet dimensions are correctly reduced, but it appears that the changes are not propagated to the upper levels.

Upon further investigation, I suspect that the packet is being propagated to the upper levels before the eBPF program completes its execution. I experimented with returning TC_ACT_SHOT at different points in the code, and I noticed that it only takes effect in the first few lines. When placed at the end of the function, it doesn’t work as expected. Could it be an issue related to asynchronicity?

This is the function I'm having problems with:

fn try_proxy_with_tc_aya(mut ctx: TcContext) -> Result<i32, i32> {
    let eth_hdr: EthHdr = ctx.load(0).map_err(|_| TC_ACT_OK)?;
    match eth_hdr.ether_type {
        EtherType::Ipv4 => {}
        _ => return Ok(TC_ACT_OK),
    }

    let ipv4_hdr: Ipv4Hdr = ctx.load(EthHdr::LEN).map_err(|_| TC_ACT_OK)?;
    let source = u32::from_be(ipv4_hdr.src_addr);

    if source != CLIENT_IP {
        info!(&ctx, "NON CLIENT_IP: {:x}", source);
        return Ok(TC_ACT_OK);
    }

    if ipv4_hdr.proto != IpProto::Tcp.into() {
        info!(&ctx, "NON TCP");
        return Ok(TC_ACT_OK);
    }

    let ipv4_len: usize =  (ipv4_hdr.ihl() << 2).into();
    let tcp_offset = EthHdr::LEN + ipv4_len;
    let tcp_hdr: TcpHdr = ctx.load(tcp_offset).map_err(|_| TC_ACT_OK)?;
    let tcp_len: usize = (tcp_hdr.doff() << 2).into();
    let http_offset = tcp_offset + tcp_len;

    info!(&ctx, "len {}", ctx.len());

    if ctx.len() <= http_offset as u32 {
        info!(&ctx, "NO HTTP, len: {} {}", ctx.len(), ctx.data_end() as usize - ctx.data() as usize);
        return Ok(TC_ACT_OK);
    }

    ctx.adjust_room(PADDING_LEN, BPF_ADJ_ROOM_NET, 0).map_err(|_| TC_ACT_OK)?;
    ctx.store(tcp_offset, &tcp_hdr, 0).map_err(|_| TC_ACT_OK)?;

    info!(&ctx, "ADJUSTED, len: {} {}", ctx.len(), ctx.data_end() as usize - ctx.data() as usize);

    Ok(TC_ACT_OK)
}

This is the link to the project.

As I am not an expert in Aya or eBPF, I might be overlooking something crucial. Any insights or suggestions would be greatly appreciated.

viveksb007 commented 5 months ago

I have seen something similar when I was changing checksum of packet during egress using TC egress classifier.

I was modifying the checksum of TCP header during egress which was somehow getting over-written by the kernel i believe. I didn't found the root cause, just that my changed in packet was getting over-written somehow (as those changes were not captured when TCP dump captured the egress packet).