Are loops allowed in Linux's BPF programs?
Asked Answered
O

2

9

I am thinking of a solution of replicating packets in the kernel and forward to 5 hosts (unicast). Planning to utilize eBPF/XDP for it.

I am trying to loop for 5 times, and inside the loop I am planning to clone the packet, modify the DST IP address, update the cksum and tx the packet out on the same intf it was received.

I read somewhere loops can't be used in XDP, so not sure if this will work ?

Need the expert's advice please.

Outdare answered 13/5, 2019 at 7:15 Comment(0)
A
19

Edit August 2022

The bpf_loop() BPF helper function is available starting with Linux 5.17 (commit), taking a number of iterations, a callback functions, and a context on which to run this callback in a loop within an eBPF program:

long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)

Edit June 2019

Bounded loops have now landed to the kernel, and are available starting with Linux 5.3 (commit).

Original answer

No, at this moment loops are not allowed in eBPF programs. Back edges are not allowed, so that the kernel verifier can make sure that the programs terminate (and do not hang the kernel).

This might change in the future, as kernel developers are working on support for bounding loops.

There are two possible workarounds worth mentioning. Both suppose that you know how many times you would have to “loop” when writing your program.

First, there's an exception for back edges, regarding functions. Which means you can have functions, and call them multiple times. So you could have all the content that you would normally put in your loop in a separate function, and call this function as many times as you would loop.

The second thing is that you can actually write loops in your C code, and ask clang to unroll them at compile time. This looks like the following:

#pragma clang loop unroll(full)
        for (i = 0; i < 4; i++) {
            /* Do stuff ... */
        }

This means that in the resulting object file the function will be unrolled, it will be replaced by the full series of instructions to be performed, with no actual backward jump.

There is no solution for a sequence with an arbitrary number of loops at the moment.

Arman answered 13/5, 2019 at 7:52 Comment(4)
$ sudo ip link set dev enp0s8 xdp object loop.o sec test_loop Prog section 'test_loop' rejected: Invalid argument (22)! - Type: 6 - Instructions: 15 (0 over limit) - License: Verifier analysis: back-edge from insn 12 to 3 Error fetching program/map!Outdare
SEC("test_loop") int loop() { int i = 0; #pragma clang loop unroll(full) for (i = 0; i < 4; i++) { /* Do stuff ... */ } return XDP_PASS; }Outdare
Im sorry, I compiled with optimization "-O2" option and it did the trick. Thank You.Outdare
bpf_loop is interesting! Just in case someone is looking for the signature of callback function, long bpf_loop(u32 iterations, long (*loop_fn)(u32 index, void *ctx), void *ctx, u64 flags); . This was take from lwn.net/Articles/877062.Laurentia
E
2

For Linux <5.3:

Technically, back-edges in the control flow graph of BPF bytecode programs are forbidden, not loops. Concretely, this means that you can write bounded loops in C, but you have to unroll them at compile time.

To unroll loop, you can use Clang's #pragma unroll directive. This should work for a 5-iterations loop, but won't for very long loops however.

Emalee answered 13/5, 2019 at 7:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.