eBPF

  • eBPF(extended Berkeley Packet Filter): Technology to extend kernel operation using small programs that run safely inside the Linux Kernel
  • eBPF allows you to attach and execute a program at a specific point inside the Kernel without directly modifying the kernel source code or reloading the Kernel Module.
    • For example, if the eBPF Program is connected to a system call, network packet processing point, file access point, process execution point, kernel function call point, etc., the eBPF Program is executed when the relevant event occurs.
  • eBPF is not simply a technology for packet filtering, but can be viewed as a kernel extension technology used for Observability, Networking, Security, and Tracing in a modern Linux environment.

BPF and eBPF

Initially, BPF was an abbreviation for Berkeley Packet Filter, and was a technology for filtering network packets.

For example, the BPF filter is used when capturing only packets under certain conditions in tcpdump or Wireshark.

tcpdump tcp port 80

Rather than filtering all packets after bringing them to User Space, the above filter first filters out only packets that meet the conditions in Kernel Space.

In other words, the core purpose of the existing BPF was network packet filtering.

However, eBPF is a structure that extends the existing BPF to allow programs to be executed at various points within the kernel as well as the network.

Therefore, eBPF is closer to an event-driven program execution environment that runs safely inside the kernel rather than a simple packet filter.

Kernel Space and User Space

One of the most important concepts when understanding eBPF is Kernel Space and User Space.

  • Kernel Space: Area where the operating system kernel runs
  • User Space: Area where general applications run

General programs run in User Space.

For example, most web servers, shells, browsers, and general C programs run in User Space.

On the other hand, Kernel Space is responsible for core functions such as system call processing, file system, network stack, process scheduling, and memory management.

In order for a User Space program to open a file, communicate over a network, or create a process, it must ultimately use the Kernel’s functions.

For example, if you have the following code:

open("/etc/passwd", 0);

This code runs in User Space, but actual file access is handled in Kernel Space.

eBPF connects a small program to a specific execution point in the Kernel Space, allowing it to operate directly inside the Kernel when an event occurs.

eBPF Program

eBPF Program is a small program that runs inside the Kernel.

It is usually written in C language and then compiled into eBPF Bytecode using LLVM/Clang.

SEC("tracepoint/syscalls/sys_enter_execve")
int handle_execve(void *ctx) {
    return 0;
}

The above code is an example of an eBPF Program that can be connected to the execve system call entry point.

This program can be executed inside the Kernel when the user runs a new program.

The eBPF Program cannot perform any operation like a typical User Space program.

Because it runs inside the Kernel, stability and security are very important.

Therefore, the eBPF Program must pass the Verifier’s inspection before being loaded into the Kernel.

eBPF Execution Flow

The eBPF Program is usually executed in the following flow.

  1. Developer writes eBPF Program.
  2. Compile to eBPF Bytecode with Clang/LLVM.
  3. User Space Loader loads the eBPF Program into the Kernel through bpf() syscall.
  4. Kernel Verifier checks whether the eBPF Program is safe.
  5. If verification passes, the eBPF Program is loaded into the Kernel.
  6. Attach the eBPF Program to a specific Hook Point.
  7. When the corresponding Kernel Event occurs, the eBPF Program is executed.
  8. eBPF Program stores data or delivers it to User Space through Map, Ring Buffer, Helper Function, etc.

Rather than simply inserting code into the kernel, eBPF follows a controlled flow of verification, program loading, attachment, event handling, and data export.

Hook Point

Hook Point is an execution point inside the Kernel where the eBPF Program is connected.

The eBPF Program is not executed anywhere, but is connected to a specific event or specific Kernel path.

Representative Hook Points are as follows.

Tracing
    - kprobe
    - kretprobe
    - tracepoint
    - fentry
    - fexit

Networking
    - XDP
    - TC
    - socket filter
    - cgroup skb
    - cgroup sock

Security
    - LSM hook

User Space Tracing
    - uprobe
    - uretprobe

For example, kprobe allows executing an eBPF Program when a kernel function is called.

tracepoint allows you to connect an eBPF Program to a predefined trace point in the kernel.

XDP allows network packets to be processed at a point close to the NIC Driver before they go up to the kernel network stack.

The LSM hook allows the eBPF Program to be connected to security decision points such as file access, process execution, and permission checking.

Program Type

eBPF Program is divided into Program Types depending on the connection location and purpose.

Program Type determines in which Kernel Context the eBPF Program runs, which Helper Functions it can use, and what return value it should have.

Representative Program Types are as follows.

BPF_PROG_TYPE_KPROBE
BPF_PROG_TYPE_TRACEPOINT
BPF_PROG_TYPE_XDP
BPF_PROG_TYPE_SCHED_CLS
BPF_PROG_TYPE_SOCKET_FILTER
BPF_PROG_TYPE_CGROUP_SKB
BPF_PROG_TYPE_CGROUP_SOCK_ADDR
BPF_PROG_TYPE_LSM
BPF_PROG_TYPE_PERF_EVENT

