有没有一种方法可以在 ebpf 程序和用户空间程序之间共享一些 ebpf 映射,这些程序具有使用 libbpf 作为键的值结构
is there a way I can share some ebpf map between ebpf program and userspace program that has value struct using libbpf for keys
所以我创建了一个 BPF_MAP_TYPE_ARRAY 类型的地图。
struct share_me
{
struct iphdr dest_ip;
};
struct bpf_map_def SEC("maps") ip_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(struct share_me),
.max_entries = 64, /* Assume netdev has no more than 64 queues */
};
所以 ip_map 是我的地图及其定义的和 SEC elf 部分,用于在上述定义中创建地图
在我正在做的ebpf程序函数中
SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx)
{
int index = ctx->rx_queue_index;
__u32 *pkt_count;
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
struct share_me me;
if ((void *)eth + sizeof(*eth) <= data_end)
{
struct iphdr *ip = data + sizeof(*eth);
//me.dest_ip=ip;
memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
bpf_map_lookup_elem(&ip_map, &index);
bpf_map_update_elem(&ip_map,&index,&me,0);
所以我正在用我的结构对象
更新ip_map键的当前值
这就是我在 usespace 程序中所做的
bpf_obj = load_bpf_and_xdp_attach(&cfg);
if (!bpf_obj) {
/* Error handling done in load_bpf_and_xdp_attach() */
exit(EXIT_FAILURE);
}
/* We also need to load the xsks_map */
map1 = bpf_object__find_map_by_name(bpf_obj, "ip_map");
xsks_map_fd = bpf_map__fd(map);
map_fd = bpf_map__fd(map1);
if(map_fd<0)
{
printf("map_fd <0\n");
exit(0);
}
if (xsks_map_fd < 0) {
fprintf(stderr, "ERROR: no xsks map found: %s\n",
strerror(xsks_map_fd));
exit(EXIT_FAILURE);
}
这是 load_bpf_and_xdp_attach 函数,只是 bpf/libbpf 的包装器,因此调用 load_bpf_object_file 等,
struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg)
{
struct bpf_program *bpf_prog;
struct bpf_object *bpf_obj;
int offload_ifindex = 0;
int prog_fd = -1;
int err;
/* If flags indicate hardware offload, supply ifindex */
if (cfg->xdp_flags & XDP_FLAGS_HW_MODE)
offload_ifindex = cfg->ifindex;
/* Load the BPF-ELF object file and get back libbpf bpf_object */
if (cfg->reuse_maps)
bpf_obj = load_bpf_object_file_reuse_maps(cfg->filename,
offload_ifindex,
cfg->pin_dir);
else
bpf_obj = load_bpf_object_file(cfg->filename, offload_ifindex);
if (!bpf_obj) {
fprintf(stderr, "ERR: loading file: %s\n", cfg->filename);
exit(EXIT_FAIL_BPF);
}
/* At this point: All XDP/BPF programs from the cfg->filename have been
* loaded into the kernel, and evaluated by the verifier. Only one of
* these gets attached to XDP hook, the others will get freed once this
* process exit.
*/
if (cfg->progsec[0])
/* Find a matching BPF prog section name */
bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec);
else
/* Find the first program */
bpf_prog = bpf_program__next(NULL, bpf_obj);
if (!bpf_prog) {
fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec);
exit(EXIT_FAIL_BPF);
}
strncpy(cfg->progsec, bpf_program__title(bpf_prog, false), sizeof(cfg->progsec));
prog_fd = bpf_program__fd(bpf_prog);
if (prog_fd <= 0) {
fprintf(stderr, "ERR: bpf_program__fd failed\n");
exit(EXIT_FAIL_BPF);
}
/* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd
* is our select file-descriptor handle. Next step is attaching this FD
* to a kernel hook point, in this case XDP net_device link-level hook.
*/
err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd);
if (err)
exit(err);
return bpf_obj;
}
但我收到错误
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
; int index = ctx->rx_queue_index;
0: (61) r2 = *(u32 *)(r1 +16)
; int index = ctx->rx_queue_index;
1: (63) *(u32 *)(r10 -4) = r2
; void *data_end = (void *)(long)ctx->data_end;
2: (61) r2 = *(u32 *)(r1 +4)
; void *data = (void *)(long)ctx->data;
3: (61) r1 = *(u32 *)(r1 +0)
; if ((void *)eth + sizeof(*eth) <= data_end)
4: (07) r1 += 14
; if ((void *)eth + sizeof(*eth) <= data_end)
5: (2d) if r1 > r2 goto pc+25
R1_w=pkt(id=0,off=14,r=14,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R10=fp0 fp-8=mmmm????
; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet
processed 7 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
libbpf: -- END LOG --
libbpf: failed to load program 'xdp_sock'
libbpf: failed to load object 'af_xdp_kern.o'
ERR: loading BPF-OBJ file(af_xdp_kern.o) (-22): Invalid argument
ERR: loading file: af_xdp_kern.o
所以我在加载我的 ebpf 程序时得到 invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14) R1 offset is outside of the packet
我有点确定错误是因为我使用结构值作为类型 BPF_MAP_TYPE_ARRY 值
的映射
所以我想知道如果我使用允许结构作为映射键值的 libbpf,是否还有其他 MAP 类型
TL;DR. 问题是您正在 out-of-bound 访问来自验证者的数据包 观点看法。您需要首先检查数据包是否足够长以实际包含 IP header。
正在读取验证程序错误消息。
; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet
验证器在指令 6 上出错,对应于 memcpy
语句。它指出您正在对数据包进行无效访问,R1
中的偏移量超出了数据包的已知范围。
您检查过数据包是否至少足以容纳以太网 header,但您从未检查过它是否足够长以容纳 IP header。所以验证者看到你试图访问以太网之外的字节 header(最多偏移量 30)和错误。
正在更新数据包边界检查。
如果您从一开始就知道您需要阅读以太网和 IP headers,您可以从以下位置更新您的检查:
if ((void *)eth + sizeof(*eth) <= data_end)
至:
if ((void *)eth + sizeof(*eth) + sizeof(struct iphdr) <= data_end)
所以我创建了一个 BPF_MAP_TYPE_ARRAY 类型的地图。
struct share_me
{
struct iphdr dest_ip;
};
struct bpf_map_def SEC("maps") ip_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(struct share_me),
.max_entries = 64, /* Assume netdev has no more than 64 queues */
};
所以 ip_map 是我的地图及其定义的和 SEC elf 部分,用于在上述定义中创建地图
在我正在做的ebpf程序函数中
SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx)
{
int index = ctx->rx_queue_index;
__u32 *pkt_count;
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
struct share_me me;
if ((void *)eth + sizeof(*eth) <= data_end)
{
struct iphdr *ip = data + sizeof(*eth);
//me.dest_ip=ip;
memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
bpf_map_lookup_elem(&ip_map, &index);
bpf_map_update_elem(&ip_map,&index,&me,0);
所以我正在用我的结构对象
更新ip_map键的当前值这就是我在 usespace 程序中所做的
bpf_obj = load_bpf_and_xdp_attach(&cfg);
if (!bpf_obj) {
/* Error handling done in load_bpf_and_xdp_attach() */
exit(EXIT_FAILURE);
}
/* We also need to load the xsks_map */
map1 = bpf_object__find_map_by_name(bpf_obj, "ip_map");
xsks_map_fd = bpf_map__fd(map);
map_fd = bpf_map__fd(map1);
if(map_fd<0)
{
printf("map_fd <0\n");
exit(0);
}
if (xsks_map_fd < 0) {
fprintf(stderr, "ERROR: no xsks map found: %s\n",
strerror(xsks_map_fd));
exit(EXIT_FAILURE);
}
这是 load_bpf_and_xdp_attach 函数,只是 bpf/libbpf 的包装器,因此调用 load_bpf_object_file 等,
struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg)
{
struct bpf_program *bpf_prog;
struct bpf_object *bpf_obj;
int offload_ifindex = 0;
int prog_fd = -1;
int err;
/* If flags indicate hardware offload, supply ifindex */
if (cfg->xdp_flags & XDP_FLAGS_HW_MODE)
offload_ifindex = cfg->ifindex;
/* Load the BPF-ELF object file and get back libbpf bpf_object */
if (cfg->reuse_maps)
bpf_obj = load_bpf_object_file_reuse_maps(cfg->filename,
offload_ifindex,
cfg->pin_dir);
else
bpf_obj = load_bpf_object_file(cfg->filename, offload_ifindex);
if (!bpf_obj) {
fprintf(stderr, "ERR: loading file: %s\n", cfg->filename);
exit(EXIT_FAIL_BPF);
}
/* At this point: All XDP/BPF programs from the cfg->filename have been
* loaded into the kernel, and evaluated by the verifier. Only one of
* these gets attached to XDP hook, the others will get freed once this
* process exit.
*/
if (cfg->progsec[0])
/* Find a matching BPF prog section name */
bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec);
else
/* Find the first program */
bpf_prog = bpf_program__next(NULL, bpf_obj);
if (!bpf_prog) {
fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec);
exit(EXIT_FAIL_BPF);
}
strncpy(cfg->progsec, bpf_program__title(bpf_prog, false), sizeof(cfg->progsec));
prog_fd = bpf_program__fd(bpf_prog);
if (prog_fd <= 0) {
fprintf(stderr, "ERR: bpf_program__fd failed\n");
exit(EXIT_FAIL_BPF);
}
/* At this point: BPF-progs are (only) loaded by the kernel, and prog_fd
* is our select file-descriptor handle. Next step is attaching this FD
* to a kernel hook point, in this case XDP net_device link-level hook.
*/
err = xdp_link_attach(cfg->ifindex, cfg->xdp_flags, prog_fd);
if (err)
exit(err);
return bpf_obj;
}
但我收到错误
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
; int index = ctx->rx_queue_index;
0: (61) r2 = *(u32 *)(r1 +16)
; int index = ctx->rx_queue_index;
1: (63) *(u32 *)(r10 -4) = r2
; void *data_end = (void *)(long)ctx->data_end;
2: (61) r2 = *(u32 *)(r1 +4)
; void *data = (void *)(long)ctx->data;
3: (61) r1 = *(u32 *)(r1 +0)
; if ((void *)eth + sizeof(*eth) <= data_end)
4: (07) r1 += 14
; if ((void *)eth + sizeof(*eth) <= data_end)
5: (2d) if r1 > r2 goto pc+25
R1_w=pkt(id=0,off=14,r=14,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R10=fp0 fp-8=mmmm????
; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet
processed 7 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
libbpf: -- END LOG --
libbpf: failed to load program 'xdp_sock'
libbpf: failed to load object 'af_xdp_kern.o'
ERR: loading BPF-OBJ file(af_xdp_kern.o) (-22): Invalid argument
ERR: loading file: af_xdp_kern.o
所以我在加载我的 ebpf 程序时得到 invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14) R1 offset is outside of the packet
我有点确定错误是因为我使用结构值作为类型 BPF_MAP_TYPE_ARRY 值
所以我想知道如果我使用允许结构作为映射键值的 libbpf,是否还有其他 MAP 类型
TL;DR. 问题是您正在 out-of-bound 访问来自验证者的数据包 观点看法。您需要首先检查数据包是否足够长以实际包含 IP header。
正在读取验证程序错误消息。
; memcpy(&me.dest_ip,ip,sizeof(struct iphdr));
6: (61) r2 = *(u32 *)(r1 +16)
invalid access to packet, off=30 size=4, R1(id=0,off=30,r=14)
R1 offset is outside of the packet
验证器在指令 6 上出错,对应于 memcpy
语句。它指出您正在对数据包进行无效访问,R1
中的偏移量超出了数据包的已知范围。
您检查过数据包是否至少足以容纳以太网 header,但您从未检查过它是否足够长以容纳 IP header。所以验证者看到你试图访问以太网之外的字节 header(最多偏移量 30)和错误。
正在更新数据包边界检查。
如果您从一开始就知道您需要阅读以太网和 IP headers,您可以从以下位置更新您的检查:
if ((void *)eth + sizeof(*eth) <= data_end)
至:
if ((void *)eth + sizeof(*eth) + sizeof(struct iphdr) <= data_end)