Skip to content

Solnix Compiler - eBPF Support Details

Supported eBPF Program Types

Networking Programs

XDP (eXpress Data Path)

  • Section: xdp
  • Context: struct xdp_md *ctx
  • Return Values: XDP_PASS (2), XDP_DROP (1), XDP_TX (3), XDP_ABORTED (0), XDP_REDIRECT (4)
  • Use Case: Fast packet processing at the network driver level

TC (Traffic Control)

  • Sections:
  • tc / classifier - Classic TC classifier
  • tcx/ingress / tc/ingress - TCX ingress hook
  • tcx/egress / tc/egress - TCX egress hook
  • Context: struct __sk_buff *ctx
  • Return Values: TC_ACT_OK (0), TC_ACT_SHOT (2), TC_ACT_UNSPEC (-1)
  • Use Case: Traffic control and packet filtering

Socket Programs

  • Sections:
  • sk_skb/stream_parser - Stream parser
  • sk_skb/stream_verdict - Stream verdict
  • sk_msg - Socket message
  • Context: struct __sk_buff *ctx (for sk_skb), struct sk_msg_md *ctx (for sk_msg)
  • Return Values: SK_PASS (1), SK_DROP (0)
  • Use Case: Socket-level packet processing

Cgroup Programs

  • Sections:
  • cgroup/skb/ingress - Cgroup SKB ingress
  • cgroup/skb/egress - Cgroup SKB egress
  • cgroup/sock - Cgroup socket
  • cgroup/sock_addr - Cgroup socket address
  • Context: struct __sk_buff *ctx (for skb), struct bpf_sock *ctx (for sock), struct bpf_sock_addr *ctx (for sock_addr)
  • Return Values: SK_PASS (1), SK_DROP (0), or 1 (allow) for sock_addr
  • Use Case: Cgroup-based network filtering and policy enforcement

Tracing Programs

Kprobe/Kretprobe

  • Sections: kprobe/<function> / kretprobe/<function>
  • Context: struct pt_regs *ctx
  • Return Values: 0 (success)
  • Use Case: Dynamic kernel function tracing

Tracepoint

  • Sections: tracepoint/<category>/<event> (e.g., tracepoint/syscalls/sys_enter_execve)
  • Context: void *ctx (typed based on tracepoint)
  • Return Values: 0 (success)
  • Use Case: Static kernel event tracing

Raw Tracepoint

  • Sections: raw_tracepoint/<event>
  • Context: struct bpf_raw_tracepoint_args *ctx
  • Return Values: 0 (success)
  • Use Case: Fast tracepoint access without type information

Fentry/Fexit

  • Sections: fentry/<function> / fexit/<function>
  • Context: Function arguments (typed)
  • Return Values: 0 (success)
  • Use Case: Modern kernel function entry/exit tracing

Security Programs

LSM (Linux Security Module)

  • Sections: lsm/<hook> (e.g., lsm/file_open)
  • Context: Hook-specific context
  • Return Values: 0 (allow), -EPERM (deny)
  • Use Case: Security policy enforcement

Supported Map Types

Hash Maps

  • Type: .hash
  • Syntax: type: .hash;
  • Use Case: Key-value storage with hash-based lookup
  • BPF Type: BPF_MAP_TYPE_HASH

Array Maps

  • Type: .array
  • Syntax: type: .array;
  • Use Case: Indexed array storage
  • BPF Type: BPF_MAP_TYPE_ARRAY

Ring Buffer

  • Type: .ringbuf
  • Syntax: type: .ringbuf;
  • Use Case: Efficient data streaming from kernel to userspace
  • BPF Type: BPF_MAP_TYPE_RINGBUF
  • Note: No key/value types required

LRU Hash

  • Type: .lru_hash
  • Syntax: type: .lru_hash;
  • Use Case: Hash map with LRU eviction
  • BPF Type: BPF_MAP_TYPE_LRU_HASH

Program Array

  • Type: .prog_array
  • Syntax: type: .prog_array;
  • Use Case: Array of eBPF programs for tail calls
  • BPF Type: BPF_MAP_TYPE_PROG_ARRAY

Perf Event Array

  • Type: .perf_event_array
  • Syntax: type: .perf_event_array;
  • Use Case: Sending events to userspace via perf buffers
  • BPF Type: BPF_MAP_TYPE_PERF_EVENT_ARRAY

Supported Data Types

Integer Types

  • u32: Unsigned 32-bit integer (__u32 in C)
  • u64: Unsigned 64-bit integer (__u64 in C)
  • i32: Signed 32-bit integer (__s32 in C)
  • i64: Signed 64-bit integer (__s64 in C)

Variable Storage Types

  • reg: Register variable (stored in eBPF register)
  • imm: Immediate value (compile-time constant)
  • heap: Map pointer (result of map lookup)

