aya icon indicating copy to clipboard operation
aya copied to clipboard

Access packet data as &[u8] from XdpContext

Open fcantournet opened this issue 2 years ago • 5 comments

Hi,

I'm trying to use pdu to do some packet parsing from the XdpContext. This seemed to work at some point in redbpf examples at https://github.com/rebpf/rebpf/blob/50e235721228c1ece2c685f9357a954bd4a322d3/examples/packet_parser/src/kern.rs#L44

[EDIT] So I ported this function to XdpContext in aya-bpf here source : https://sourcegraph.com/github.com/rebpf/rebpf@50e235721228c1ece2c685f9357a954bd4a322d3/-/blob/rebpf/src/libbpf.rs?L547:12#tab=def

I'm trying to access it like so :

#[inline(always)]
fn try_dns_snoop(ctx: XdpContext) -> Result<u32, Error> {

    let buf: &[u8] = if let Some(buf) = ctx.data_buffer() {
        buf
    } else {
        return Ok(xdp_action::XDP_PASS);
    };

    //let ether = EthernetPdu::new(buf)?;
    let first_byte = buf[0];

    let log_entry = PacketLog {
        first_byte: first_byte,
    };
    unsafe {
        EVENTS.output(&ctx, &log_entry, 0);
    }
    Ok(xdp_action::XDP_PASS)
}

But the verifier complains about out of packet access :

0: R1=ctx(id=0,off=0,imm=0) R10=fp0
0: (61) r2 = *(u32 *)(r1 +0)
1: R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0
1: (15) if r2 == 0x0 goto pc+17
 R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0
2: R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0
2: (61) r3 = *(u32 *)(r1 +4)
3: R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R10=fp0
3: (3d) if r2 >= r3 goto pc+15
 R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R10=fp0
4: R1=ctx(id=0,off=0,imm=0) R2_w=pkt(id=0,off=0,r=0,imm=0) R3_w=pkt_end(id=0,off=0,imm=0) R10=fp0
4: (71) r2 = *(u8 *)(r2 +0)
invalid access to packet, off=0 size=1, R2(id=0,off=0,r=0)
R2 offset is outside of the packet
verification time 29 usec
stack depth 0
processed 5 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Am I holding this wrong ?

fcantournet avatar Nov 05 '21 16:11 fcantournet

I can't see the source of the function that you've ported - both links show redbpf code, not aya.

You need to make sure that you check that the offset you are reading is within the bounds of the packet or the verifier will complain like this. See https://aya-rs.github.io/book/start/logging-packets.html#getting-packet-data-from-the-context for an example function that has a check like this.

In your example, you'd need to check first_byte is in bounds before you use it.

dave-tucker avatar Nov 05 '21 16:11 dave-tucker

Hi dave-tucker, and thanks for the response :) I've updated the issue with a link to my fork. So I understand it now, every access needs to be verified, you cannot just validate the buffer up until some point once and go ham.

This means pdu needs to be changed quite a bit to work in a bpf context. I've found a fork here that does this (partially) https://github.com/uccidibuti/pdu Now I can parse the data into a EthernetPdu and use (some of) pdu's api.

I think it would be nice to have those methods I added back in XdpContext, so i'll open a PR

fcantournet avatar Nov 05 '21 17:11 fcantournet

Hi dave-tucker, and thanks for the response :) I've updated the issue with a link to my fork. So I understand it now, every access needs to be verified, you cannot just validate the buffer up until some point once and go ham.

This is why I didn't add a method that returns a slice btw. Returning a slice but then not being able to use it like you would use any other slice in rust doesn't seem very intuitive.

I'm very much in favor of having an API that returns something slice-like, but it needs to work without having to do extra validation with pointers and stuff, else I think it actually becomes worse than telling people to just use pointers.

alessandrod avatar Nov 06 '21 11:11 alessandrod

I'm very much in favor of having an API that returns something slice-like,

To expand a bit on this: it should be possible to create an API that returns a slice (or something that behaves like a slice), that does packet boundary checks internally in a way that the verifier is able to follow. It will probably require a bit of back and forth with the verifier, but I believe it should be possible.

alessandrod avatar Nov 06 '21 11:11 alessandrod

Yes I'm seeing that now from looking at the bpf branch of pdu from https://github.com/uccidibuti/pdu.

I'm very much in favor of having an API that returns something slice-like,

To expand a bit on this: it should be possible to create an API that returns a slice (or something that behaves like a slice), that does packet boundary checks internally in a way that the verifier is able to follow. It will probably require a bit of back and forth with the verifier, but I believe it should be possible.

I think that would be a very nice api.

fcantournet avatar Nov 06 '21 11:11 fcantournet