API Reference¶
Complete reference for eBPF helper functions, data structures, and APIs used in this project.
🔧 eBPF Helper Functions¶
Process Information¶
bpf_get_current_pid_tgid()¶
Signature: u64 bpf_get_current_pid_tgid(void)
Returns: Combined PID/TGID value (upper 32 bits: TGID, lower 32 bits: PID)
Available since: Linux 3.19
u64 pid_tgid = bpf_get_current_pid_tgid();
u32 pid = pid_tgid & 0xFFFFFFFF; // Process ID
u32 tgid = pid_tgid >> 32; // Thread Group ID
bpf_get_current_uid_gid()¶
Signature: u64 bpf_get_current_uid_gid(void)
Returns: Combined UID/GID value (upper 32 bits: GID, lower 32 bits: UID)
Available since: Linux 4.2
u64 uid_gid = bpf_get_current_uid_gid();
u32 uid = uid_gid & 0xFFFFFFFF; // User ID
u32 gid = uid_gid >> 32; // Group ID
bpf_get_current_comm()¶
Signature: long bpf_get_current_comm(void *buf, u32 size_of_buf)
Parameters:
- buf: Buffer to store command name
- size_of_buf: Size of buffer (typically 16)
Returns: 0 on success, negative on error
Available since: Linux 3.19
char comm[16];
if (bpf_get_current_comm(&comm, sizeof(comm)) == 0) {
// comm now contains process name
}
bpf_get_current_task()¶
Signature: u64 bpf_get_current_task(void)
Returns: Pointer to current task_struct
Available since: Linux 4.8
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// Use with bpf_probe_read_kernel() to access fields safely
Memory Access¶
bpf_probe_read_kernel()¶
Signature: long bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)
Parameters:
- dst: Destination buffer
- size: Number of bytes to read
- unsafe_ptr: Source kernel pointer
Returns: 0 on success, negative on error
Available since: Linux 5.5
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
u32 pid;
if (bpf_probe_read_kernel(&pid, sizeof(pid), &task->pid) == 0) {
// pid is now safely read from kernel memory
}
bpf_probe_read_user()¶
Signature: long bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr)
Parameters:
- dst: Destination buffer
- size: Number of bytes to read
- unsafe_ptr: Source user pointer
Returns: 0 on success, negative on error
Available since: Linux 5.5
char user_buffer[256];
char *user_ptr = (char *)ctx->args[1];
if (bpf_probe_read_user(&user_buffer, sizeof(user_buffer), user_ptr) == 0) {
// user_buffer contains data from user space
}
bpf_probe_read_user_str()¶
Signature: long bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr)
Parameters:
- dst: Destination buffer
- size: Maximum bytes to read
- unsafe_ptr: Source user string pointer
Returns: String length on success (including null terminator), negative on error
Available since: Linux 4.11
char filename[256];
char *user_filename = (char *)ctx->args[1];
long len = bpf_probe_read_user_str(&filename, sizeof(filename), user_filename);
if (len > 0) {
// filename contains null-terminated string
}
Time Functions¶
bpf_ktime_get_ns()¶
Signature: u64 bpf_ktime_get_ns(void)
Returns: Current kernel time in nanoseconds since boot
Available since: Linux 4.1
bpf_ktime_get_boot_ns()¶
Signature: u64 bpf_ktime_get_boot_ns(void)
Returns: Current time in nanoseconds since boot (including suspend time)
Available since: Linux 5.8
Map Operations¶
bpf_map_lookup_elem()¶
Signature: void *bpf_map_lookup_elem(void *map, const void *key)
Parameters:
- map: Map to lookup in
- key: Key to search for
Returns: Pointer to value if found, NULL if not found
Available since: Linux 3.19
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 1024);
} counters SEC(".maps");
u32 pid = 1234;
u64 *counter = bpf_map_lookup_elem(&counters, &pid);
if (counter) {
(*counter)++;
}
bpf_map_update_elem()¶
Signature: long bpf_map_update_elem(void *map, const void *key, const void *value, u64 flags)
Parameters:
- map: Map to update
- key: Key to update
- value: New value
- flags: Update flags (BPF_ANY, BPF_NOEXIST, BPF_EXIST)
Returns: 0 on success, negative on error
Available since: Linux 3.19
u32 pid = 1234;
u64 count = 1;
long ret = bpf_map_update_elem(&counters, &pid, &count, BPF_ANY);
if (ret == 0) {
// Update successful
}
bpf_map_delete_elem()¶
Signature: long bpf_map_delete_elem(void *map, const void *key)
Parameters:
- map: Map to delete from
- key: Key to delete
Returns: 0 on success, negative on error
Available since: Linux 3.19
u32 pid = 1234;
long ret = bpf_map_delete_elem(&counters, &pid);
if (ret == 0) {
// Key deleted successfully
}
Ring Buffer Operations¶
bpf_ringbuf_reserve()¶
Signature: void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags)
Parameters:
- ringbuf: Ring buffer map
- size: Size to reserve
- flags: Reservation flags (usually 0)
Returns: Pointer to reserved space, NULL on failure
Available since: Linux 5.8
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} events SEC(".maps");
struct event_data *data = bpf_ringbuf_reserve(&events, sizeof(*data), 0);
if (data) {
// Fill data structure
data->pid = bpf_get_current_pid_tgid() & 0xFFFFFFFF;
// Must call bpf_ringbuf_submit() or bpf_ringbuf_discard()
}
bpf_ringbuf_submit()¶
Signature: void bpf_ringbuf_submit(void *data, u64 flags)
Parameters:
- data: Data pointer from bpf_ringbuf_reserve()
- flags: Submission flags (usually 0)
Available since: Linux 5.8
bpf_ringbuf_discard()¶
Signature: void bpf_ringbuf_discard(void *data, u64 flags)
Parameters:
- data: Data pointer from bpf_ringbuf_reserve()
- flags: Discard flags (usually 0)
Available since: Linux 5.8
// If you decide not to submit the reserved data
bpf_ringbuf_discard(data, 0);
// Reserved space is freed without sending to userspace
Utility Functions¶
bpf_get_smp_processor_id()¶
Signature: u32 bpf_get_smp_processor_id(void)
Returns: Current CPU number
Available since: Linux 4.1
bpf_trace_printk()¶
Signature: long bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
Parameters:
- fmt: Format string
- fmt_size: Length of format string
- ...: Arguments
Returns: Number of bytes written, negative on error
Available since: Linux 4.1
Note: For debugging only, not for production
bpf_trace_printk("Process %d opened file\n", 25, pid);
// Output appears in /sys/kernel/debug/tracing/trace_pipe
📊 eBPF Data Structures¶
Common Event Structures¶
Basic Process Event¶
struct process_event {
u32 pid; // Process ID
u32 ppid; // Parent process ID
u32 uid; // User ID
u32 gid; // Group ID
char comm[16]; // Process name (kernel limit)
u64 timestamp; // Event timestamp (nanoseconds)
} __attribute__((packed));
File Operation Event¶
struct file_event {
u32 pid; // Process ID performing operation
u32 fd; // File descriptor (if applicable)
u32 flags; // Open flags
char comm[16]; // Process name
char filename[256]; // File path
u64 timestamp; // Event timestamp
} __attribute__((packed));
Network Event¶
struct network_event {
u32 pid; // Process ID
u32 src_ip; // Source IP (network byte order)
u32 dst_ip; // Destination IP (network byte order)
u16 src_port; // Source port (network byte order)
u16 dst_port; // Destination port (network byte order)
u8 protocol; // Protocol (TCP=6, UDP=17)
char comm[16]; // Process name
u64 timestamp; // Event timestamp
} __attribute__((packed));
Map Definitions¶
Hash Map¶
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32); // Key type
__type(value, struct counter); // Value type
__uint(max_entries, 10000); // Maximum entries
__uint(map_flags, BPF_F_NO_PREALLOC); // Optional flags
} process_counters SEC(".maps");
Array Map¶
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32); // Index (0 to max_entries-1)
__type(value, u64); // Value type
__uint(max_entries, 256); // Array size
} statistics SEC(".maps");
Ring Buffer Map¶
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24); // 16MB buffer
} events SEC(".maps");
Per-CPU Array¶
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 1); // One entry per CPU
} per_cpu_stats SEC(".maps");
Program Section Names¶
Tracepoint Programs¶
SEC("tracepoint/syscalls/sys_enter_openat") // System call entry
SEC("tracepoint/syscalls/sys_exit_openat") // System call exit
SEC("tracepoint/sched/sched_process_exec") // Process execution
SEC("tracepoint/sched/sched_process_exit") // Process exit
SEC("tracepoint/ext4/ext4_free_inode") // Filesystem events
Kprobe Programs¶
SEC("kprobe/do_sys_openat2") // Kernel function entry
SEC("kretprobe/do_sys_openat2") // Kernel function return
SEC("kprobe/vfs_open") // VFS layer function
Network Programs¶
SEC("xdp") // XDP program
SEC("tc") // Traffic control
SEC("socket") // Socket filter
SEC("sk_msg") // Socket message
🔗 Go eBPF Library (Cilium)¶
Loading Programs¶
Basic Loading¶
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target native program ../bpf/program.c
func loadProgram() error {
objs := programObjects{}
if err := loadProgramObjects(&objs, nil); err != nil {
return fmt.Errorf("loading eBPF objects: %w", err)
}
defer objs.Close()
// Use objs.ProgramName and objs.MapName
return nil
}
Advanced Loading Options¶
func loadProgramWithOptions() error {
spec, err := loadProgramSpecs()
if err != nil {
return err
}
// Customize before loading
spec.Maps["events"].MaxEntries = 32 * 1024 * 1024 // 32MB ring buffer
coll, err := ebpf.NewCollection(spec)
if err != nil {
return err
}
defer coll.Close()
return nil
}
Map Operations¶
Reading from Maps¶
func readFromMap(m *ebpf.Map) error {
var key uint32 = 1234
var value uint64
if err := m.Lookup(key, &value); err != nil {
if errors.Is(err, ebpf.ErrKeyNotExist) {
// Key doesn't exist
return nil
}
return fmt.Errorf("map lookup: %w", err)
}
fmt.Printf("Value for key %d: %d\n", key, value)
return nil
}
Writing to Maps¶
func writeToMap(m *ebpf.Map) error {
var key uint32 = 1234
var value uint64 = 5678
if err := m.Update(key, value, ebpf.UpdateAny); err != nil {
return fmt.Errorf("map update: %w", err)
}
return nil
}
Iterating Maps¶
func iterateMap(m *ebpf.Map) error {
var key uint32
var value uint64
iter := m.Iterate()
for iter.Next(&key, &value) {
fmt.Printf("Key: %d, Value: %d\n", key, value)
}
return iter.Err()
}
Ring Buffer Operations¶
Reading Events¶
func readRingBuffer(rb *ebpf.Map) error {
reader, err := ringbuf.NewReader(rb)
if err != nil {
return err
}
defer reader.Close()
for {
record, err := reader.Read()
if err != nil {
if errors.Is(err, ringbuf.ErrClosed) {
break
}
return err
}
// Process record.RawSample
var event ProcessEvent
if err := binary.Read(bytes.NewReader(record.RawSample),
binary.LittleEndian, &event); err != nil {
continue
}
fmt.Printf("Event: PID=%d, Comm=%s\n",
event.PID, nullTerminatedString(event.Comm[:]))
}
return nil
}
Program Attachment¶
Tracepoint Attachment¶
func attachTracepoint(prog *ebpf.Program) error {
l, err := link.Tracepoint("syscalls", "sys_enter_openat", prog, nil)
if err != nil {
return fmt.Errorf("attaching tracepoint: %w", err)
}
defer l.Close()
// Keep program running
return nil
}
Kprobe Attachment¶
func attachKprobe(prog *ebpf.Program) error {
l, err := link.Kprobe("do_sys_openat2", prog, nil)
if err != nil {
return fmt.Errorf("attaching kprobe: %w", err)
}
defer l.Close()
return nil
}
🔧 Utility Functions¶
String Processing¶
// Convert null-terminated byte array to Go string
func nullTerminatedString(b []byte) string {
for i, c := range b {
if c == 0 {
return string(b[:i])
}
}
return string(b)
}
// Convert Go string to fixed-size byte array
func stringToBytes(s string, size int) []byte {
b := make([]byte, size)
copy(b, s)
return b
}
Time Conversion¶
// Convert eBPF timestamp (nanoseconds since boot) to Go time
func ebpfTimeToGoTime(ns uint64) time.Time {
// This is approximate - real implementation would need boot time
return time.Unix(0, int64(ns))
}
// Get current timestamp in eBPF format
func currentEBPFTime() uint64 {
return uint64(time.Now().UnixNano())
}
Network Byte Order¶
// Convert network byte order to host byte order
func ntohl(n uint32) uint32 {
return binary.BigEndian.Uint32((*[4]byte)(unsafe.Pointer(&n))[:])
}
func ntohs(n uint16) uint16 {
return binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&n))[:])
}
// Convert host byte order to network byte order
func htonl(h uint32) uint32 {
b := make([]byte, 4)
binary.BigEndian.PutUint32(b, h)
return *(*uint32)(unsafe.Pointer(&b[0]))
}
func htons(h uint16) uint16 {
b := make([]byte, 2)
binary.BigEndian.PutUint16(b, h)
return *(*uint16)(unsafe.Pointer(&b[0]))
}
📝 Error Codes¶
Common eBPF Errors¶
| Error Code | Description | Common Causes |
|---|---|---|
-EACCES |
Permission denied | Missing capabilities, wrong program type |
-EINVAL |
Invalid argument | Invalid program, map type mismatch |
-E2BIG |
Program too large | Exceeds instruction or complexity limits |
-ENOENT |
No such entry | Map key doesn't exist, attachment point invalid |
-EEXIST |
Entry exists | BPF_NOEXIST flag with existing key |
-ENOMEM |
Out of memory | Map full, ring buffer full |
-EPERM |
Operation not permitted | Insufficient privileges |
Verifier Errors¶
- R1 invalid mem access: Direct memory access without helper
- invalid indirect read from stack: Uninitialized stack access
- back-edge from insn X to Y: Unbounded loop detected
- unreachable insn: Dead code after terminating instruction
🔍 Debugging Commands¶
bpftool Commands¶
# List programs and maps
sudo bpftool prog list
sudo bpftool map list
# Show program details
sudo bpftool prog show id <id>
sudo bpftool map show id <id>
# Dump program instructions
sudo bpftool prog dump xlated id <id>
sudo bpftool prog dump jited id <id>
# Dump map contents
sudo bpftool map dump id <id>
# Program statistics
sudo bpftool prog show id <id> --json | jq '.run_cnt, .run_time_ns'
Trace Commands¶
# View debug prints
sudo cat /sys/kernel/debug/tracing/trace_pipe
# Clear trace buffer
echo > /sys/kernel/debug/tracing/trace
# Enable/disable tracing
echo 1 > /sys/kernel/debug/tracing/tracing_on
echo 0 > /sys/kernel/debug/tracing/tracing_on
This API reference provides comprehensive documentation for all the key functions and data structures used in eBPF development. Use it as a quick reference when building your own eBPF tools! 📚