Context Access Methods

Packet Data Access (Positive Offset)

When ctx.load_*() is called with a positive offset, it accesses packet data: - ctx.load_u8(offset) - Load unsigned 8-bit value from packet - ctx.load_u16(offset) - Load unsigned 16-bit value from packet - ctx.load_u32(offset) - Load unsigned 32-bit value from packet - ctx.load_u64(offset) - Load unsigned 64-bit value from packet - ctx.load_i8(offset) - Load signed 8-bit value from packet - ctx.load_i16(offset) - Load signed 16-bit value from packet - ctx.load_i32(offset) - Load signed 32-bit value from packet - ctx.load_i64(offset) - Load signed 64-bit value from packet

Context Field Access (Negative Offset)

When ctx.load_*() is called with a negative offset, it accesses context structure fields.

Supported Operations

Binary Operations

  • Addition: + (Add)
  • Subtraction: - (Sub)
  • Multiplication: * (Mul)
  • Division: / (Div)
  • Modulo: % (Mod)

Assignment Operations

  • Simple Assignment: =
  • Add and Assign: +=
  • Subtract and Assign: -=
  • Multiply and Assign: *=
  • Divide and Assign: /=
  • Modulo and Assign: %=

Map Operations

  • Map Lookup: map_name.lookup(key_expr) - Returns a heap pointer
  • Dereference: *ptr - Dereference a pointer (typically map value pointer)
  • Null Check: guard(ptr) - Conditional block that only executes if pointer is non-null

Control Flow

  • Return Statement: return <value>;
  • If Guard: if guard(ptr) { ... } - Conditional execution based on pointer null check

Map Declaration Syntax

map map_name {
    type: .hash | .array | .ringbuf | .lru_hash | .prog_array | .perf_event_array;
    key: u32 | u64 | i32 | i64;
    value: u32 | u64 | i32 | i64;
    max: <number>;
}

Unit (Program) Declaration Syntax

unit program_name {
    section: "<section_name>";
    license: "GPL" | "<other_license>";

    // Variable declarations
    reg var_name = <expression>;
    imm const_name = <number>;
    heap ptr_name = map_name.lookup(<key>);

    // Operations
    *ptr = <value>;
    *ptr += <value>;

    // Control flow
    if guard(ptr) {
        // statements
    }

    return <value>;
}

Supported eBPF Helpers

The compiler includes standard eBPF helper headers: - #include "vmlinux.h" - Kernel type definitions - #include <bpf/bpf_helpers.h> - Standard eBPF helpers - #include <bpf/bpf_endian.h> - Endianness conversion helpers

Common helpers available (via bpf_helpers.h): - bpf_map_lookup_elem() - Map lookup (generated automatically) - bpf_map_update_elem() - Map update (via assignments) - bpf_map_delete_elem() - Map delete - bpf_get_current_pid_tgid() - Get process/thread ID - bpf_get_current_uid_gid() - Get user/group ID - And many more standard eBPF helpers

Compilation Process

  1. Lexing: Tokenizes the .snx source file
  2. Parsing: Builds AST from tokens
  3. Semantic Analysis: Validates sections, types, and map declarations
  4. IR Lowering: Converts AST to intermediate representation
  5. Code Generation: Emits eBPF C code
  6. Object Compilation: Compiles C to eBPF object file using clang

Limitations and Notes

  1. Map Types: While the parser accepts ringbuf, lru_hash, prog_array, and perf_event_array, full support may vary in code generation
  2. Context Access: Context field access (negative offsets) is supported but packet bounds checking is program-type specific
  3. Helper Functions: Direct helper function calls are not yet supported in the language syntax (only via context methods)
  4. Loops: Currently no loop constructs (while, for) - programs are linear
  5. Functions: No user-defined functions - all code is in the unit body
  6. Tail Calls: Program arrays are supported but tail call syntax is not yet implemented

Example Programs

Current Test Program (test.snx)

map results {
    type: .array;
    key: u32;
    value: u64;
    max: 8;
}

unit sample_tracepoint {
    section: "tracepoint/syscalls/sys_enter_execve";
    license: "GPL";

    reg a = 10;
    reg b = 20;
    reg c = a + b;   
    imm k0 = 0; 
    heap p0 = results.lookup(k0); 
    *p0 = c;

    return 0;
}

XDP Example (from README)

map connection_counter {
    type: .hash;
    key: u32;
    value: u64;
    max: 1024;
}

unit filter_packets {
    section: "xdp";
    license: "GPL";

    reg src_ip = ctx.load_u32(26);
    heap count_ptr = connection_counter.lookup(src_ip);

    if guard(count_ptr) {
        *count_ptr += 1;
    }

    return 1;
}