For example, XDP Program is a Program Type for quickly processing network packets.

The XDP Program can decide whether to pass the packet as is, drop it, or redirect it to another interface.

XDP_PASS
XDP_DROP
XDP_REDIRECT
XDP_TX

On the other hand, the LSM Program is connected to a security hook and can be used to allow or block specific actions.

For example, access can be controlled based on policy at points such as file opening, process execution, and socket connection.

In other words, the eBPF Program Type is an important factor that determines the execution location and scope of authority of the eBPF Program.

Verifier

Verifier is a Kernel internal verifier that checks whether the eBPF Program is safe before being loaded into the Kernel.

Since the eBPF Program runs in Kernel Space, incorrect memory access, infinite loops, or incorrect use of pointers may affect the stability of the entire Kernel.

Therefore, the Kernel does not immediately execute the eBPF Program but first verifies it through the Verifier.

Verifier checks approximately the following contents.

  • Are there any incorrect memory accesses?
  • Ensure that uninitialized values are not used.
  • Are pointers used safely?
  • Make sure you do not call a helper function that is not allowed in the program type.
  • Is it within the stack range?
  • Can the program be terminated?
  • Is the context access correct?

For example, if an eBPF Program attempts to read a random Kernel address directly, it may be rejected by the Verifier.

int *ptr = (int *)0xffffffff81000000;
return *ptr;

The eBPF Program runs inside the Kernel, but it does not have unlimited access to Kernel memory like a typical Kernel Module.

The Verifier tracks the register status, stack status, branch flow, etc. of the eBPF Program and checks whether it is a safe program.

Because of this process, eBPF can provide a relatively safe execution model while running inside the Kernel.

JIT Compilation

The eBPF Program is initially loaded into the Kernel in the form of eBPF Bytecode.

Afterwards, the Kernel can interpret and execute the corresponding Bytecode, or convert it to Native Machine Code through JIT Compile and execute it.

JIT stands for Just-In-Time Compilation.

If eBPF JIT Compile is activated, eBPF Bytecode is converted to machine code suitable for the actual CPU architecture at the time of execution.

For example, in an x86-64 environment, eBPF Bytecode can be converted to x86-64 Machine Code.

This way, it can be executed faster than interpreting the bytecode every time.

In other words, eBPF runs inside the Kernel, but it can be seen as a structure that secures safety through Verifier and performance through JIT Compile.

eBPF Map

eBPF Map is a kernel internal data structure for sharing data between eBPF Program and User Space program.

Since eBPF Program cannot freely use global variables like a general program, it uses Map to save state or share data.

Map can be viewed as a Key-Value type storage.

For example, Map can be used to store the number of events for a specific PID, to store the number of packets for a specific IP, or to query policy information delivered from User Space in the Kernel.

Representative Map Types are as follows.

BPF_MAP_TYPE_HASH
BPF_MAP_TYPE_ARRAY
BPF_MAP_TYPE_PERCPU_HASH
BPF_MAP_TYPE_PERCPU_ARRAY
BPF_MAP_TYPE_LRU_HASH
BPF_MAP_TYPE_RINGBUF
BPF_MAP_TYPE_PERF_EVENT_ARRAY
BPF_MAP_TYPE_PROG_ARRAY
BPF_MAP_TYPE_LPM_TRIE

For example, Hash Map can be used to store the value for a specific key.

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key, __u32);
    __type(value, __u64);
} counter SEC(".maps");

The above Map is a Hash Map with a Key of type __u32 and a Value of type __u64.

In eBPF Program, Map is accessed through Helper Function.

__u64 *value;
__u32 key = 1;

value = bpf_map_lookup_elem(&counter, &key);

bpf_map_lookup_elem is a helper function that searches the value corresponding to a specific key in the map.

In other words, eBPF Map is the state storage of the eBPF Program and the communication path between User Space and Kernel Space.

Helper Function

Helper Function is a function provided by the Kernel so that the eBPF Program can use Kernel functions safely.

eBPF Program cannot freely call general C library functions.

For example, common functions such as printf, malloc, and open cannot be used inside an eBPF Program.

Instead, it performs limited functions through helper functions allowed by the kernel.

Representative helper functions are as follows.

bpf_map_lookup_elem
bpf_map_update_elem
bpf_map_delete_elem
bpf_ktime_get_ns
bpf_get_current_pid_tgid
bpf_get_current_comm
bpf_probe_read_kernel
bpf_probe_read_user
bpf_ringbuf_output
bpf_perf_event_output
bpf_tail_call

For example, to get PID of the current process, you can use the bpf_get_current_pid_tgid Helper Function.

__u64 pid_tgid = bpf_get_current_pid_tgid();

