Documentation ¶
Overview ¶
Package cbpfc implements a cBPF (classic BPF) to eBPF (extended BPF, not be confused with cBPF extensions) compiler.
cbpfc can compile cBPF filters to:
- C, which can be compiled to eBPF with Clang
- eBPF
Both the C and eBPF output are intended to be accepted by the kernel verifier:
- All packet loads are guarded with runtime packet length checks
- RegA and RegX are zero initialized as required
- Division by zero is guarded by runtime checks
The generated C / eBPF is intended to be embedded into a larger C / eBPF program.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ToC ¶
func ToC(filter []bpf.Instruction, opts COpts) (string, error)
ToC compiles a cBPF filter to a C function with a signature of:
uint32_t opts.FunctionName(const uint8_t *const data, const uint8_t *const data_end)
The function returns the filter's return value: 0 if the packet does not match the cBPF filter, non 0 if the packet does match.
Example ¶
ExampleToC demonstrates how to use ToC() to embed a cBPF filter in a C program, and compile it to eBPF.
package main import ( "bytes" "os" "text/template" "github.com/cloudflare/cbpfc/clang" "github.com/pkg/errors" "golang.org/x/net/bpf" ) var testTemplate = template.Must(template.New(entryPoint).Parse(` #define __section(NAME) __attribute__((section(NAME), used)) char __license[] __section("license") = "BSD"; // Shim out all the definitions required by cbpfc // Real programs should use the proper headers typedef unsigned long long uint64_t; typedef long long int64_t; typedef unsigned int uint32_t; typedef int int32_t; typedef unsigned short uint16_t; typedef unsigned char uint8_t; typedef char bool; #define false 0 #define true 1 #define ntohs __builtin_bswap16 #define ntohl __builtin_bswap32 struct xdp_md { uint32_t data; uint32_t data_end; }; enum xdp_action { XDP_DROP = 1, XDP_PASS, }; {{.Filter}} __section("xdp") int {{.ProgramName}}(struct xdp_md *ctx) { uint8_t *data = (uint8_t *)(long)ctx->data; uint8_t const *data_end = (uint8_t *)(long)ctx->data_end; if ({{.FilterName}}(data, data_end)) { return XDP_DROP; } return XDP_PASS; } `)) type testTemplateOpts struct { // Definition of the filter Filter string // Function name of the filter FilterName string // Name of the eBPF program ProgramName string } // ExampleToC demonstrates how to use ToC() to embed a cBPF filter // in a C program, and compile it to eBPF. func main() { // simple cBPF filter that matches all packets filter := []bpf.Instruction{ bpf.RetConstant{Val: 1}, } elf, err := buildC(filter, "example", COpts{FunctionName: "example_filter"}) if err != nil { panic(err) } // ELF with a single eBPF program 'example' // Can be loaded with cilium/ebpf or libbpf _ = elf } // buildC compiles a cBPF filter to C, embeds it in a C template, // and compiles the resulting C program to eBPF / XDP using clang. // The XDP program XDP_DROP's incoming packets that match the filter. // Returns the compiled ELF func buildC(filter []bpf.Instruction, programName string, opts COpts) ([]byte, error) { // convert filter to C ebpfFilter, err := ToC(filter, opts) if err != nil { return nil, errors.Wrap(err, "converting filter to C") } // embed filter in C template c := bytes.Buffer{} err = testTemplate.Execute(&c, testTemplateOpts{ Filter: ebpfFilter, FilterName: opts.FunctionName, ProgramName: programName, }) if err != nil { return nil, errors.Wrap(err, "executing template with C filter") } // lookup clang binary to use clangBin, ok := os.LookupEnv("CLANG") if !ok { clangBin = "/usr/bin/clang" } // compile C program elf, err := clang.Compile(c.Bytes(), entryPoint, clang.Opts{ Clang: clangBin, EmitDebug: true, // For BTF }) if err != nil { return nil, errors.Wrap(err, "compiling C") } return elf, nil }
Output:
func ToEBPF ¶
func ToEBPF(filter []bpf.Instruction, opts EBPFOpts) (asm.Instructions, error)
ToEBF converts a cBPF filter to eBPF.
The generated eBPF code always jumps to opts.ResultLabel, with register opts.Result containing the filter's return value: 0 if the packet does not match the cBPF filter, non 0 if the packet does match.
Example ¶
ExampleToEBPF demonstrates how to use ToEBPF() to embed a cBPF filter in an eBPF assembly program.
package main import ( "github.com/cilium/ebpf/asm" "github.com/pkg/errors" "golang.org/x/net/bpf" ) // ExampleToEBPF demonstrates how to use ToEBPF() to embed a cBPF filter // in an eBPF assembly program. func main() { // simple cBPF filter that matches all packets filter := []bpf.Instruction{ bpf.RetConstant{Val: 1}, } prog, err := buildEBPF(filter) if err != nil { panic(err) } // Prog can be loaded directly using cilium/ebpf, // or converted to a '[]struct bpf_insn' for libbpf _ = prog } // buildEBPF compiles a cBPF filter to eBPF, and embeds it an eBPF program. // The XDP program XDP_DROP's incoming packets that match the filter. // Returns the eBPF program instructions func buildEBPF(filter []bpf.Instruction) (asm.Instructions, error) { ebpfFilter, err := ToEBPF(filter, EBPFOpts{ // Pass packet start and end pointers in these registers PacketStart: asm.R2, PacketEnd: asm.R3, // Result of filter Result: asm.R4, ResultLabel: "result", // Registers used by generated code Working: [4]asm.Register{asm.R4, asm.R5, asm.R6, asm.R7}, LabelPrefix: "filter", }) if err != nil { return nil, errors.Wrap(err, "converting filter to eBPF") } prog := asm.Instructions{ // R1 holds XDP context // Packet start asm.LoadMem(asm.R2, asm.R1, 0, asm.Word), // Packet end asm.LoadMem(asm.R3, asm.R1, 4, asm.Word), // Fall through to filter } prog = append(prog, ebpfFilter...) prog = append(prog, asm.Mov.Imm(asm.R0, 2).WithSymbol("result"), // XDP_PASS asm.JEq.Imm(asm.R4, 0, "return"), asm.Mov.Imm(asm.R0, 1), // XDP_DROP asm.Return().WithSymbol("return"), ) return prog, nil }
Output:
Types ¶
type COpts ¶
type COpts struct { // FunctionName is the symbol to use as the generated C function. Must match regex: // [A-Za-z_][0-9A-Za-z_]* FunctionName string // NoInline doesn't force the generated function to be inlined, allowing clang to emit // a BPF to BPF call. // Requires at least kernel 5.10 (for x86, later for other architectures) if used with tail-calls. NoInline bool }
type EBPFOpts ¶
type EBPFOpts struct { // PacketStart is a register holding a pointer to the start of the packet. // Not modified. PacketStart asm.Register // PacketEnd is a register holding a pointer to the end of the packet. // Not modified. PacketEnd asm.Register // Register to output the filter return value in. Result asm.Register // Label to jump to with the result of the filter in register Result. ResultLabel string // Working are registers used internally. // Caller saved. // Must be different to PacketStart and PacketEnd, but Result can be reused. Working [4]asm.Register // StackOffset is the number of bytes of stack already used / reserved. // R10 (ebpf frame pointer) + StackOffset will be used as the top of the stack. StackOffset int // LabelPrefix is the prefix to prepend to labels used internally. LabelPrefix string }
EBPFOpts control how a cBPF filter is converted to eBPF