Check out our latest blog: "eBPF Decoded: Use Cases, Concepts, and Architecture"

Learn More
logo
left-arrowBlog

eBPF Decoded: Use Cases, Concepts, and Architecture

eBPF Decoded: Use Cases, Concepts, and Architecture
Bhaskar Ganji
Bhaskar Ganji

Head of Engineering

August 13, 2024

What Is eBPF?

The Linux kernel is integral to networking, observability, and security, but its complexity often poses challenges for developers. Traditionally, adding modules or modifying kernel source code to implement new features could be cumbersome and risky. The Extended Berkeley Packet Filter (eBPF) offers a modern solution by enabling developers to run programs without altering the kernel or adding new modules.

eBPF, available since Linux kernel 4.4, functions as a lightweight, sandboxed virtual machine (VM) within the kernel. It allows the execution of Berkeley Packet Filter (BPF) bytecode, leveraging kernel resources to perform various tasks. By eliminating the need to modify kernel source code, eBPF enhances the software's ability to interact with existing kernel layers, significantly impacting how services such as observability, security, and networking are delivered.

eBPF Use Cases

1. Security

eBPF enhances system security by providing comprehensive visibility into system calls, packet operations, and socket-level activities. It allows the development of advanced security mechanisms that integrate control over various system components. Unlike traditional methods, which often require separate systems for different security aspects, eBPF provides a unified framework, improving context-awareness and control.

2. Networking

Due to its efficiency and programmability, eBPF is a strong candidate for packet processing in networking solutions. It allows for the dynamic addition of protocol parsers and the programming of forwarding logic within the kernel. The Just-In-Time (JIT) compiler ensures that eBPF programs execute with performance levels close to natively compiled kernel code, making it ideal for high-performance networking applications.

3. Tracing and Profiling

eBPF can attach programs to trace points, kernel functions, and user application probe points, offering detailed visibility into the runtime behavior of applications and systems. This capability is crucial for diagnosing system performance issues. eBPF supports advanced statistical data structures, enabling efficient data collection without the overhead typically associated with large-scale data sampling.

4. Observability and Monitoring

eBPF allows for real-time observability by generating visibility events and collecting custom metrics directly within the kernel. This capability reduces system overhead by focusing on collecting only necessary data and processing it at the source. As a result, eBPF significantly enhances the depth of system monitoring while minimizing performance impacts, making it a powerful tool for observability.

User Space and Kernel Space

How eBPF Works?

eBPF programs interact with the Linux kernel to access hardware and perform tasks such as debugging, tracing, and networking. The technology was inspired by trace, a dynamic tracing tool for BSD and Solaris, and evolved to overcome the limitations of the original BPF, which was primarily used for packet filtering.

The development of eBPF involved expanding BPF's capabilities, eventually leading to its full-fledged implementation in Linux 4.4. eBPF programs must pass a series of checks before they are loaded into the kernel. These checks, performed by a verifier, ensure that the program will not cause system instability. The verifier checks for issues like infinite loops, improper memory access, and other potential system crashes.

Once verified, eBPF programs are compiled into the kernel, ready to execute in response to specific events. These programs can efficiently collect and process data according to predefined rules, making them invaluable for a wide range of applications. Here's a breakdown of how eBPF functions:

  1. Program Creation and Compilation:
    • eBPF programs are typically written in a subset of the C language and compiled into bytecode using tools like LLVM. This bytecode is then loaded into the kernel.
    • Developers can also use higher-level abstractions, such as bpf trace or Cilium, to define eBPF programs without writing low-level code directly.
  2. Program Loading and Verification:
    • Once compiled, the eBPF program is loaded into the kernel using the bpf() system call.
    • The kernel includes an eBPF verifier, which performs a series of checks on the program to ensure it is safe to execute. These checks prevent issues such as infinite loops and ensure memory safety.
  3. Execution Triggers:
    • eBPF programs are executed in response to specific events, known as hooks. These hooks can be predefined kernel events such as network packet arrival, system calls, function entry/exit, or custom user-space events.
    • When an event occurs, the corresponding eBPF program is triggered and executed within the kernel context.
  4. Helper Functions and Maps:
    • eBPF programs can interact with kernel resources and maintain state through helper functions and eBPF maps.
    • Helper functions are predefined API calls provided by the kernel, allowing eBPF programs to perform actions such as manipulating packets, generating random numbers, or accessing kernel data structures.
    • eBPF maps are data structures that store and share data between eBPF programs and user-space applications. They can be used to maintain state, aggregate data, or pass information.
  5. Safety and Isolation:
    • eBPF runs within a restricted environment, ensuring that it cannot perform unsafe operations. This isolation protects the kernel and the system from potential security vulnerabilities introduced by custom code.
  6. JIT Compilation:
    • To enhance performance, eBPF bytecode can be Just-In-Time (JIT) compiled into native machine code. This compilation allows eBPF programs to run at near-native speeds, providing efficient and low-latency execution.

