BPF Helper Functions for Maps

Last Update : 07 August, 2023 | Published : 17 April, 2023 | 4 Min Read

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:

  1. map: A pointer to the BPF map to perform the lookup on.
  2. 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);
  1. &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 the xdp_stats_map BPF map that was defined earlier in the code.
  2. &key: A pointer to the key that you want to look up in the map. The key is of type __u32 and its value is determined by the variable key in the code, which is set to XDP_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.

This allows the eBPF program to keep track of the number of packets that pass through the XDP hook

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.

Looking for Cloud-Native Implementation?

Finding the right talent is pain. More so, keeping up with concepts, culture, technology and tools. We all have been there. Our AI-based automated solutions helps eliminate these issues, making your teams lives easy.

Contact Us