github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/docs/ebpf/guides/getting-started.md (about) 1 # Getting Started with eBPF in Go 2 3 In this guide, we'll walk you through building a new eBPF-powered Go application 4 from scratch. We'll introduce the toolchain, write a minimal eBPF C example and 5 compile it using bpf2go. Then, we'll put together a Go 6 application that loads the eBPF program into the kernel and periodically 7 displays its output. 8 9 The application attaches an eBPF program to an XDP hook that counts the number 10 of packets received by a physical interface. Filtering and modifying packets is 11 a major use case for eBPF, so you'll see a lot of its features being geared 12 towards it. However, eBPF's capabilities are ever-growing, and it has been 13 adopted for tracing, systems and application observability, security and much 14 more. 15 16 ## eBPF C program 17 18 !!! abstract "Dependencies" 19 To follow along with the example, you'll need: 20 21 * Linux kernel version 5.7 or later, for bpf_link support 22 * LLVM 11 or later [^1] (`clang` and `llvm-strip`) 23 * libbpf headers [^2] 24 * Linux kernel headers [^3] 25 * Go compiler version supported by {{ proj }}'s Go module 26 27 [^1]: 28 Use `clang --version` to check which version of LLVM you have installed. 29 Refer to your distribution's package index to finding the right packages to 30 install, as this tends to vary wildly across distributions. Some 31 distributions ship `clang` and `llvm-strip` in separate packages. 32 33 [^2]: 34 For Debian/Ubuntu, you'll typically need `libbpf-dev`. On Fedora, it's 35 `libbpf-devel`. 36 37 [^3]: 38 On AMD64 Debian/Ubuntu, install `linux-headers-amd64`. On Fedora, install 39 `kernel-devel`. 40 41 On Debian, you may also need `ln -sf /usr/include/asm-generic/ 42 /usr/include/asm` since the example expects to find `<asm/types.h>`. 43 44 Let's begin by writing our eBPF C program, as its structure will be used as the 45 basis for generating Go boilerplate. 46 47 Click the :material-plus-circle: annotations in the code snippet for a detailed 48 explanation of the individual components. 49 50 {{ c_example('getting_started_counter', title='counter.c') }} 51 52 1. When putting C files alongside Go files, they need to be excluded by a Go 53 build tag, otherwise `go build` will complain with `C source files not 54 allowed when not using cgo or SWIG`. The Go toolchain can safely ignore our 55 eBPF C files. 56 57 2. Include headers containing the C macros used in the example. Identifiers such 58 as `__u64` and `BPF_MAP_TYPE_ARRAY` are shipped by the Linux kernel, with 59 `__uint`, `__type`, `SEC` and BPF helper definitions being provided by 60 libbpf. 61 62 3. Declare a BPF map called `pkt_count`, an Array-type Map holding a single 63 u64 value. See `man bpf` or the online [bpf man 64 pages](https://man7.org/linux/man-pages/man2/bpf.2.html) for an overview of 65 all available map types.<br/><br/> 66 For this example, we went with an array since it's a well-known data 67 structure you're likely familiar with. In BPF, arrays are preallocated and 68 zeroed, making them safe and ready to use without any initialization. 69 70 4. The Map definition is placed in the `.maps` ELF section, which is where {{ 71 proj }} expects to find it. 72 73 5. In BPF, not all programs are equal. Some act on raw packets, some execute 74 within the context of kernel or user space functions, while others expect to 75 be run against an `__sk_buff`. These differences are encoded in the Program 76 Type. libbpf introduced a set of conventions around which ELF sections 77 correspond to which type. In this example, we've chosen `xdp` since we'll 78 attach this program to the XDP hook later. 79 80 6. There's only one possible element in `pkt_count` since we've specified a 81 `max_entries` value of 1. We'll always access the 0th element of the array. 82 83 7. Here, we're asking the BPF runtime for a pointer to the 0th element of the 84 `pkt_count` Map. <br/><br/> 85 `bpf_map_lookup_elem` is a BPF helper declared in `docs.h`. Helpers are small 86 pieces of logic provided by the kernel that enable a BPF program to interact 87 with its context or other parts of the kernel. Discover all BPF helpers 88 supported by your kernel using `man bpf-helpers` or the online [bpf-helpers 89 man pages](https://man7.org/linux/man-pages/man7/bpf-helpers.7.html). 90 91 8. All Map lookups can fail. If there's no element for the requested `key` in 92 the Map, `count` will hold a null pointer. The BPF verifier is very strict 93 about checking access to potential null pointers, so any further access 94 to `count` needs to be gated by a null check. 95 96 9. Atomically increase the value pointed to by `count` by 1. It's important to 97 note that on systems with SMP enabled (most systems nowadays), the same BPF 98 program can be executed concurrently.<br/><br/> 99 Even though we're loading only one 'copy' of our Program, accompanied by a 100 single `pkt_count` Map, the kernel may need to process incoming packets on 101 multiple receive queues in parallel, leading to multiple instances of the 102 program being executed, and `pkt_count` effectively becoming a piece of 103 shared memory. Use atomics to avoid dirty reads/writes. 104 105 10. XDP allows for dropping packets early, way before it's passed to the 106 kernel's networking stack where routing, firewalling (ip/nftables) and things 107 like TCP and sockets are implemented. We issue the `XDP_PASS` verdict to 108 avoid ever interfering with the kernel's network stack. 109 110 11. Since some BPF helpers allow calling kernel code licensed under GPLv2, BPF 111 programs using specific helpers need to declare they're (at least partially) 112 licensed under GPL. Dual-licensing is possible, which we've opted for here 113 with `Dual MIT/GPL`, since {{ proj }} is MIT-licensed. 114 115 Create an empty directory and save this file as `counter.c`. In the next step, 116 we'll set up the necessary bits to compile our eBPF C program using `bpf2go`. 117 118 ## Compile eBPF C and generate scaffolding using bpf2go 119 120 With the `counter.c` source file in place, create another file called `gen.go` 121 containing a `//go:generate` statement. This invokes `bpf2go` when running `go 122 generate` in the project directory. 123 124 Aside from compiling our eBPF C program, bpf2go will also generate some 125 scaffolding code we'll use to load our eBPF program into the kernel and interact 126 with its various components. This greatly reduces the amount of code we need to 127 write to get up and running. 128 129 {{ go_example('getting_started_gen', title='gen.go') }} 130 131 !!! tip "" 132 Using a dedicated file for your package's `//go:generate` statement(s) is 133 neat for keeping them separated from application logic. At this point in the 134 guide, we don't have a `main.go` file yet. Feel free to include it in 135 existing Go source files if you prefer. 136 137 Before using the Go toolchain, Go wants us to declare a Go module. This command 138 should take care of that: 139 140 ```{ .shell-session data-copy="go mod init ebpf-test && go mod tidy" } 141 % go mod init ebpf-test 142 go: creating new go.mod: module ebpf-test 143 go: to add module requirements and sums: 144 go mod tidy 145 % go mod tidy 146 ``` 147 148 We also need to manually add a dependency on `bpf2go` since it's not explicitly 149 imported by a `.go` source file: 150 151 ```{ .shell-session data-copy="go get github.com/cilium/ebpf/cmd/bpf2go" } 152 % go get github.com/cilium/ebpf/cmd/bpf2go 153 go: added github.com/cilium/ebpf v0.11.0 154 go: added golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 155 go: added golang.org/x/sys v0.6.0 156 ``` 157 158 Now we're ready to run `go generate`: 159 160 ```{ .shell-session data-copy="go generate" } 161 % go generate 162 Compiled /home/timo/getting_started/counter_bpfel.o 163 Stripped /home/timo/getting_started/counter_bpfel.o 164 Wrote /home/timo/getting_started/counter_bpfel.go 165 Compiled /home/timo/getting_started/counter_bpfeb.o 166 Stripped /home/timo/getting_started/counter_bpfeb.o 167 Wrote /home/timo/getting_started/counter_bpfeb.go 168 ``` 169 170 `bpf2go` built `counter.c` into `counter_bpf*.o` behind the scenes using 171 `clang`. It generated two object files and two corresponding Go source files 172 based on the contents of the object files. Do not remove any of these, we'll 173 need them later. 174 175 Let's inspect one of the generated .go files: 176 177 {{ go_example('counterPrograms', title='counter_bpfel.go', signature=True) }} 178 179 Neat! Looks like bpf2go automatically generated a scaffolding for interacting 180 with our `count_packets` Program from Go. In the next step, we'll explore how to 181 load our program into the kernel and put it to work by attaching it to an XDP 182 hook! 183 184 ## The Go application 185 186 Finally, with our eBPF C code compiled and Go scaffolding generated, all that's 187 left is writing the Go code responsible for loading and attaching the program to 188 a hook in the Linux kernel. 189 190 Click the :material-plus-circle: annotations in the code snippet for some of the 191 more intricate details. Note that we won't cover anything related to the Go 192 standard library here. 193 194 {{ go_example('getting_started_main', title='main.go') }} 195 196 1. Linux kernels before 5.11 use RLIMIT_MEMLOCK to control the maximum amount of 197 memory allocated for a process' eBPF resources. By default, it's set to a 198 relatively low value. See [Resource Limits](../concepts/rlimit.md) for a deep 199 dive. 200 201 1. `counterObjects` is a struct containing nil pointers to Map and Program 202 objects. A subsequent call to `loadCounterObjects` populates these fields 203 based on the struct tags declared on them. This mechanism saves a lot of 204 repetition that would occur by checking a Collection for Map and Program 205 objects by string.<br/><br/> 206 As an added bonus, `counterObjects` adds type safety by turning these into 207 compile-time lookups. If a Map or Program doesn't appear in the ELF, it won't 208 appear as a struct field and your Go application won't compile, eliminating 209 a whole class of runtime errors. 210 211 1. Close all file descriptors held by `objs` right before the Go application 212 terminates. See [Object Lifecycle](../concepts/object-lifecycle.md) for a 213 deep dive. 214 215 1. Associate the `count_packets` (stylized in the Go scaffolding as 216 `CountPackets`) eBPF program with `eth0`. This returns a {{ 217 godoc('link/Link') }} abstraction. 218 219 1. Close the file descriptor of the Program-to-interface association. Note that 220 this will stop the Program from executing on incoming packets if the Link was 221 not {{ godoc('link/Link.Pin') }}ed to the bpf file system. 222 223 1. Load a uint64 stored at index 0 from the `pkt_count` Map (stylized in the Go 224 scaffolding as `PktCount`). This corresponds to the logic in `counter.c`. 225 226 Save this file as `main.go` in the same directory alongside `counter.c` and 227 `gen.go`. 228 229 ## Building and running the Go application 230 231 Now `main.go` is in place, we can finally compile and run our Go application! 232 233 ```{ .shell-session data-copy="go build && sudo ./ebpf-test" } 234 % go build && sudo ./ebpf-test 235 2023/09/20 17:18:43 Counting incoming packets on eth0.. 236 2023/09/20 17:18:47 Received 0 packets 237 2023/09/20 17:18:48 Received 4 packets 238 2023/09/20 17:18:49 Received 11 packets 239 2023/09/20 17:18:50 Received 15 packets 240 ``` 241 242 Generate some traffic on eth0 and you should see the counter increase. 243 244 ### Iteration Workflow 245 246 When iterating on the C code, make sure to keep generated files up-to-date. 247 Without re-running bpf2go, the eBPF C won't be recompiled, and any changes made 248 to the C program structure won't be reflected in the Go scaffolding. 249 250 ```{ .shell-session data-copy="go generate && go build && sudo ./ebpf-test" } 251 % go generate && go build && sudo ./ebpf-test 252 ``` 253 254 ## What's Next? 255 256 Congratulations, you've just built your (presumably) first eBPF-powered Go app! 257 Hopefully, this guide piqued your interest and gave you a better sense of what 258 eBPF can do and how it works. 259 260 With XDP, we've only barely scratched the surface of eBPF's many use cases and 261 applications. For more easily-accessible examples, see [the main repository's 262 examples/ folder](https://github.com/cilium/ebpf/tree/main/examples). It 263 demonstrates use cases like tracing user space applications, extracting 264 information from the kernel, attaching eBPF programs to network sockets and 265 more. 266 267 Follow our other guides to continue on your journey of shipping a portable 268 eBPF-powered application to your users.