github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/xdp_stats_assembly/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "os/signal" 7 "strings" 8 "time" 9 10 "github.com/dylandreimerink/gobpfld" 11 "github.com/dylandreimerink/gobpfld/bpftypes" 12 "github.com/dylandreimerink/gobpfld/ebpf" 13 "golang.org/x/sys/unix" 14 ) 15 16 // This example command executes the same program as xdp_stats which is basic03-map-counter from 17 // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter 18 // However, this example demonstrates manually recreating the program from eBPF assembly which 19 // have been annotated as a bonus lesson. The assembly is then assembled into 20 // eBPF instructions. 21 // The same output can be generated by a program using gobpflb to generate dynamic eBPF programs 22 23 func main() { 24 program := &gobpfld.ProgramXDP{ 25 AbstractBPFProgram: gobpfld.AbstractBPFProgram{ 26 Name: gobpfld.MustNewObjName("xdp_stats1"), 27 ProgramType: bpftypes.BPF_PROG_TYPE_XDP, 28 License: "GPL", 29 Maps: map[string]gobpfld.BPFMap{ 30 "xdp_stats_map": &gobpfld.ArrayMap{ 31 AbstractMap: gobpfld.AbstractMap{ 32 Name: gobpfld.MustNewObjName("xdp_stats_map"), 33 Definition: gobpfld.BPFMapDef{ 34 Type: bpftypes.BPF_MAP_TYPE_ARRAY, 35 KeySize: 4, // SizeOf(uint32) 36 ValueSize: 8, // SizeOf(uint64) 37 MaxEntries: 5, 38 }, 39 }, 40 }, 41 }, 42 MapFDLocations: map[string][]uint64{ 43 "xdp_stats_map": { 44 // `r1 = 0 ll` is the 5th instruction in this program. So the first byte of the 45 // 5th instruction is the width of a instruction * 4 to skip the first 4 instructions 46 uint64(ebpf.BPFInstSize) * 4, 47 }, 48 }, 49 }, 50 } 51 52 asm := ` 53 # __u32 key = XDP_PASS; 54 r1 = 2 55 *(u32 *)(r10 - 4) = r1 56 57 # rec = bpf_map_lookup_elem(&xdp_stats_map, &key); 58 r2 = r10 59 r2 += -4 60 r1 = 0 ll 61 call 1 62 63 # if (!rec) return XDP_ABORTED; 64 r1 = 0 65 if r0 == 0 goto return 66 67 # lock_xadd(&rec->rx_packets, 1); 68 r1 = 1 69 lock *(u64 *)(r0 + 0) += r1 70 71 # return XDP_PASS; 72 r1 = 2 73 74 return: 75 r0 = r1 76 exit 77 ` 78 79 inst, err := ebpf.AssemblyToInstructions("inline-asm", strings.NewReader(asm)) 80 if err != nil { 81 panic(err) 82 } 83 84 program.Instructions = ebpf.MustEncode(inst) 85 86 // All maps loaded from elf files are BPFGenericMaps 87 statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap) 88 89 log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 90 VerifierLogLevel: bpftypes.BPFLogLevelBasic, 91 }) 92 93 fmt.Printf("BPF Verifier log:\n%s\n", log) 94 95 if err != nil { 96 program.DecodeToReader(os.Stdout) 97 fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error()) 98 os.Exit(1) 99 } 100 101 sigChan := make(chan os.Signal, 1) 102 signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT) 103 104 err = program.Attach(gobpfld.ProgXDPAttachOpts{ 105 InterfaceName: "lo", 106 Replace: true, 107 }) 108 109 if err != nil { 110 fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error()) 111 os.Exit(1) 112 } 113 114 ticker := time.Tick(1 * time.Second) 115 for { 116 select { 117 case <-ticker: 118 // The key is 2 since the program puts stats in the XDP_PASS key which has value 2 119 // Tho this is specific to the XDP program we are using as an example. 120 key := uint32(2) 121 var value int64 122 123 err = statsMap.Get(key, &value) 124 if err != nil { 125 fmt.Fprintf(os.Stderr, "error while getting stats from map: %s\n", err.Error()) 126 os.Exit(1) 127 } 128 129 fmt.Printf("%d packets were processed\n", value) 130 131 case <-sigChan: 132 fmt.Println("Detaching XPD program and stopping") 133 134 err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{ 135 All: true, 136 }) 137 if err != nil { 138 fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error()) 139 os.Exit(1) 140 } 141 142 os.Exit(0) 143 } 144 } 145 }