034 Project 34: eBPF XDP Packet Counter

034 Build an eBPF XDP Packet Counter

Attach an XDP program to a NIC and count packets in a BPF map.

Goal

  • attach XDP to interface
  • increment packet counter map in kernel
  • read counter from userspace every second

main.go skeleton

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/cilium/ebpf/link"
)

func main() {
    objs := bpfObjects{}
    if err := loadBpfObjects(&objs, nil); err != nil {
        log.Fatal(err)
    }
    defer objs.Close()

    l, err := link.AttachXDP(link.XDPOptions{
        Program:   objs.XdpCount,
        Interface: 2, // set your interface index
    })
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()

    for range time.Tick(time.Second) {
        var key uint32 = 0
        var value uint64
        if err := objs.PacketCount.Lookup(&key, &value); err == nil {
            fmt.Printf("packets=%d\n", value)
        }
    }
}

Notes

  • Keep this in a lab VM.
  • Add interface discovery + CLI flags.
  • Add map reset endpoint after reading.

Step-by-Step Explanation

  1. Keep kernel-side eBPF logic minimal and focused.
  2. Load and attach program from userspace.
  3. Stream events or counters from maps/buffers.
  4. Handle shutdown and detachment cleanly.
  5. Validate behavior in an isolated Linux lab environment.

Code Anatomy

  • eBPF program runs in kernel hook.
  • Userspace Go loader manages attach/read lifecycle.
  • Reader loop transforms low-level events into readable output.

Learning Goals

  • Understand event-driven observability close to the kernel.
  • Learn safe iteration practices for low-level tooling.
  • Build confidence with modern Linux instrumentation.