
Escribe tus programas BPF en Go, no en C. gobee transpila un subconjunto de Go a C BPF y genera bindings tipados de cilium/ebpf.
Escribe tus programas BPF en Go, no en C. gobee transpila un subconjunto estricto de Go a C BPF, genera enlaces Go tipados para el lado de espacio de usuario y controla las cargas contra el kernel en ejecución.
El ecosistema Go tiene herramientas sólidas de espacio de usuario para BPF. El lado del kernel siempre terminaba con "ahora escribe tu programa en C". Aya llevó eBPF a Rust escribiendo un nuevo backend BPF en rustc. gobee llega de otra manera: transpilando a C y reutilizando el maduro backend de clang.
Un tracepoint que transmite cada execve al espacio de usuario a través de un ringbuf:
| Tu entrada (Go) | Lo que gobee emite (BPF C) |
|---|---|
|
|
gobee translate --bindings-dir ./bpf ./bpf/src produce ambos archivos, además de un mapa de origen (events.bpf.c.map) para que los errores del verificador se asignen a líneas Go y un archivo de enlaces tipados (bpf/events_bindings.go) para que el controlador de espacio de usuario escriba objs.Events, objs.AttachOnExec() y decodifique las cargas útiles del ringbuf directamente en bpf.Event (la misma estructura que ves arriba, republicada en Go) en lugar de búsquedas coll.Programs["..."] basadas en cadenas.
El C es legible a propósito. Si gobee emite algo extraño, puedes verlo. Para tracepoints + kprobes + XDP combinados en un solo binario, consulta example/sysmon/.
| gobee | C + clang + bpf2go | Aya (Rust) | bpftrace | BCC | |
|---|---|---|---|---|---|
| Lenguaje del kernel | Subconjunto Go | C | Rust | DSL | C |
| Integración con espacio de usuario | Enlaces Go tipados + cilium/ebpf | bpf2go | aya-runtime | ninguno | python |
| CO-RE | ✅ vía clang | ✅ | ✅ vía LLVM | ✅ | ✅ |
| Cobertura de helpers | 200 wrappers Go tipados | completo (escribe C) | completo | limitado | completo (escribe C) |
| Error de verificador → fuente | ✅ archivo Go:línea:col | ❌ C puro | ✅ archivo Rust:línea | ❌ | parcial |
| Control de versión del kernel en carga | ✅ vía bpfvet | manual | manual | n/a | runtime |
| Dependencias de herramientas | Go + clang | clang + bpf2go | rustc + LLVM | bpftrace | python + bcc |
| Artefacto generado | .bpf.o + binario Go | .bpf.o + binario Go | .bpf.o + binario Rust | JIT | JIT |
Si ya estás en un flujo de trabajo C / libbpf, gobee no pretende reemplazarlo por completo. Es para casos en los que quieres el lado del kernel, el lado de espacio de usuario y la canalización de construcción, todo en un solo módulo Go.
Consulta docs/status.md para la matriz completa (subconjunto Go, sentencias, expresiones, cada helper, cada tipo de mapa, cada directiva). Vista rápida:
| Superficie | Cobertura |
|---|---|
| Tipos de programa (8) | XDP, tracepoint, kprobe / kretprobe, uprobe / uretprobe, sock_ops, TC, cgroup_skb, LSM |
| Tipos de mapa (19) | array, hash, lru_hash, variantes por CPU, bloom_filter, lpm_trie, ringbuf, perf_event_array, prog_array, queue, stack, almacenamiento sk/task/inode, devmap/cpumap/xskmap |
| Helpers BPF | ~200 stubs Go tipados autogenerados de los encabezados libbpf v1.5.0. Los que se ejercitan en example/helloworld/ y example/sysmon/ se prueban en CI de kernel real; el resto no están verificados. Reporta un problema si un stub no coincide con la firma del kernel |
| CO-RE | ✅ detectado automáticamente. BPF_CORE_READ para campos de estructuras internas del kernel (task_struct, sock, inode); ctx->field directo para contextos BPF UAPI (xdp_md, __sk_buff, bpf_sock_ops). Probado en Linux 6.x (CI Ubuntu 24.04); kernels más antiguos aún no están en la matriz CI |
| Salida lista para BTF | ✅ el C emitido incluye vmlinux.h y usa BPF_CORE_READ para lecturas de campos internos del kernel, para que el BTF que clang genera de clang -g lleve las reubicaciones correctas. clang sigue siendo tu responsabilidad (los Makefiles de ejemplo muestran la invocación canónica) |
| Helpers definidos por el usuario | ✅ las funciones Go de nivel superior sin //bpf:section se emiten como funciones C static __always_inline |
| Enlaces Go tipados | ✅ Load<Stem>, Close, Attach<Name> por programa, AttachAll, además de tus tipos de estructura del kernel y constantes republicados en Go |
| Control de versión del kernel |
go/types sobre tu entrada primero, para que los usos incorrectos aparezcan en file:line:col).<Stem>_bindings.go tipado junto al .bpf.c: bpf.LoadCounter(spec), objs.PerIface.Lookup(...), objs.AttachAll(ifindex), además de tus tipos de estructura del kernel y constantes republicados en Go.*ebpf.VerifierError de LoadAndAssign con posiciones en el código fuente Go, sin necesidad de tubería manual gobee diagnose.Load<Stem> para que kernels antiguos fallen rápido con bpf program needs kernel >= 5.8, host is 5.4.static __always_inline.cilium/ebpf. Los enlaces generados se sitúan sobre él.gc, el compilador Go, no tiene un backend BPF basado en LLVM. Agregar uno es un proyecto de compilador de varios años. rustc está construido sobre LLVM y por eso Aya funciona. Así que gobee emite C y reutiliza el backend BPF de clang, lo que nos da generación de código madura, BTF y reubicaciones CO-RE de forma gratuita.
go install github.com/boratanrikulu/gobee/cmd/gobee@latest
cd example/helloworld
make build # gobee translate, clang, go build
sudo ./helloworld eth0
Necesitarás clang con el objetivo BPF. En Linux es el paquete de la distribución; en macOS, brew install llvm. El transpilador en sí es Go puro y funciona en cualquier lugar.
tuproyecto/
├── bpf/ # Paquete Go, importable desde cualquier lugar de tu proyecto
│ ├── embed_amd64.go # //go:embed bin/x86/tu.bpf.o
│ ├── embed_arm64.go
│ ├── tu_bindings.go # generado por gobee
│ ├── bin/{x86,arm64}/tu.bpf.o
│ └── src/ # No es un paquete Go; clang vive aquí
│ ├── tu.go # //go:build ignore: fuente BPF
│ ├── tu.bpf.c # generado
│ ├── Makefile # clang por arquitectura
│ └── vmlinux.h # volcado BTF incluido
├── main.go # importa tuproyecto/bpf
└── Makefile
La división mantiene bpf/ como un paquete Go limpio e importable (Go rechaza archivos .c en paquetes que no usan cgo). Las fuentes del kernel y los artefactos de clang viven un nivel más abajo en bpf/src/.
example/helloworld/: el contador de paquetes XDP canónico, ~40 líneas BPF, ~80 líneas de espacio de usuario.example/sysmon/: XDP, dos tracepoints y un kprobe en un solo binario, compartiendo un ringbuf para eventos. Demuestra contextos tipados por syscall, funciones helper definidas por el usuario y el atajo AttachAll.GitHub Actions ejecuta cuatro capas en cada push:
go test, go vet, pruebas golden del transpilador//bpf:section tiene al menos un ejemplobpfvetebpf.NewCollectionWithOptions en cada .bpf.o (ejecutor Ubuntu 24.04, kernel 6.x)docs/design.md: arquitectura y fundamentosdocs/go-subset.md: sintaxis Go aceptada en archivos fuente BPFdocs/directives.md: referencia de //bpf:*docs/status.md: matriz de soporte (fuente única de verdad).bpf.o necesita clang con el objetivo BPF. El clang incluido de Apple no lo trae; en macOS usa brew install llvm o compila dentro de una máquina virtual Linux.MIT. Ver LICENSE.
Copyright (c) 2026 Bora Tanrikulu <[email protected]>
✅ bpfvet se ejecuta en tiempo de carga. Falla rápido con bpf program needs kernel >= 5.8, host is 5.4 en lugar de EINVAL opaco |
| Error de verificador → fuente Go | ✅ anotado automáticamente dentro de Load<Stem>. Sin tubería manual a gobee diagnose; *ebpf.VerifierError regresa con marcadores → counter.go:18:5 |
| Archivo sidecar de mapa de origen | ✅ <stem>.bpf.c.map se escribe junto a cada .bpf.c para uso offline también con gobee diagnose |
| Multi-arquitectura | ✅ Linux arm64 + amd64 |