Skip to content

Libbpf eBPF macro bpf_for_each

v1.2.0

The bpf_for_each macro is used to make looping over open-coded iterators easier.

Definition

#define bpf_for_each(type, cur, args...) for (                          \
    /* initialize and define destructor */                          \
    struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */,    \
                            cleanup(bpf_iter_##type##_destroy))),   \
    /* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */     \
                   *___p __attribute__((unused)) = (                \
                    bpf_iter_##type##_new(&___it, ##args),          \
    /* this is a workaround for Clang bug: it currently doesn't emit BTF */         \
    /* for bpf_iter_##type##_destroy() when used from cleanup() attribute */        \
                    (void)bpf_iter_##type##_destroy, (void *)0);        \
    /* iteration and termination check */                           \
    (((cur) = bpf_iter_##type##_next(&___it)));                     \
)

Usage

This macro makes looping over open coded iterators easier. Open coded iterators were introduced in kernel v6.4 and allow for iterating/looping over specific kernel data structures.

Normally, you would have to write iteration logic manually like:

SEC("raw_tp/sys_enter")
int my_example(const void *ctx)
{
    struct bpf_iter_task task_it;
    struct task_struct *task_ptr;

    // Initialize the iterator, request to iterate over all processes on the system
    bpf_iter_task_new(&task_it, NULL, BPF_TASK_ITER_ALL_PROCS);

    // Loop until `bpf_iter_task_next` returns NULL
    while((task_ptr = bpf_iter_task_next(&task_it))) {
        // Do something with the task pointer

        if (/* We found the task we are looking for*/)
            break;
    }

    bpf_iter_task_destroy(&task_it);
    return 0;
}

There are a few different iterator types, all follow the same naming convention: bpf_iter_<type>_{new,next,destroy}. The bpf_for_each macro makes use of this to simplify the iteration logic the user has to write.

The first argument is the <type> part of the iterator functions. The second argument is the variable name that will be used to store the current iterator value. And the rest of the arguments are passed to the bpf_iter_<type>_new function, the exact meaning depends on the iterator type.

Example

SEC("raw_tp/sys_enter")
int my_example(const void *ctx)
{
    struct task_struct *task_ptr;
    bpf_for_each(task, task_ptr, NULL, BPF_TASK_ITER_ALL_PROCS) {
        // Do something with the task pointer

        if (/* We found the task we are looking for*/)
            break;
    }

    return 0;
}