|
| 1 | +// This program demonstrates attaching an eBPF program to a network interface |
| 2 | +// with Linux TC (Traffic Control). The program counts ingress and egress |
| 3 | +// packets using two ARRAY maps. |
| 4 | +// The userspace program (Go code in this file) prints the contents |
| 5 | +// of the two maps to stdout every second. |
| 6 | +// This example depends on tcx bpf_link, available in Linux kernel version 6.6 or newer. |
| 7 | +package main |
| 8 | + |
| 9 | +import ( |
| 10 | + "fmt" |
| 11 | + "log" |
| 12 | + "net" |
| 13 | + "os" |
| 14 | + "time" |
| 15 | + |
| 16 | + "github.com/cilium/ebpf" |
| 17 | + "github.com/cilium/ebpf/link" |
| 18 | +) |
| 19 | + |
| 20 | +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf tcx.c -- -I../headers |
| 21 | +func main() { |
| 22 | + if len(os.Args) < 2 { |
| 23 | + log.Fatalf("Please specify a network interface") |
| 24 | + } |
| 25 | + |
| 26 | + // Look up the network interface by name. |
| 27 | + ifaceName := os.Args[1] |
| 28 | + iface, err := net.InterfaceByName(ifaceName) |
| 29 | + if err != nil { |
| 30 | + log.Fatalf("lookup network iface %q: %s", ifaceName, err) |
| 31 | + } |
| 32 | + |
| 33 | + // Load pre-compiled programs into the kernel. |
| 34 | + objs := bpfObjects{} |
| 35 | + if err := loadBpfObjects(&objs, nil); err != nil { |
| 36 | + log.Fatalf("loading objects: %s", err) |
| 37 | + } |
| 38 | + defer objs.Close() |
| 39 | + |
| 40 | + // Attach the program to Ingress TC. |
| 41 | + l, err := link.AttachTCX(link.TCXOptions{ |
| 42 | + Interface: iface.Index, |
| 43 | + Program: objs.IngressProgFunc, |
| 44 | + Attach: ebpf.AttachTCXIngress, |
| 45 | + }) |
| 46 | + if err != nil { |
| 47 | + log.Fatalf("could not attach TCx program: %s", err) |
| 48 | + } |
| 49 | + defer l.Close() |
| 50 | + |
| 51 | + log.Printf("Attached TCx program to INGRESS iface %q (index %d)", iface.Name, iface.Index) |
| 52 | + |
| 53 | + // Attach the program to Egress TC. |
| 54 | + l2, err := link.AttachTCX(link.TCXOptions{ |
| 55 | + Interface: iface.Index, |
| 56 | + Program: objs.EgressProgFunc, |
| 57 | + Attach: ebpf.AttachTCXEgress, |
| 58 | + }) |
| 59 | + if err != nil { |
| 60 | + log.Fatalf("could not attach TCx program: %s", err) |
| 61 | + } |
| 62 | + defer l2.Close() |
| 63 | + |
| 64 | + log.Printf("Attached TCx program to EGRESS iface %q (index %d)", iface.Name, iface.Index) |
| 65 | + log.Printf("Press Ctrl-C to exit and remove the program") |
| 66 | + |
| 67 | + // Print the contents of the counters maps. |
| 68 | + ticker := time.NewTicker(1 * time.Second) |
| 69 | + defer ticker.Stop() |
| 70 | + for range ticker.C { |
| 71 | + s, err := formatCounters(objs.IngressPktCount, objs.EgressPktCount) |
| 72 | + if err != nil { |
| 73 | + log.Printf("Error reading map: %s", err) |
| 74 | + continue |
| 75 | + } |
| 76 | + |
| 77 | + log.Printf("Packet Count: %s\n", s) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +func formatCounters(ingressMap, egressMap *ebpf.Map) (string, error) { |
| 82 | + var ( |
| 83 | + ingressPacketCount uint64 |
| 84 | + egressPacketCount uint64 |
| 85 | + key int32 |
| 86 | + ) |
| 87 | + |
| 88 | + // retrieve value from the ingress map |
| 89 | + if err := ingressMap.Lookup(&key, &ingressPacketCount); err != nil { |
| 90 | + return "", err |
| 91 | + } |
| 92 | + |
| 93 | + // retrieve value from the egress map |
| 94 | + if err := egressMap.Lookup(&key, &egressPacketCount); err != nil { |
| 95 | + return "", err |
| 96 | + } |
| 97 | + |
| 98 | + return fmt.Sprintf("%10v Ingress, %10v Egress", ingressPacketCount, egressPacketCount), nil |
| 99 | +} |
0 commit comments