Skip to content

Latest commit

Β 

History

History
661 lines (540 loc) Β· 16.8 KB

File metadata and controls

661 lines (540 loc) Β· 16.8 KB

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

u64 timestamp = bpf_ktime_get_ns();
// timestamp contains nanoseconds since boot

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

u64 boot_time = bpf_ktime_get_boot_ns();
// Includes time spent in suspend

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

// After filling reserved data
bpf_ringbuf_submit(data, 0);
// Data is now available to userspace

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

u32 cpu = bpf_get_smp_processor_id();
// cpu contains current CPU number (0-based)

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! πŸ“š