Skip to content

eBPF Notes

Published: at 04:08 PM

eBPF (Extended Berkeley Packet Filter)

eBPF makes the kernel programmable, so you don’t need to change the official linux kernel (not that easy 😄)

Table of Contents

Open Table of Contents

Hello World Programm:

#!/usr/bin/python3
from bcc import BPF

program =r"""
int hello(void *ctx) {
    bpf_trace_printk("Hello World!");
    return 0;
}
"""

b =BPF(text=program)
syscall =b.get_syscall_fnname("execve")
b.attach_kprobe(event=syscall,fn_name="hello")

b.trace_print()

The Programm here gets compiled in Python, load into the kernel, and attached to a kprobe that will be hit whenever the execve system call runs. (execve system call is used to execute a program) This is the actual cBPF code:

int hello(void *ctx) {
    bpf_trace_printk("Hello World!");
    return 0;
}

This looks like a C function called hello(). Execute the programm like this:

./hello.py

Now if you go into another terminal and run a command you can see this output:

alt text

eBPF Maps

eBPF Maps are data structures that can be accessed from within eBPF programs in the kernel, and from user space applications. They can be used to share information between eBPF programs and with user space code - for example, to pass configuration into an eBPF program, or to send observability data collected in the kernel to user space.

alt text

Maps attributes:

Create a map:

Program creates a hash table, which stores key-value pairs:

BPF_HASH(counter_table);

In the user space code we can access it like this:

while True:
    sleep(2)
    s = ""
    for k,v in b["counter_table"].items():
        s += f"ID {k.value}: {v.value}\t"
    print(s)

Maps usecase:

#!/usr/bin/python3
from bcc import BPF
from time import sleep

program = r"""
BPF_HASH(counter_table);

int hello(void *ctx) {
   u64 uid;
   u64 counter = 0;
   u64 *p;

   uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
   p = counter_table.lookup(&uid);
   if (p != 0) {
      counter = *p;
   }
   counter++;
   counter_table.update(&uid, &counter);
   return 0;
}
"""

b = BPF(text=program)
syscall = b.get_syscall_fnname("execve")
b.attach_kprobe(event=syscall, fn_name="hello")

# Attach to a tracepoint that gets hit for all syscalls
# b.attach_raw_tracepoint(tp="sys_enter", fn_name="hello")

while True:
    sleep(2)
    s = ""
    for k,v in b["counter_table"].items():
        s += f"ID {k.value}: {v.value}\t"
    print(s)

Get user ID and group ID with helper function:

uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;

Lookup in the counter_table map to see if there is already an entry with the key that matches this user ID:

p = counter_table.lookup(&uid);

Counter gets incremented and the key-value pair (uid-counter) gets written to the map.

counter++;
counter_table.update(&uid, &counter);

eBPF in k8s