ebpf-cnf-scaffold
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.
$ インストール
git clone https://github.com/cassamajor/xcnf /tmp/xcnf && cp -r /tmp/xcnf/.claude/skills/ebpf-cnf-scaffold ~/.claude/skills/xcnf// tip: Run this command in your terminal to install the skill
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
Basic Structure
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
With Internal Packages
For complex CNFs, use subpackages to organize code:
examples/{name}/
├── main.go # Entry point, CLI, event loop
├── main_test.go # Integration tests
├── bytecode/
│ ├── gen.go # bpf2go generation
│ ├── vmlinux.h # Kernel types for CO-RE
│ ├── {name}.c # eBPF program
│ ├── {name}_bpfel.go # Generated
│ └── {name}_bpfeb.go # Generated
├── {subpkg}/ # Internal package
│ ├── {subpkg}.go # Core functionality
│ ├── {subpkg}_test.go # Unit tests
│ └── options.go # Functional options
├── go.mod
└── README.md
Example from netkit-ipv6:
examples/netkit-ipv6/
├── main.go
├── main_test.go
├── bytecode/
│ ├── gen.go
│ ├── vmlinux.h
│ └── netkit_ipv6.c
├── netkit/ # Subpackage for device management
│ ├── netkit.go # CreatePair, Delete
│ ├── netkit_test.go
│ ├── options.go # WithL2Mode, WithL3Mode
│ └── ipv6.go # ConfigureIPv6LinkLocal
└── go.mod
Benefits of subpackages:
- Separation of concerns (bytecode generation vs device management)
- Reusable components
- Independent unit testing
- Clearer imports in main.go
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):
orb -
Generate vmlinux.h (for CO-RE programs):
cd examples/{name}/bytecode bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h -
Generate eBPF bytecode:
cd examples/{name} go generate ./bytecode -
Implement 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"
Repository
