| name | ebpf-cnf-scaffold |
| description | Scaffold new eBPF-based Cloud Native Network Function (CNF) projects with proper directory structure, boilerplate C kernel code, Go userspace application, bpf2go generation, and build configuration following established patterns. Use when creating new CNF examples or starting fresh eBPF projects. |
eBPF CNF Scaffold Skill
This skill scaffolds complete eBPF-based CNF projects following the established patterns in this repository.
What This Skill Does
Creates a new eBPF CNF project with:
- Proper directory structure (
examples/{name}/) - eBPF kernel program in Restricted C (
bytecode/{name}.c) - Go userspace application (
main.go) - bpf2go code generation setup (
bytecode/gen.go) - Go module initialization
- README with build/run instructions
- Optional Dockerfile for containerization
When to Use
- Creating a new CNF from scratch
- Adding a new example to the examples/ directory
- Starting a new eBPF networking project
- Need a working template that follows repository conventions
Project Structure Created
examples/{name}/
├── main.go # Go userspace application
├── bytecode/
│ ├── gen.go # bpf2go generation directive
│ ├── {name}.c # eBPF kernel program (Restricted C)
│ ├── {name}_bpfel.go # Generated (after go generate)
│ └── {name}_bpfeb.go # Generated (after go generate)
├── go.mod # Go module file
├── README.md # Build and run instructions
└── Dockerfile # Optional containerization
Information to Gather
Before scaffolding, ask the user:
- CNF Name: What to name the CNF (lowercase, hyphens only)
- Hook Type: Which eBPF hook to use (XDP, TC/tcx, netkit, kprobe, tracepoint)
- Functionality: Brief description of what the CNF should do
- Maps Needed: Does it need eBPF maps? (hash, array, ringbuf, etc.)
- Dockerfile: Should a Dockerfile be included?
Boilerplate Components
1. eBPF Program Template (bytecode/{name}.c)
//go:build ignore
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
// Add appropriate headers based on hook type
// XDP: <linux/if_ether.h>, <linux/ip.h>
// TC/tcx: <linux/pkt_cls.h>
// netkit: <linux/if_link.h>
char _license[] SEC("license") = "GPL";
SEC("{hook_section}")
int {function_name}(struct {ctx_type} *ctx) {
// TODO: Implement CNF logic
return {return_value};
}
2. Go Generation File (bytecode/gen.go)
package bytecode
//go:generate go tool bpf2go -type {types} {CapitalizedName} {name}.c -- -O2 -Wall -Werror
3. Go Userspace Application (main.go)
package main
import (
"log"
"os"
"os/signal"
"syscall"
"{module}/bytecode"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
)
func main() {
// Load eBPF objects
spec, err := bytecode.Load{Name}()
if err != nil {
log.Fatalf("loading eBPF spec: %v", err)
}
objs := &bytecode.{Name}Objects{}
if err := spec.LoadAndAssign(objs, nil); err != nil {
log.Fatalf("loading eBPF objects: %v", err)
}
defer objs.Close()
// TODO: Attach program to hook
// TODO: Read from maps/ringbufs if needed
log.Println("{Name} CNF is running...")
// Wait for signal
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
log.Println("Shutting down...")
}
4. README Template
# {Name} CNF
{Description}
## Build
Compile the eBPF program:
```shell
go generate ./bytecode
```
Build the userspace application:
```shell
go build -o {name}
```
## Run
```shell
sudo ./{name}
```
## Test
```shell
sudo -E go test .
```
Hook-Specific Configuration
XDP
- Section:
SEC("xdp") - Context:
struct xdp_md *ctx - Return values:
XDP_PASS,XDP_DROP,XDP_TX,XDP_REDIRECT,XDP_ABORTED - Attach:
link.AttachXDP()
TC/tcx (Kernel 6.6+)
- Section:
SEC("tc")orSEC("tcx/ingress")/SEC("tcx/egress") - Context:
struct __sk_buff *skb - Return values:
TC_ACT_OK,TC_ACT_SHOT,TC_ACT_REDIRECT,TC_ACT_PIPE - Attach:
link.AttachTCX()for tcx,link.AttachTC()for legacy
netkit (BPF-programmable network device)
- Section:
SEC("netkit/primary")andSEC("netkit/peer") - Context:
struct __sk_buff *skb - Return values:
NETKIT_PASS,NETKIT_DROP,NETKIT_REDIRECT - Attach:
link.AttachNetkit()withebpf.AttachNetkitPrimaryorebpf.AttachNetkitPeer - Note: Requires two programs (primary and peer) for bidirectional processing
kprobe/kretprobe
- Section:
SEC("kprobe/function_name")orSEC("kretprobe/function_name") - Context:
struct pt_regs *ctx - Return value:
0 - Attach:
link.Kprobe()orlink.Kretprobe()
tracepoint
- Section:
SEC("tracepoint/category/name") - Context: Custom struct based on tracepoint
- Return value:
0 - Attach:
link.Tracepoint()
Go Module Initialization
After creating the structure, initialize the Go module:
cd examples/{name}
go mod init github.com/{username}/xcnf/examples/{name}
go get -tool github.com/cilium/ebpf/cmd/bpf2go
go mod tidy
Post-Scaffold Instructions
After scaffolding, remind the user to:
Enter the Linux VM (if using OrbStack):
orbGenerate eBPF bytecode:
cd examples/{name} go generate ./bytecodeImplement the CNF logic in:
bytecode/{name}.c- kernel-space processingmain.go- userspace control and data handling
Build and test:
go build -o {name} sudo ./{name}
Best Practices
- Use meaningful variable names that describe the CNF's purpose
- Add TODO comments for areas that need implementation
- Include bounds checking for all packet data access
- Use appropriate helper functions (
bpf_printkfor debugging) - Follow the existing patterns in the repository
- Add proper error handling in Go code
- Use
deferfor cleanup (Close(), link cleanup)
Example Usage
User: "Create a new CNF called 'rate-limiter' that uses XDP to rate limit incoming packets"