bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_HASH, key_size=4, value_size=1, max_entries=1024, map_flags=0, inner_map_fd=0, map_name="whitelist_map", map_ifindex=0, btf_fd=0, btf_key_type_id=0, btf_value_type_id=0, btf_vmlinux_value_type_id=0}, 72) = 3 bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_PERF_EVENT_ARRAY, key_size=36, value_size=4, max_entries=256, map_flags=0, inner_map_fd=0, map_name="violation_event", map_ifindex=0, btf_fd=0, btf_key_type_id=0, btf_value_type_id=0, btf_vmlinux_value_type_id=0}, 72) = -1 EINVAL (Invalid argument) bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_PERF_EVENT_ARRAY, key_size=36, value_size=4, max_entries=256, map_flags=0, inner_map_fd=0, map_name="", map_ifindex=0, btf_fd=0, btf_key_type_id=0, btf_value_type_id=0, btf_vmlinux_value_type_id=0}, 72) = -1 EINVAL (Invalid argument) libbpf: failed to create map (name: 'violation_events'): Invalid argument libbpf: failed to load object 'xdp_whitelist_kern.o' bpf(BPF_MAP_UPDATE_ELEM, {map_fd=-1, key=0x7ffc815c7704, value=0x413254, flags=BPF_ANY}, 72) = -1 EBADF (Bad file descriptor) Failed to attach perf event: Bad file descriptor XDP program unloaded +++ exited with 0 +++
时间: 2025-07-22 14:28:44 浏览: 4
<think>我们注意到错误日志中,创建PERF_EVENT_ARRAY映射时,key_size=36和value_size=4,但根据内核要求,PERF_EVENT_ARRAY映射的key_size必须是4(表示CPU编号,u32类型),value_size必须是4(表示文件描述符,u32类型)。此外,映射名称不能为空字符串。
错误信息显示:
第一次创建:map_name="violation_event" -> 返回EINVAL(因为key_size=36,而不是4)
第二次创建:map_name="" -> 返回EINVAL(名称无效)
因此,我们需要在BPF程序中正确定义PERF_EVENT_ARRAY映射:
- key_size: sizeof(u32) 即4
- value_size: sizeof(u32) 即4
- 映射名称必须非空且唯一
另外,用户空间程序在创建映射失败后尝试使用一个无效的文件描述符(-1)更新映射,导致EBADF错误。
解决方案:
1. 在内核BPF程序(xdp_whitelist_kern.c)中,使用正确的参数定义PERF_EVENT_ARRAY映射。
2. 确保用户空间程序在加载BPF对象时,能够正确创建映射并获取文件描述符。
以下是修正后的BPF程序中的映射定义部分:</think>### 问题分析
错误日志显示关键问题仍然在创建 `BPF_MAP_TYPE_PERF_EVENT_ARRAY` 映射时:
```
bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_PERF_EVENT_ARRAY, key_size=36, value_size=4, ...}) = -1 EINVAL (Invalid argument)
```
主要问题有两个:
1. **键大小错误**:`key_size=36` 不符合要求(应为 `sizeof(u32)` = 4 字节)
2. **值大小错误**:`value_size=4` 不符合要求(应为 `sizeof(u32)` = 4 字节,但映射类型要求值大小为文件描述符大小)
### 解决方案
#### 1. 正确配置 PERF_EVENT_ARRAY 映射
在 eBPF 程序中,`BPF_MAP_TYPE_PERF_EVENT_ARRAY` 映射有严格的参数要求:
```c
// 正确配置的 eBPF 映射定义 (xdp_whitelist_kern.c)
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// PERF_EVENT_ARRAY 映射 - 用于向用户空间报告事件
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32)); // 必须是4字节 (CPU ID)
__uint(value_size, sizeof(u32)); // 必须是4字节 (文件描述符)
__uint(max_entries, 0); // 必须为0或CPU核心数
} violation_events SEC(".maps");
// 事件数据结构
struct violation_event {
u32 src_ip;
u32 dst_ip;
u16 src_port;
u16 dst_port;
u8 protocol;
} __attribute__((packed));
SEC("xdp")
int xdp_whitelist(struct xdp_md *ctx) {
// ... [数据包处理逻辑] ...
// 报告违规事件
struct violation_event event = {
.src_ip = key.src_ip,
.dst_ip = key.dst_ip,
.src_port = key.src_port,
.dst_port = key.dst_port,
.protocol = key.protocol
};
bpf_perf_event_output(ctx, &violation_events, BPF_F_CURRENT_CPU,
&event, sizeof(event));
return XDP_DROP;
}
```
#### 2. 用户空间程序正确初始化
用户空间程序需要正确设置 perf event 映射:
```c
// 用户空间加载器 (loader.c)
#include <bpf/libbpf.h>
#include <perf-monitor.h>
int main(int argc, char **argv) {
struct bpf_object *obj;
struct bpf_map *perf_map;
int err;
// 加载BPF对象
obj = bpf_object__open_file("xdp_whitelist_kern.o", NULL);
if (!obj) {
fprintf(stderr, "无法打开BPF对象文件\n");
return 1;
}
// 创建映射
err = bpf_object__load(obj);
if (err) {
fprintf(stderr, "加载BPF对象失败: %d\n", err);
return 1;
}
// 获取PERF_EVENT_ARRAY映射
perf_map = bpf_object__find_map_by_name(obj, "violation_events");
if (!perf_map) {
fprintf(stderr, "找不到violation_events映射\n");
return 1;
}
// 初始化perf缓冲区
struct perf_buffer *pb = perf_buffer__new(
bpf_map__fd(perf_map), // 映射文件描述符
8, // 每CPU缓冲区页数
handle_event, // 事件处理回调
NULL, // 丢失事件回调
NULL, // 上下文
NULL // 选项
);
if (!pb) {
fprintf(stderr, "创建perf缓冲区失败\n");
return 1;
}
// 事件处理循环
while (true) {
perf_buffer__poll(pb, 100); // 100ms超时
}
return 0;
}
```
### 关键修复点说明
1. **映射参数要求**:
```c
__uint(key_size, sizeof(u32)); // 必须是4字节
__uint(value_size, sizeof(u32)); // 必须是4字节
__uint(max_entries, 0); // 特殊值,表示使用CPU核心数
```
这是内核对 `BPF_MAP_TYPE_PERF_EVENT_ARRAY` 的硬性要求
2. **事件数据结构**:
```c
struct violation_event {
u32 src_ip;
u32 dst_ip;
u16 src_port;
u16 dst_port;
u8 protocol;
} __attribute__((packed));
```
使用 `packed` 属性避免编译器填充,确保数据布局正确
3. **事件输出函数**:
```c
bpf_perf_event_output(ctx, &violation_events, BPF_F_CURRENT_CPU,
&event, sizeof(event));
```
- 第一个参数:XDP 上下文
- 第二个参数:perf event 映射
- 第三个参数:标志位(`BPF_F_CURRENT_CPU` 表示使用当前CPU)
- 第四/五个参数:事件数据和大小
4. **用户空间初始化**:
```c
struct perf_buffer *pb = perf_buffer__new(
bpf_map__fd(perf_map), 8, handle_event, NULL, NULL, NULL);
```
- `8` 表示每CPU缓冲区大小(8页 = 32KB)
- `handle_event` 是用户定义的事件处理函数
### 完整验证流程
1. 编译 eBPF 程序:
```bash
clang -O2 -target bpf -c xdp_whitelist_kern.c -o xdp_whitelist_kern.o
```
2. 编译用户空间加载器:
```bash
gcc -o xdp_loader loader.c -lbpf -lelf -lz
```
3. 运行程序(需要 root 权限):
```bash
sudo ./xdp_loader
```
4. 测试事件报告:
```bash
# 生成测试流量
ping -c 1 8.8.8.8
curl http://example.com
# 观察事件输出
```
阅读全文
相关推荐


















