github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/xdp_stats_instructions/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "os/signal" 7 "time" 8 9 "github.com/dylandreimerink/gobpfld" 10 "github.com/dylandreimerink/gobpfld/bpftypes" 11 "github.com/dylandreimerink/gobpfld/ebpf" 12 "golang.org/x/sys/unix" 13 ) 14 15 // This example command executes the same program as xdp_stats which is basic03-map-counter from 16 // xdp-tutorial. https://github.com/xdp-project/xdp-tutorial/tree/master/basic03-map-counter 17 // However, this example demonstrates manually recreating the program from eBPF instructions which 18 // have been annotated as a bonus lesson in eBPF instructions. 19 // The same output can be generated by a program using gobpflb to generate dynamic eBPF programs 20 21 func main() { 22 program := &gobpfld.ProgramXDP{ 23 AbstractBPFProgram: gobpfld.AbstractBPFProgram{ 24 Name: gobpfld.MustNewObjName("xdp_stats1"), 25 ProgramType: bpftypes.BPF_PROG_TYPE_XDP, 26 License: "GPL", 27 Maps: map[string]gobpfld.BPFMap{ 28 "xdp_stats_map": &gobpfld.ArrayMap{ 29 AbstractMap: gobpfld.AbstractMap{ 30 Name: gobpfld.MustNewObjName("xdp_stats_map"), 31 Definition: gobpfld.BPFMapDef{ 32 Type: bpftypes.BPF_MAP_TYPE_ARRAY, 33 KeySize: 4, // SizeOf(uint32) 34 ValueSize: 8, // SizeOf(uint64) 35 MaxEntries: 5, 36 }, 37 }, 38 }, 39 }, 40 MapFDLocations: map[string][]uint64{ 41 "xdp_stats_map": { 42 // LoadConstant64bit is the 5th instruction in this program. So the first byte of the 43 // 5th instruction is the width of a instruction * 4 to skip the first 4 instructions 44 uint64(ebpf.BPFInstSize) * 4, 45 }, 46 }, 47 Instructions: ebpf.MustEncode([]ebpf.Instruction{ 48 //// 49 // __u32 key = XDP_PASS; 50 //// 51 &ebpf.Mov64{ // r1 = 2 52 Dest: ebpf.BPF_REG_1, 53 Value: ebpf.XDP_PASS, 54 }, 55 &ebpf.StoreMemoryRegister{ // *(u32 *)(r10 - 4) = r1 56 Size: ebpf.BPF_W, // 32bits 57 Dest: ebpf.BPF_REG_10, // R10 = Frame pointer(end of stack) 58 Offset: -4, // 4x8 = 32 59 Src: ebpf.BPF_REG_1, 60 }, 61 62 //// 63 // rec = bpf_map_lookup_elem(&xdp_stats_map, &key); 64 //// 65 &ebpf.Mov64Register{ // r2 = r10 66 Dest: ebpf.BPF_REG_2, 67 Src: ebpf.BPF_REG_10, 68 }, 69 // make R2 a pointer to key, aka &key 70 &ebpf.Add64{ // r2 += -4 71 Dest: ebpf.BPF_REG_2, 72 Value: -4, 73 }, 74 // The constant value to be loaded here will be inserted by the loader 75 // it will become a pointer to the map, aka &xdp_stats_map 76 &ebpf.LoadConstant64bit{ // r1 = 0 ll 77 Dest: ebpf.BPF_REG_1, 78 }, 79 &ebpf.Nop{}, // Nop is a dummy since LoadConstant64bit consumes 2 instructions to fit 64bits 80 // The actual function call which takes R1 and R2 as arguments and uses R0 as return value 81 // where R1 is the pointer to the map in which the lookup should occur 82 // and where R2 is the pointer to the key to use 83 // R0 will become a pointer to the value 84 &ebpf.CallHelper{ // call 1 85 Function: 1, // 1 = bpf_map_lookup_elem 86 }, 87 88 //// 89 // if (!rec) return XDP_ABORTED; 90 //// 91 &ebpf.Mov64{ // r1 = 0 92 Dest: ebpf.BPF_REG_1, 93 Value: ebpf.XDP_ABORTED, // XDP_ABORTED = 0 94 }, 95 // check if the return value is a null pointer, aka: if (!rec) 96 &ebpf.JumpEqual{ // if r0 == 0: goto pc+3 <LBL0> 97 Dest: ebpf.BPF_REG_0, 98 Offset: 3, // 99 Value: 0, 100 }, 101 102 //// 103 // lock_xadd(&rec->rx_packets, 1); 104 //// 105 &ebpf.Mov64{ // r1 = 1 106 Dest: ebpf.BPF_REG_1, 107 Value: 1, 108 }, 109 &ebpf.AtomicAdd{ // lock *(u64 *)(r0 + 0) += r1 110 Size: ebpf.BPF_DW, 111 Dest: ebpf.BPF_REG_0, // R0 contains the pointer to the value in the map 112 Src: ebpf.BPF_REG_1, 113 }, 114 115 //// 116 // return XDP_PASS; 117 //// 118 &ebpf.Mov64{ // r1 = 2 119 Dest: ebpf.BPF_REG_1, 120 Value: ebpf.XDP_PASS, // XDP_PASS = 2 121 }, 122 123 //// 124 // Label LBL0, target of JumpEqual 125 //// 126 &ebpf.Mov64Register{ // r0 = r1 127 Dest: ebpf.BPF_REG_0, // R0 is the return value of this program 128 Src: ebpf.BPF_REG_1, // R1 is XDP_PASS or XDP_ABORTED depending on branching 129 }, 130 &ebpf.Exit{}, // exit 131 }), 132 }, 133 } 134 135 // All maps loaded from elf files are BPFGenericMaps 136 statsMap := program.Maps["xdp_stats_map"].(*gobpfld.ArrayMap) 137 138 log, err := program.Load(gobpfld.ProgXDPLoadOpts{ 139 VerifierLogLevel: bpftypes.BPFLogLevelBasic, 140 }) 141 142 fmt.Printf("BPF Verifier log:\n%s\n", log) 143 144 if err != nil { 145 program.DecodeToReader(os.Stdout) 146 fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error()) 147 os.Exit(1) 148 } 149 150 sigChan := make(chan os.Signal, 1) 151 signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT) 152 153 err = program.Attach(gobpfld.ProgXDPAttachOpts{ 154 InterfaceName: "lo", 155 Replace: true, 156 }) 157 158 if err != nil { 159 fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error()) 160 os.Exit(1) 161 } 162 163 ticker := time.Tick(1 * time.Second) 164 for { 165 select { 166 case <-ticker: 167 // The key is 2 since the program puts stats in the XDP_PASS key which has value 2 168 // Tho this is specific to the XDP program we are using as an example. 169 key := uint32(2) 170 var value int64 171 172 err = statsMap.Get(key, &value) 173 if err != nil { 174 fmt.Fprintf(os.Stderr, "error while getting stats from map: %s\n", err.Error()) 175 os.Exit(1) 176 } 177 178 fmt.Printf("%d packets were processed\n", value) 179 180 case <-sigChan: 181 fmt.Println("Detaching XPD program and stopping") 182 183 err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{ 184 All: true, 185 }) 186 if err != nil { 187 fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error()) 188 os.Exit(1) 189 } 190 191 os.Exit(0) 192 } 193 } 194 }