Skip to main content

What is eBPF?

eBPF (extended Berkeley Packet Filter) is a revolutionary Linux kernel technology that allows programs to run in kernel space without requiring kernel modules or modifications. Originally designed for packet filtering, eBPF has evolved into a general-purpose execution engine for safe, efficient kernel instrumentation.
The eBPF Event Interceptor uses eBPF to monitor TCP and UDP network activity with minimal overhead and no kernel modifications.

Why eBPF for Network Monitoring?

Traditional Approaches vs eBPF

Kernel Modules

Risks:
  • Can crash entire system
  • No safety verification
  • Complex to maintain
  • Security vulnerabilities
Benefits:
  • Full kernel access
  • Maximum flexibility

eBPF Programs

Benefits:
  • Verified safe before loading
  • Cannot crash kernel
  • Sandboxed execution
  • No recompilation needed
Constraints:
  • Limited instruction set
  • Stack size limits
  • Bounded loops only

Key Advantages

  1. Safety: eBPF verifier ensures programs terminate and don’t crash the kernel
  2. Performance: Runs in kernel context without context switching
  3. Flexibility: Load/unload programs dynamically without rebooting
  4. Observability: Zero application code changes required
  5. Security: Cannot access arbitrary kernel memory

BCC: BPF Compiler Collection

This project uses BCC (BPF Compiler Collection) to simplify eBPF program development.
BCC version is displayed at startup:

BCC Components

BCC API Usage

1. Initialize BPF Program:
2. Attach Kprobe:
3. Open Perf Buffer:
4. Poll for Events:
5. Cleanup:

Kprobes: Dynamic Kernel Instrumentation

What are Kprobes?

Kprobes allow eBPF programs to execute when specific kernel functions are called. Two types exist:
  • kprobe: Executes at function entry (before function runs)
  • kretprobe: Executes at function return (after function completes)
Kprobes attach to kernel function names, which can change between kernel versions. This implementation targets common functions stable across kernel 4.x-5.x+.

TCP Kprobes

The TCP interceptor attaches to a single critical function:
Why tcp_set_state? This function is called whenever a TCP connection changes state (SYN_SENT → ESTABLISHED → FIN_WAIT1 → etc.). It provides:
  • Connection establishment events
  • Connection teardown events
  • Access to struct sock* containing all connection metadata
TCP State Machine:

UDP Kprobes

UDP is connectionless, so the interceptor tracks operations across multiple functions:
Send Operation:
Receive Operation (Return Probe):

Kprobe Attachment Process

From setupBPF() in udpTracer.cc:394-471:

eBPF Program Structure

TCP eBPF Program

The TCP eBPF code is loaded from external source (passed to setupBPF()). Key elements:

UDP eBPF Program

The UDP program is embedded directly in udpTracer.cc:58-386:
The UDP program uses BPF hash maps (BPF_HASH) to maintain state across multiple kprobe invocations, tracking per-socket statistics.

eBPF Helper Functions

eBPF programs can only call approved helper functions:

Context Helpers

Memory Access Helpers

Direct pointer dereferencing is only allowed for kernel structures passed as arguments. Use bpf_probe_read_kernel() for nested structures to avoid verifier errors.

Perf Buffer Helpers

Map Operations

Safety and Verification

eBPF Verifier Checks

Before loading, the kernel’s eBPF verifier ensures:
  1. Bounded Execution: All loops have maximum iteration limits
  2. Memory Safety: All memory accesses are validated
  3. Null Pointer Checks: Pointers verified before dereferencing
  4. Stack Limits: Stack usage < 512 bytes
  5. Instruction Limits: Program < 1 million instructions
  6. No Infinite Loops: All code paths reach exit
If verification fails, bpf.init() returns an error with the verifier’s detailed analysis.

What eBPF Cannot Do

  • ❌ Modify kernel code or data structures
  • ❌ Call arbitrary kernel functions
  • ❌ Access user space memory directly
  • ❌ Use floating point operations
  • ❌ Create unbounded loops
  • ❌ Allocate dynamic memory

What eBPF Can Do

  • ✅ Read kernel structures (read-only)
  • ✅ Write to maps and perf buffers
  • ✅ Call approved helper functions
  • ✅ Make bounded decisions
  • ✅ Aggregate statistics
  • ✅ Filter events

Performance Characteristics

Overhead Analysis

Kprobe Overhead

  • Entry: ~1-2μs
  • Return: ~2-3μs
  • Per packet cost: minimal

CPU Impact

  • Idle system: < 0.1%
  • Busy system: 1-3%
  • No network slowdown

Memory Usage

  • eBPF maps: ~100KB
  • Perf buffers: ~2MB
  • Queue: ~1MB (1024 events)

JIT Compilation

Modern kernels compile eBPF bytecode to native machine code:
JIT compilation reduces eBPF instruction execution time by 3-5x.

Loading and Attaching eBPF Programs

Complete Initialization Flow

Error Handling

Every BCC operation returns a status object:
eBPF operations require CAP_BPF (kernel 5.8+) or CAP_SYS_ADMIN. Run with sudo or grant capabilities:

Memory Cleanup

Freeing BCC Resources

From event.cc:206-210:
free_bcc_memory() releases LLVM compiler resources (~50-100MB) after program loading. This is safe because the compiled eBPF bytecode is already in the kernel.

Next Steps

Event Collection

Learn about event data structures and collection mechanics

Architecture

Review the complete system architecture