在eBPF中不断增加地图标识符(map ids)。

huangapple go评论108阅读模式
英文:

Continuously increasing map ids in eBPF

问题

I am running eBPF sockops program. During testing, I need to load and reload the programs several times.

以下是用户空间程序我使用来加载和附加BPF程序的部分翻译:

static const char *__doc__ = "用户空间程序,用于加载sockops bpf程序以注册套接字";

#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

const char *cgroup_dir = "/sys/fs/cgroup/unified";

int main(int argc, char **argv) {
  int err, len;
  int cgroup_fd;
  const char *sockops_file = "bpf_sockops.o";
  struct bpf_object *sockhash_obj;
  bool do_unload = false;

  // 打开cgroup文件描述符,这在附加和分离操作中都需要。
  fprintf(stdout, "打开cgroup文件 %s\n", cgroup_filename);
  cgroup_fd = open(cgroup_dir, O_RDONLY);
  if (cgroup_fd < 0) {
    fprintf(stderr, "错误: 打开cgroup文件 %s\n", strerror(errno));
    goto exit_cgroup;
  }

  // 检查是否要卸载程序。
  if (do_unload) {
    // 卸载sockops程序
    err = bpf_prog_detach(cgroup_fd, BPF_CGROUP_SOCK_OPS);
    if (err) {
      goto fail;
    }

    return 0;
  }

  // 如果尚未附加,打开、加载并附加sockops_obj
  sockhash_obj = bpf_object__open_file(sockops_file, NULL);
  if (libbpf_get_error(sockhash_obj)) {
    goto fail;
  }

  struct bpf_program *sockops_prog = bpf_object__find_program_by_name(sockhash_obj, "bpf_add_to_sockhash");
  if (!sockops_prog) {
    goto fail;
  }

  // 加载sockops程序
  err = bpf_object__load(sockhash_obj);
  if (err) {
    goto fail;
  }

  // 附加sockops程序
  // 使用核心BPF API,因为libbpf尚不支持sockops。
  err = bpf_prog_attach(bpf_program__fd(sockops_prog), cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
  if (err) {
    fprintf(stderr, "错误: 附加程序\n");
    goto fail;
  }

  fprintf(stdout, "成功加载BPF程序。\n");

  return 0;

exit_cgroup:
  close(cgroup_fd);

fail:
  return -1;
}

我进行的关键调用来附加/分离用户空间程序中的sockops程序的部分翻译:

err = bpf_prog_attach(bpf_program__fd(sockops_prog), cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
...
...
...
err = bpf_prog_detach(cgroup_fd, BPF_CGROUP_SOCK_OPS);

我的sockops程序看起来像这样:

// 文件名:bpf_sockops.c
#include <linux/in.h>
#include <linux/tcp.h>

#include <linux/bpf.h>
#include <sys/socket.h>

#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>

char LICENSE[] SEC("license") = "GPL";

// sock_ops_map将sock_ops键映射到套接字描述符
struct {
  __uint(type, BPF_MAP_TYPE_SOCKHASH);
  __uint(max_entries, 65535);
  __type(key, struct sock_key);
  __type(value, __u64);
} sock_ops_map SEC(".maps");

// 'sock_key'是sockmap的键
struct sock_key {
  __u32 sip4;
  __u32 dip4;
  __u32 sport;
  __u32 dport;
} __attribute__((packed));

// 'sk_extract_key'从'bpf_sock_ops'结构中提取键
static inline void sk_extract_key(struct bpf_sock_ops *ops,
                                  struct sock_key *key) {
  key->dip4 = ops->remote_ip4;
  key->sip4 = ops->local_ip4;
  key->sport = (bpf_htonl(ops->local_port) >> 16);
  key->dport = ops->remote_port >> 16;
}

SEC("sockops")
int bpf_add_to_sockhash(struct bpf_sock_ops *skops) {
  __u32 family, op;

  family = skops->family;
  op = skops->op;
  bpf_printk("收到套接字的新操作 %d。\n", op);

  switch (op) {
    case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
    case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
      if (family == AF_INET) {
        struct sock_key key = {};
        sk_extract_key(skops, &key);
        int ret = bpf_sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST);
        if (ret != 0) {
          bpf_printk("无法更新sockmap:%d\n", ret);
        } else {
          bpf_printk("将新套接字添加到sockmap\n");
        }
      }
      break;
    default:
      break;
  }
  return 0;
}

由于我多次重新加载程序,'sock_ops_map'的映射ID不断增加:

~/ebpf-code$ sudo bpftool prog show
1751: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1752: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1753: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1754: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 409

<details>
<summary>英文:</summary>

I am running eBPF `sockops` program. During testing, I need to load and reload the programs several times.

Here is the userspace program I use to load and attach the BPF program:
```c
static const char *__doc__ = &quot;User space program to load the sockops bpf program to register sockets\n&quot;;

#include &lt;bpf/bpf.h&gt;
#include &lt;bpf/libbpf.h&gt;
#include &lt;errno.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;

const char *cgroup_dir = &quot;/sys/fs/cgroup/unified&quot;;

int main(int argc, char **argv) {
  int err, len;
  int cgroup_fd;
  const char *sockops_file = &quot;bpf_sockops.o&quot;;
  struct bpf_object *sockhash_obj;
  bool do_unload = false;

  // Open the cgroup fd -- needed for both attach and detach operations.
  fprintf(stdout, &quot;Opening cgroup file %s\n&quot;, cgroup_filename);
  cgroup_fd = open(cgroup_dir, O_RDONLY);
  if (cgroup_fd &lt; 0) {
    fprintf(stderr, &quot;ERR: opening cgroup file %s\n&quot;, strerror(errno));
    goto exit_cgroup;
  }

  // Check if the program is to be unloaded.
  if (do_unload) {
    // Unload the sockops program
    err = bpf_prog_detach(cgroup_fd, BPF_CGROUP_SOCK_OPS);
    if (err) {
      goto fail;
    }

    return 0;
  }

  // Open, load and attach sockops_obj if not already attached
  sockhash_obj = bpf_object__open_file(sockops_file, NULL);
  if (libbpf_get_error(sockhash_obj)) {
    goto fail;
  }

  struct bpf_program *sockops_prog = bpf_object__find_program_by_name(sockhash_obj, &quot;bpf_add_to_sockhash&quot;);
  if (!sockops_prog) {
    goto fail;
  }

  // Load the sockops program
  err = bpf_object__load(sockhash_obj);
  if (err) {
    goto fail;
  }

  // Attach the sockops program
  // Using core BPF API as libbpf doesn&#39;t support sockops yet.
  err = bpf_prog_attach(bpf_program__fd(sockops_prog), cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
  if (err) {
    fprintf(stderr, &quot;ERR: attaching program\n&quot;);
    goto fail;
  }

  fprintf(stdout, &quot;Successfully loaded BPF program.\n&quot;);

  return 0;

exit_cgroup:
  close(cgroup_fd);

fail:
  return -1;
}

The key calls that I make to attach/detach the sockops program from the userspace program are:

err = bpf_prog_attach(bpf_program__fd(sockops_prog), cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
...
...
...
err = bpf_prog_detach(cgroup_fd, BPF_CGROUP_SOCK_OPS);

My sockops program looks like:

// File Name: bpf_sockops.c
#include &lt;linux/in.h&gt;
#include &lt;linux/tcp.h&gt;

#include &lt;linux/bpf.h&gt;
#include &lt;sys/socket.h&gt;

#include &lt;bpf/bpf_endian.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;

char LICENSE[] SEC(&quot;license&quot;) = &quot;GPL&quot;;

// sock_ops_map maps the sock_ops key to a socket descriptor
struct {
  __uint(type, BPF_MAP_TYPE_SOCKHASH);
  __uint(max_entries, 65535);
  __type(key, struct sock_key);
  __type(value, __u64);
} sock_ops_map SEC(&quot;.maps&quot;);

// `sock_key&#39; is a key for the sockmap
struct sock_key {
  __u32 sip4;
  __u32 dip4;
  __u32 sport;
  __u32 dport;
} __attribute__((packed));

// `sk_extract_key&#39; extracts the key from the `bpf_sock_ops&#39; struct
static inline void sk_extract_key(struct bpf_sock_ops *ops,
                                  struct sock_key *key) {
  key-&gt;dip4 = ops-&gt;remote_ip4;
  key-&gt;sip4 = ops-&gt;local_ip4;
  key-&gt;sport = (bpf_htonl(ops-&gt;local_port) &gt;&gt; 16);
  key-&gt;dport = ops-&gt;remote_port &gt;&gt; 16;
}

SEC(&quot;sockops&quot;)
int bpf_add_to_sockhash(struct bpf_sock_ops *skops) {
  __u32 family, op;

  family = skops-&gt;family;
  op = skops-&gt;op;
  bpf_printk(&quot;Got new operation %d for socket.\n&quot;, op);

  switch (op) {
    case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
    case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
      if (family == AF_INET) {
        struct sock_key key = {};
        sk_extract_key(skops, &amp;key);
        int ret = bpf_sock_hash_update(skops, &amp;sock_ops_map, &amp;key, BPF_NOEXIST);
        if (ret != 0) {
          bpf_printk(&quot;Failed to update sockmap: %d\n&quot;, ret);
        } else {
          bpf_printk(&quot;Added new socket to sockmap\n&quot;);
        }
      }
      break;
    default:
      break;
  }
  return 0;
}

As I reload the program several times, the map id for the sock_ops_map keeps on increasing:

~/ebpf-code$ sudo bpftool prog show
1751: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1752: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1753: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1754: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1755: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1756: cgroup_skb  tag 6deef7357e7b4530  gpl
        loaded_at 2023-06-21T23:42:28-0600  uid 0
        xlated 64B  jited 54B  memlock 4096B
1762: sock_ops  name bpf_add_to_sock  tag 79fcc2296545280f  gpl
        loaded_at 2023-06-21T23:53:36-0600  uid 0
        xlated 880B  jited 521B  memlock 4096B  map_ids 1219,1217,1220
        btf_id 1554

As shown above, the map ids are already 1217-1220. Is it expected for the map ids to be continuously increasing? What happens when the limit (or max map id) is reached?

答案1

得分: 2

> 地图ID是否应连续增加?

地图在最后一个引用地图消失后立即卸载。因此,当您使用 bpf_prog_detach 分离旧程序时,也会导致地图卸载。然后,当您使用 bpf_object__load 重新创建地图,从而增加其ID。

因此,这取决于您的目标,是否期望发生这种情况。如果您不希望这种情况发生,可以采取以下措施:

跨程序重新加载以保留地图的最简单方法是将地图固定。您可以通过在地图上设置 pinning 字段来实现:

struct {
  __uint(type, BPF_MAP_TYPE_SOCKHASH);
  __uint(max_entries, 65535);
  __type(key, struct sock_key);
  __type(value, __u64);
  __uint(pinning, LIBBPF_PIN_BY_NAME);
} sock_ops_map SEC(".maps");

当使用 bpf_object__open_file 打开对象文件时,您需要指定固定路径。您必须设置 opts->pin_root_path

> 当达到限制(或最大地图ID)时会发生什么?

ID将增加直到达到(1 << 32)-1,然后重新开始,内核将重用不再使用的ID,但不会在仍然存在的情况下重复使用ID。

英文:

> Is it expected for the map ids to be continuously increasing?

Maps are unloaded as soon as the last reference to the map goes away. So when you detach the old program with bpf_prog_detach you also cause the map to unload. Then when you bpf_object__load you re-create the map thus incrementing its ID.

So it depends on your goals if this is expected or not. If you don't want this to happen you can do the following:

The easiest way to persist the map across program reloads is to pin the map. You can do so my setting the pinning field on the map:

struct {
__uint(type, BPF_MAP_TYPE_SOCKHASH);
__uint(max_entries, 65535);
__type(key, struct sock_key);
__type(value, __u64);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} sock_ops_map SEC(&quot;.maps&quot;);

You will need to specify a pinning path when opening the object file with bpf_object__open_file. You have to set opts-&gt;pin_root_path.

> What happens when the limit (or max map id) is reached?

The ids will increase until (1 << 32)-1 then rollover and start from scratch, the kernel will re-use ids that are no longer in use, but never re-use an id if it still exists

huangapple
  • 本文由 发表于 2023年6月22日 14:18:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76529038.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定