To get the current process name, you can use bpf_get_current_comm.

char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));

The types of helper functions that can be used vary depending on the program type.

For example, the Helper Function that can be used in the XDP Program and the Helper Function that can be used in the LSM Program may be different.

In other words, the Helper Function can be viewed as a limited API for the eBPF Program to interact with the Kernel.

Loader

Loader is a program that loads and attaches the eBPF Program from User Space to the Kernel.

The eBPF Program does not automatically load into the Kernel just by writing a C file.

In User Space, the Loader must read the eBPF Object File, create a Map, load the Program, and attach to the Hook Point.

Representative tools or libraries such as libbpf, BCC, and bpftrace can perform the loader role.

eBPF C Program
        ↓
Clang/LLVM Compile
        ↓
eBPF Object File
        ↓
User Space Loader
        ↓
bpf() syscall
        ↓
Kernel Verifier
        ↓
Attach to Hook Point

libbpf is a C library for loading and managing eBPF Program.

Recently, a method that uses BTF and CO-RE together to respond more flexibly to differences in kernel versions is widely used.

BTF and CO-RE

BTF is an abbreviation for BPF Type Format.

BTF is a metadata format that provides Kernel internal structure and type information so that the eBPF Program can utilize it.

eBPF Program often needs to refer to Kernel internal structures.

However, the field positions of the Kernel structure may vary depending on the kernel version or build settings.

For example, in some Kernel versions, certain fields of a structure may be at the front, while in other Kernel versions they may be at the back.

At this time, if the field offset is hard-coded, the eBPF Program may be broken when the kernel version changes.

CO-RE stands for Compile Once - Run Everywhere.

CO-RE is a method that uses BTF information to enable eBPF Program to be executed relatively flexibly in various Kernel versions.

In other words, BTF provides Kernel Type information, and CO-RE improves the portability of the eBPF Program by correcting differences in kernel versions based on that information.

Ring Buffer and Perf Buffer

eBPF Program often needs to transmit events that occur inside the Kernel to User Space.

For example, to check in User Space what processes were executed, what files were opened, and what network connections occurred, events must be passed from the Kernel to User Space.

Representative methods that can be used at this time are Ring Buffer and Perf Buffer.

Ring Buffer is a structure in which event data is recorded in the Kernel and the User Space program reads it.

struct event {
    __u32 pid;
    char comm[16];
};

eBPF Program can create an event structure and transmit it to Ring Buffer.

User Space Loader reads events while polling the Ring Buffer.

In other words, Ring Buffer is one of the main communication methods for delivering Kernel Events from eBPF-based monitoring tools to User Space.

XDP

XDP is the abbreviation for eXpress Data Path.

XDP is an eBPF Hook for processing network packets very early.

Generally, network packets come in from NIC and are delivered to the Socket through the Kernel Network Stack.

However, XDP can run the eBPF Program before the packet goes up to the Kernel Network Stack.

So XDP can be used for very fast packet filtering, DDoS defense, load balancing, packet redirection, etc.

XDP Program can make the following decisions about packets.

  • XDP_PASS: Pass the packet.
  • XDP_DROP: Drop the packet.
  • XDP_REDIRECT: Redirect packets to another interface or CPU.
  • XDP_TX: Send the packet back to the receiving interface.

For example, unnecessary costs can be reduced by immediately dropping packets with specific conditions rather than uploading them to the Kernel Network Stack.

For this reason, XDP is often mentioned in high-performance network processing.

TC eBPF

TC stands for Traffic Control.

TC eBPF is a method of processing packets by attaching an eBPF Program to the Linux Traffic Control layer.

XDP processes packets at a very early stage, and TC operates at a packet processing point a little later after entering the Kernel Network Stack.

TC eBPF can be used in both ingress and egress directions.

  • Ingress: Incoming packets
  • Egress: Outgoing packet

TC eBPF can be used for packet filtering, traffic control, packet modification, and network policy application.

If XDP is responsible for fast processing at a lower level, TC eBPF can control packets by utilizing a slightly richer Kernel Context.

Tracing

eBPF is often used to trace the behavior of kernels and applications.

Tracing is a technology that observes which functions are called within the system, which system calls occur, and which delays occur.

Hooks frequently used in eBPF Tracing are as follows.

kprobe
kretprobe
tracepoint
fentry
fexit
uprobe
uretprobe

kprobe is a method of attaching an eBPF Program to the kernel function entry point.

kretprobe is a method of attaching an eBPF Program to the kernel function return point.

Tracepoint is attached to a trace point stably provided by the kernel.

Uprobe is a method of attaching an eBPF Program to a function of a User Space program.

For example, it is possible to trace malloc calls of a specific program or check the number of calls to a specific library function using uprobe.

In other words, eBPF can be used to observe the execution flow of not only Kernel Space but also User Space programs.