How Are eBPF Programs Written?

eBPF (Extended Berkeley Packet Filter) programs are written and executed within the Linux kernel to perform tasks like monitoring, security enforcement, and networking. Here's an overview of how these programs are created:

  1. Writing the Program:
    • Language and Syntax: eBPF programs are typically written in a restricted subset of the C programming language, known as "pseudo-C." This subset is designed to ensure safety and simplicity, avoiding complex features like loops with unknown termination conditions.
    • Tools and Frameworks: Developers often use frameworks and tools like BPF Compiler Collection (BCC), bpftrace, or Cilium to write eBPF programs. These tools provide higher-level abstractions and utilities, making it easier to develop and manage eBPF code.
  2. Compilation:
    • Bytecode Compilation: Once written, the eBPF program is compiled into bytecode using a compiler suite like LLVM (Low-Level Virtual Machine). This bytecode is the intermediate representation that the kernel can execute.
    • Verifier Compliance: The resulting bytecode must comply with the strict rules of the eBPF verifier, ensuring that it will not compromise kernel stability or security.
  3. Loading the Program:
    • System Call (bpf()): The compiled eBPF bytecode is loaded into the kernel using the bpf() system call. This system call attaches the eBPF program to specific hook points in the kernel, such as network events, tracepoints, or system calls.
    • Verification: During the loading process, the kernel's eBPF verifier checks the program for safety, ensuring it cannot perform unsafe operations, such as dereferencing invalid pointers or causing infinite loops.
  4. Helper Functions and Maps:
    • Helper Functions: eBPF programs can use a set of predefined helper functions provided by the kernel. These functions perform tasks like manipulating network packets, accessing kernel data, or interacting with eBPF maps.
    • eBPF Maps: Maps are special data structures used to store state and share data between the eBPF program and user-space applications. They can hold various types of data, such as arrays, hash tables, or ring buffers.
  5. Testing and Debugging:
    • Debugging Tools: Developers use tools like bpftool, bpftrace, and perf to debug and analyze eBPF programs. These tools help in inspecting the program's behavior, monitoring performance, and ensuring correctness.
    • Testing: Thorough testing is crucial, as eBPF programs operate in a sensitive kernel space. Developers must ensure that their programs behave as expected and do not cause system instability.
  6. Deployment:
    • Execution in Kernel Space: Once deployed, eBPF programs run in response to the specified events (hooks) in the kernel. They can operate on data in real-time, making decisions or collecting metrics based on their logic.

Workflow of an eBPF Program

Some Helpful eBPF Basic Concepts

Predefined Hooks

Hooks are points in the Linux kernel where eBPF programs can attach to intercept and process events. There are several types of hooks available, each corresponding to different kernel subsystems:

  • Tracepoints: These hooks are pre-defined points in the kernel that are useful for monitoring system events like scheduling, I/O, or system calls.
  • Kprobes and Uprobes: Kprobes allow eBPF programs to attach to and trace kernel functions, while uprobes are used for tracing user-space functions.
  • Network Hooks: These include points in the networking stack, such as XDP (eXpress Data Path) for high-performance packet processing and TC (Traffic Control) for network traffic management.
Program Verification

Before an eBPF program is loaded into the kernel, it must be verified for safety. This verification process ensures that:

  • Only privileged processes can load the program unless specified otherwise.
  • The program will not cause harm or crash the system.
  • The program will always terminate, avoiding infinite loops.
eBPF Maps

eBPF programs use maps to store and share data. Maps are versatile data structures that can be accessed by both eBPF programs and user-space applications through system calls. They can hold various types of data, such as hash tables, arrays, ring buffers, and more, enabling efficient data storage and retrieval.

Helper Calls

