What is the recommended way to unit test eBPF/XDP program?

246 views Asked by At

I'm writing a XDP program that is decoding/encoding a specific protocol. I would like to utilise unit tests to validate my implementation. However, there doesn't seem to be a standardised way to test this new "breed" of programs.

I have tried using standard C/C++ testing frameworks, e.g., GoogleTest. However I've been unsuccessful to mock XDP data. I also tried using the BCC framework (e.g., https://gist.github.com/yunazuno/14eb1b7fc5eb6bb23f36d1d01ed00367) but it seems to me more direct to small programs and only allows to include a single external header file.

1

There are 1 answers

4
Dylan Reimerink On BEST ANSWER

I recommend you look into using the bpf_prog_test_run_opts function of LibBPF or equivalent function in whatever loader library you are using.

Under the hood this function uses the BPF_PROG_TEST_RUN operation of the BPF syscall to invoke your program with a packet crafted by your test tool.

The options struct looks like this:

struct bpf_test_run_opts {
    size_t sz; /* size of this struct for forward/backward compatibility */
    const void *data_in; /* optional */
    void *data_out;      /* optional */
    __u32 data_size_in;
    __u32 data_size_out; /* in: max length of data_out
                  * out: length of data_out
                  */
    const void *ctx_in; /* optional */
    void *ctx_out;      /* optional */
    __u32 ctx_size_in;
    __u32 ctx_size_out; /* in: max length of ctx_out
                 * out: length of cxt_out
                 */
    __u32 retval;        /* out: return code of the BPF program */
    int repeat;
    __u32 duration;      /* out: average per repetition in ns */
    __u32 flags;
    __u32 cpu;
    __u32 batch_size;
};

The way you would use this is that you would load your BPF program as usual, then setup stuff like map values for your test senario.

You craft a packet you want to test with, by hand, with some sort of library or by capturing real packets and copying the bytes. Once you have a byte array of your packet you place a pointer to it in data_in and set the size of the array in data_size_in. If your program modifies the packet and you want to inspect the result you can provide the same or a different buffer to data_out and data_size_out.

Set repeat to 1 to test once, but you can also set this higher for if you want to benchmark your code. The retval will be populated with the return value of your XDP program so XDP_PASS,XDP_TX,XDP_DROP ect.

You should be able to assert on these output values with any test framework of your choosing. The program is actually executed, so writes to maps ect. actually happen, so you can assert on the changes in map contents as well.