英文:
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__ = "User space program to load the sockops bpf program to register sockets\n";
#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;
// Open the cgroup fd -- needed for both attach and detach operations.
fprintf(stdout, "Opening cgroup file %s\n", cgroup_filename);
cgroup_fd = open(cgroup_dir, O_RDONLY);
if (cgroup_fd < 0) {
fprintf(stderr, "ERR: opening cgroup file %s\n", 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, "bpf_add_to_sockhash");
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't support sockops yet.
err = bpf_prog_attach(bpf_program__fd(sockops_prog), cgroup_fd, BPF_CGROUP_SOCK_OPS, 0);
if (err) {
fprintf(stderr, "ERR: attaching program\n");
goto fail;
}
fprintf(stdout, "Successfully loaded BPF program.\n");
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 <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 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(".maps");
// `sock_key' is a key for the sockmap
struct sock_key {
__u32 sip4;
__u32 dip4;
__u32 sport;
__u32 dport;
} __attribute__((packed));
// `sk_extract_key' extracts the key from the `bpf_sock_ops' struct
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("Got new operation %d for socket.\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("Failed to update sockmap: %d\n", ret);
} else {
bpf_printk("Added new socket to sockmap\n");
}
}
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(".maps");
You will need to specify a pinning path when opening the object file with bpf_object__open_file
. You have to set opts->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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论