eBPF programs cannot directly call arbitrary kernel functions. Instead, they use helper functions provided by the kernel, which act as a stable API layer. These helper functions allow eBPF programs to perform tasks like generating random numbers, accessing maps, and manipulating network packets.

Function and Tail Calls

Function calls within eBPF programs allow for modular code design, while tail calls enable. This modularity and composability make eBPF programs flexible and powerful, allowing for complex data processing and decision-making workflows.

eBPF Architecture

The architecture of eBPF is designed to provide a flexible and efficient framework for executing user-defined programs in the kernel space. It comprises several key components:

eBPF Architecture

  1. User Space: In the user space, developers write eBPF programs using a restricted subset of the C language. These programs are compiled into eBPF bytecode using tools like LLVM. User-space applications can interact with the kernel's eBPF subsystem via system calls, such as bpf(), to load programs, create maps, and retrieve data.
  2. Kernel Space: Within the kernel, eBPF programs are executed in a VM that enforces strict safety and performance constraints. The kernel includes various hooks and subsystems where eBPF programs can be attached. This integration allows eBPF to monitor, modify, and react to events in real-time, providing capabilities like packet filtering, performance monitoring, and security policy enforcement.
  3. Control Plane and Maps: Maps serve as the control plane in the eBPF architecture. They enable communication between eBPF programs and user-space applications, facilitating tasks such as data collection, configuration, and command execution. Maps are highly flexible and can store various data types, supporting efficient lookups and updates.
  4. Extensibility and Safety: The design of eBPF prioritizes extensibility and safety. The verifier ensures that only safe and efficient programs are executed, protecting the kernel from potentially harmful operations. This safety mechanism, combined with the flexibility to extend kernel functionality without modifying the kernel code, makes eBPF a powerful tool for system administrators and developers.

What is eBPF XDP?

eBPF eXpress Data Path (XDP) is a high-performance packet processing framework within the Linux kernel, utilizing eBPF technology. XDP enables efficient processing of network packets at the earliest point in the kernel's networking stack, right after they are received by the network interface card (NIC). By leveraging eBPF programs, XDP can perform various operations, such as filtering, redirecting, or modifying packets, directly in the network driver.

Key Features of eBPF XDP:

  1. High Speed: XDP offers extremely low-latency packet processing, as it operates closer to the hardware level, bypassing several layers of the networking stack.
  2. Programmability: eBPF allows developers to write custom programs for packet processing tasks, which can be dynamically loaded and executed by the XDP framework.
  3. Flexibility: XDP can be used for a wide range of use cases, including DDoS protection, load balancing, network monitoring, and firewalling.
  4. Efficiency: By handling packets at the ingress point, XDP reduces the workload on higher layers of the networking stack, improving overall system performance and resource utilization.

In summary, eBPF XDP provides a powerful and flexible mechanism for high-speed packet processing, making it a valuable tool for network administrators and developers seeking to optimize network performance and security.

Conclusion

eBPF provides a revolutionary way to safely execute custom bytecode within the Linux kernel, offering enhanced capabilities in observability, security, and networking. It allows developers to write efficient programs that interact closely with kernel resources, without the need for modifying the kernel source code. While eBPF does not entirely replace Linux Loadable Kernel Modules (LKMs), it offers a safer and more versatile alternative for introducing custom functionalities related to protected hardware resources. With its robust security model and extensive use cases, eBPF is set to become a cornerstone technology in modern Linux-based systems. It enables deep visibility into system behavior, efficient packet processing, and dynamic tracing, all while maintaining a high level of safety. As a result, eBPF has become an invaluable tool for system administrators, developers, and security professionals.

If you're interested in how KubeSense has built instant observability using eBPF and the latest technology to capture petabytes of data, process it at lightning speed, and make it available for business metrics and ad-hoc querying, you must try it for yourself. Our agentless approach utilizes eBPF for application performance monitoring, eliminating the need for expensive and intrusive instrumentation. Schedule a demo to experience it firsthand and learn more about how KubeSense can revolutionize your observability needs.


Tags

eBPF
KubeSense
Observability
eBPF Observability
Kernel Programming
Agent-less Observability
Agentless Monitoring
Application Performance Monitoring
AI and LLM Powered Observability
Security
Agentless Monitoring
Tracing And Profiling
RealTime Observability
DynamicTracing