Create BPF mapping method

💡 How to create a BPF map

Programmers often send messages to programs to cause program behavior to be called. The most magical function of bpf is the code running in the kernel, and the program loading these codes can realize real-time communication through message passing.

BPF mapping is that key values are stored in the kernel and can be accessed by BPF programs. User space programs can also access BPF mappings through file descriptors. BPF mapping can store any type of data with the size specified in advance.

The kernel takes the key value as a binary block. The kernel is not related to the specific content of bpf mapping, and the verifier ensures the security.

Create BPF mapping

The most direct way to create BPF mapping is to use BPF system call, and the first parameter is set to BPF_MAP_CREATE, it means to create a new mapping.

This call returns the file descriptor associated with creating the map. The second parameter of bpf system call is as follows:

https://man7.org/linux/man-pages/man2/bpf.2.htm[1]

BPF_PROG_LOAD
              Verify and load an eBPF program, returning a new file
              descriptor associated with the program.  The close-on-exec
              file descriptor flag (seefcntl(2)) is automatically
              enabled for the new file descriptor.

              Thebpf_attr union consists of various anonymous
              structures that are used by differentbpf() commands:

           union bpf_attr {
               struct {    /* Used by BPF_MAP_CREATE */
                   __u32         map_type;
                   __u32         key_size;    /* size of key in bytes */
                   __u32         value_size;  /* size of value in bytes */
                   __u32         max_entries; /* maximum number of entries
                                                 in a map */
               };

               struct {    /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY
                              commands */
                   __u32         map_fd;
                   __aligned_u64 key;
                   union {
                       __aligned_u64 value;
                       __aligned_u64 next_key;
                   };
                   __u64         flags;
               };

               struct {    /* Used by BPF_PROG_LOAD */
                   __u32         prog_type;
                   __u32         insn_cnt;
                   __aligned_u64 insns;      /* 'const struct bpf_insn *' */
                   __aligned_u64 license;    /* 'const char *' */
                   __u32         log_level;  /* verbosity level of verifier */
                   __u32         log_size;   /* size of user buffer */
                   __aligned_u64 log_buf;    /* user supplied 'char *'
                                                buffer */
                   __u32         kern_version;
                                             /* checked when prog_type=kprobe
                                                (since Linux 4.1) */
               };
           } __attribute__((aligned(8)));

If the system call fails, the kernel returns - 1; There are three main reasons for failure: invalid attribute EINVAL, insufficient execution permission EPERM, and insufficient memory to save mapping ENOMEM.

Creating BPF mappings using ELF conventions

The kernel includes conventions and helper functions for generating and using BPF mappings.

Such as help function bpf_create_map encapsulates the above code.

Mappings can be predefined:

struct bpf_map_def SEC("maps") my_map = {
    .type = BPF_MAP_TYPE_HASH,
    .key_size= sizeof(int),
    .value_size=sizeof(int),
    .max_entries=100,
    .map_flags = BPF_F_NO_PREALLOC,
};

In this way, the section attribute is used to define the mapping. In this example, it is SEC ("maps"). Tell the kernel to create a bpf mapping when the structure is changed, and tell the kernel to create a corresponding mapping.

Once the mappings are initialized, they can be used to pass messages between the kernel and user space.

#include <errno.h>
#include <linux/bpf.h>
#include <stdio.h>
#include <string.h>
#include "bpf.h"

static const char *file_path = "/sys/fs/bpf/my_array";

int main(int argc, char **argv) {
  int key, value, fd, added, pinned;

  fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 100, 0);
  if (fd < 0) {
    printf("Failed to create map: %d (%s)\n", fd, strerror(errno));
    return -1;
  }

  key = 1, value = 1234;
  added = bpf_map_update_elem(fd, &key, &value, BPF_ANY);
  if (added < 0) {
    printf("Failed to update map: %d (%s)\n", added, strerror(errno));
    return -1;
  }

  pinned = bpf_obj_pin(fd, file_path);
  if (pinned < 0) {
    printf("Failed to pin map to the file system: %d (%s)\n", pinned,
           strerror(errno));
    return -1;
  }

  return 0;
}

Welcome to WeChat official account:

💡 Tip: calico: Here's @ tomkinsda's #fosdem 2022 talk title '2-cluster #Kubernetes, with #Calico, #BGP Interconnect and #WireGuard... All Without Leaving Your Laptop!' Stay tuned for the fun surprise at the end.

https://video.fosdem.org/2022/D.network/2_cluster_kubernetes_with_calico_bgp_interconnect_and_wireguard_all_without_leaving_your_laptop.webm[2]

reference material

[1]

https://man7.org/linux/man-pages/man2/bpf.2.htm: https://man7.org/linux/man-pages/man2/bpf.2.htm

[2]

https://video.fosdem.org/2022/D.network/2_cluster_kubernetes_with_calico_bgp_interconnect_and_wireguard_all_without_leaving_your_laptop.webm: https://man7.org/linux/man-pages/man2/bpf.2.htm

Keywords: Go Linux Operation & Maintenance Kubernetes server

Added by onlyican on Tue, 08 Feb 2022 23:41:32 +0200