bpf_map_lookup_elem is a function in the Linux kernel’s BPF subsystem that is used to look up an element in a BPF map. BPF maps are key-value data structures that can be used by BPF programs running in the Linux kernel to store and retrieve data.
The bpf_map_lookup_elem
function takes two arguments:
map
: A pointer to the BPF map to perform the lookup on.key
: A pointer to the key used to look up the element in the map.
The function returns a pointer to the value associated with the given key in the BPF map if the key is found, or NULL
if the key is not found.
The function signature for bpf_map_lookup_elem
:
void *bpf_map_lookup_elem(void *map, const void *key);
In our program, bpf_map_lookup_elem()
the helper function provided by the eBPF API that is used to look up an element in the BPF map. It takes two arguments:
rec = bpf_map_lookup_elem(&xdp_stats_map, &key);
&xdp_stats_map
: A pointer to the BPF map (struct bpf_map_def) that we want to perform the lookup on. In this case, it refers to thexdp_stats_map
BPF map that was defined earlier in the code.&key
: A pointer to thekey
that you want to look up in the map. The key is oftype __u32
and its value is determined by the variable key in the code, which is set toXDP_PASS
.
The bpf_map_lookup_elem()
function returns a pointer to the value associated with the given key in the BPF map (&xdp_stats_map)
.
In other words, it allows you to retrieve the value stored in the BPF map corresponding to the key XDP_PASS
and store it in the rec
variable, which is of type struct datarec
and represents the data record stored in the map.
Note that if the lookup fails (i.e., the key does not exist in the map), the function may return NULL
, and it's important to perform a null pointer check, as shown in the code, to ensure the safety and correctness of the eBPF program.
if (!rec)
return XDP_ABORTED;
Code if (!rec)
is checking if the value of the pointer rec
is NULL
or not.
If rec
is NULL
, it means that the lookup operation using bpf_map_lookup_elem()
function failed, and the corresponding entry for the given key
was not found in the BPF map xdp_stats_map
.
The function returns XDP_ABORTED
as the return value.
The program defines a BPF hash map named xdp_stats_map to store the statistics. The map is an array with a size equal to XDP_ACTION_MAX (max entries), where each entry represents a different XDP action.
struct bpf_map_def SEC("maps") xdp_stats_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct datarec),
.max_entries = XDP_ACTION_MAX,
};
The XDP actions are enumerated in enum xdp_action,which is defined in include/uapi/linux/bpf.h
and their values are XDP_ABORTED, XDP_DROP, XDP_PASS, XDP_TX, and XDP_REDIRECT. For each XDP action, a corresponding entry is created in the xdp_stats_map
to store the number of packets that are associated with that action.
enum xdp_action {
XDP_ABORTED = 0,
XDP_DROP,
XDP_PASS,
XDP_TX,
XDP_REDIRECT,
};
Safely modifying shared data with _sync_fetch_and_add
#ifndef lock_xadd
#define lock_xadd(ptr, val) ((void) __sync_fetch_and_add(ptr, val))
#endif
We define a macro lock_xadd
that wraps the __sync_fetch_and_add
function using the GCC built-in function __sync_fetch_and_add
for performing an atomic fetch-and-add operation on a given memory location.
The macro takes two arguments: a pointer ptr to the target memory location, and a value val to be added to the current value of the memory location.
__sync_fetch_and_add
is a built-in GCC (GNU Compiler Collection) function that provides an atomic operation for fetching the current value of a memory location, adding a value to it, and storing the result back into the same memory location in a single, uninterruptible step.
This function is typically used in multi-threaded or concurrent programming to safely update shared variables without race conditions or other synchronization issues.
The macro definition simply wraps the __sync_fetch_and_add
function call with an additional (void)
cast to suppress any potential warnings about unused results, as the function returns the previous value of the memory location before the addition, which might not be used in some cases.
lock_xadd
lock_xadd(&rec->rx_packets, 1);
The lock_xadd()
function is used to atomically increment the value of rec->rx_packets
by 1
.
This operation ensures that the increment is performed atomically, meaning that it is thread-safe and can be safely used in a multi-CPU environment where multiple threads may be accessing the same memory location simultaneously.
The purpose of this operation is to increment the packet count in the rx_packets
field of the struct datarec
data record, which is stored in the xdp_stats_map
BPF map.
Once the packet count is updated, the eBPF program may return XDP_PASS
to indicate that the packet should be allowed to continue processing by the kernel networking stack.