eBPF LSM

eBPF LSM is a method of connecting the eBPF Program to the Linux Security Module Hook.

LSM is a framework for processing security decisions in the Linux Kernel.

LSM Hook is called at security-related points such as file access, process execution, permission checking, and socket connection.

Using eBPF LSM, you can allow or block actions based on policy by attaching an eBPF Program to these security decision points.

For example, when a specific process tries to open /etc/shadow, it can be blocked or the connection to a specific port can be denied.

SEC("lsm/file_open")
int BPF_PROG(check_file_open, struct file *file) {
    return 0;
}

You can connect the eBPF Program to the LSM Hook in the form above.

The LSM Program can be used to enforce security policies in that it does not simply observe but can reject specific actions through return values.

eBPF Use Cases

eBPF is used in various fields.

Representative application areas are as follows.

  • Observability
  • Networking
  • Security
  • Performance Profiling
  • Runtime Enforcement
  • Container Monitoring
  • Kubernetes Networking
  • DDoS Mitigation
  • System Call Tracing
  • File Access Monitoring

In the field of observability, it is used to track system calls, network connections, file access, CPU usage, function call delays, etc.

In the networking field, packet filtering, load balancing, network policy application, packet redirection, etc. can be performed using XDP, TC, etc.

In the security field, suspicious activities can be detected or blocked using eBPF LSM, kprobe, tracepoint, etc.

In a container environment, security monitoring at the container level can be performed by tracking process, network, and file access.

Differences Between eBPF and Kernel Modules

eBPF and Kernel Module both have something in common: they can extend Kernel functions.

However, the two methods differ in stability and development method.

Kernel Module is code that is loaded directly into the Kernel.

Kernel Modules are powerful, but if written incorrectly, they can cause Kernel Panic or make the entire system unstable.

On the other hand, the eBPF Program is inspected by the Verifier before being loaded into the Kernel.

Therefore, incorrect memory access, infinite loops, incorrect pointer use, etc. are restricted.

Additionally, eBPF’s execution location and functions are limited depending on Hook Point and Program Type.

In other words, Kernel Module is more free and powerful, but carries greater risks, and eBPF is more limited, but can be seen as a way to safely expand Kernel functions.

Limitations of eBPF

eBPF is not a technology that can do everything.

Because the eBPF Program runs inside the Kernel, several limitations exist.

Typical limitations are as follows:

  • Verifier must pass verification.
  • The helper functions that can be used are limited depending on the program type.
  • Random kernel memory cannot be freely accessed.
  • Common C library functions cannot be used.
  • Stack size is limited.
  • Complex looping or branching structures may be rejected by the Verifier.
  • Supported functions may vary depending on Kernel Version.

These restrictions may seem inconvenient, but they are necessary to maintain kernel stability.

Because eBPF runs inside the Kernel, the balance between performance and safety is important.

Why eBPF Matters

The reason eBPF is important is because it allows kernel-level observation and control without modifying the kernel.

Previously, to observe or control the internal operation of the kernel, you had to write a Kernel Module, patch the kernel, or use relatively heavy-duty methods such as ptrace.

However, using eBPF, you can collect necessary data or apply policies by dynamically connecting a small program to a specific point in the Kernel.

For example, you can check directly within the Kernel which process opened which file, which IP connected to, which system call was called, which function caused the delay, etc.

In addition, there is an advantage in terms of performance because network packets can be processed directly within the kernel without uploading them to the user space.

Overall eBPF Architecture

The overall structure of eBPF can be roughly viewed as follows.

User Space
    - eBPF Loader
    - Policy Manager
    - Event Reader
    - CLI / GUI
    - Metrics Collector

Kernel Space
    - eBPF Program
    - eBPF Map
    - Verifier
    - JIT Compiler
    - Hook Point
    - Helper Function

In User Space, the eBPF Program is loaded, the Map is managed, and events transmitted from the Kernel are read.

In Kernel Space, the eBPF Program runs at the Hook Point and saves data in the Map or calls the Helper Function.

These two interact through bpf() syscall, Map, Ring Buffer, etc.

In other words, eBPF is a structure in which the control program in User Space and the eBPF Program in Kernel Space operate together.

Summary

eBPF is a technology that dynamically expands kernel functions using a small program that runs safely inside the Linux Kernel.

Initially, BPF was a technology for network packet filtering, but eBPF expanded it to be used in various fields such as network, observability, security, and performance analysis.

The eBPF Program must be inspected by a verifier before being loaded into the Kernel, and after passing the verification, it is attached to a specific Hook Point and executed when an event occurs.

The core components of eBPF are as follows.

  • eBPF Program
  • Hook Point
  • Program Type
  • Verifier
  • JIT Compiler
  • Helper Function
  • eBPF Map
  • Loader
  • Ring Buffer
  • BTF / CO-RE