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.