<think>根据编译错误,主要问题集中在以下几个方面:
1. 缺少头文件:如`<time.h>`(解决localtime和strftime的隐式声明问题)、`<sys/mman.h>`(解决mmap、munmap的隐式声明问题)、`<linux/perf_event.h>`(解决perf_event_attr结构体不完整的问题)。
2. 使用了未声明的常量:如`PERF_SAMPLE_RAW`、`PERF_TYPE_SOFTWARE`、`PERF_COUNT_SW_BPF_OUTPUT`等,这些需要包含`<linux/perf_event.h>`。
3. `__NR_perf_event_open`未定义:需要包含`<sys/syscall.h>`。
4. 函数声明不是原型:在函数定义时,参数列表应明确为`(void)`而不是空括号。
5. 结构体`perf_event_attr`的使用:由于内核版本(4.19)与头文件版本可能不一致,需要确保包含正确的头文件。
此外,在用户态程序中,我们使用了libbpf的API,需要确保链接到正确的库。
下面我们修正用户态程序(xdp_whitelist_user.c)的代码,解决上述问题。
### 修正后的用户态程序 (`xdp_whitelist_user.c`)
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/syscall.h> // For syscall, __NR_perf_event_open
#include <sys/mman.h> // For mmap, munmap, PROT_READ, PROT_WRITE, MAP_SHARED
#include <time.h> // For localtime, strftime
#include <netinet/in.h>
#include <arpa/inet.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <linux/if_link.h>
#include <linux/perf_event.h> // For perf_event_attr, PERF_TYPE_SOFTWARE, etc.
// 与内核一致的结构定义
struct flow_key {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
__u8 protocol;
};
struct violation_event {
struct flow_key key;
unsigned char src_mac[6];
unsigned char dst_mac[6];
__u64 timestamp;
};
// 全局变量
static int ifindex = -1;
static int prog_fd = -1;
static int map_fd = -1;
static int perf_fd = -1;
static struct bpf_object *obj = NULL;
// 打印MAC地址
static void print_mac(const unsigned char *mac) {
printf("%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// 打印违规事件
static void print_violation(const struct violation_event *e) {
char src_ip[INET_ADDRSTRLEN];
char dst_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &e->key.src_ip, src_ip, sizeof(src_ip));
inet_ntop(AF_INET, &e->key.dst_ip, dst_ip, sizeof(dst_ip));
time_t ts = e->timestamp / 1000000000;
struct tm *tm_info = localtime(&ts);
char time_buf[20];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
printf("\n[!] VIOLATION DETECTED (%s)\n", time_buf);
printf(" SRC: %s:%d (MAC: ", src_ip, ntohs(e->key.src_port));
print_mac(e->src_mac);
printf(")\n");
printf(" DST: %s:%d (MAC: ", dst_ip, ntohs(e->key.dst_port));
print_mac(e->dst_mac);
printf(")\n");
printf(" PROTO: %s\n", e->key.protocol == IPPROTO_TCP ? "TCP" :
e->key.protocol == IPPROTO_UDP ? "UDP" : "OTHER");
}
// 读取性能事件
static void read_violations(void) {
struct perf_event_mmap_page *header;
struct violation_event *events;
char *buf = NULL;
size_t page_size = sysconf(_SC_PAGESIZE);
size_t mmap_size = page_size * 8; // 8 pages buffer
// 设置性能事件
struct perf_event_attr attr = {
.sample_type = PERF_SAMPLE_RAW,
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_BPF_OUTPUT,
.size = sizeof(struct perf_event_attr),
.wakeup_events = 1,
};
perf_fd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, 0);
if (perf_fd < 0) {
fprintf(stderr, "Failed to open perf event: %s\n", strerror(errno));
return;
}
// 将perf事件附加到BPF映射
int key = 0;
if (bpf_map_update_elem(map_fd, &key, &perf_fd, BPF_ANY) < 0) {
fprintf(stderr, "Failed to attach perf event: %s\n", strerror(errno));
close(perf_fd);
return;
}
// 内存映射
buf = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, perf_fd, 0);
if (buf == MAP_FAILED) {
fprintf(stderr, "Failed to mmap perf buffer: %s\n", strerror(errno));
close(perf_fd);
return;
}
header = (struct perf_event_mmap_page *)buf;
events = (struct violation_event *)(buf + page_size);
printf("Monitoring for violations... (Ctrl+C to stop)\n");
while (1) {
__u64 data_head = __sync_fetch_and_add(&header->data_head, 0);
__u64 data_tail = header->data_tail;
if (data_head == data_tail) {
usleep(1000); // 1ms sleep
continue;
}
while (data_tail < data_head) {
struct violation_event *e = (struct violation_event *)((char *)events + (data_tail % (mmap_size - page_size)));
print_violation(e);
data_tail += sizeof(*e);
}
__sync_synchronize();
header->data_tail = data_tail;
}
munmap(buf, mmap_size);
close(perf_fd);
}
// 添加白名单规则
static void add_rule(const char *src_ip, const char *dst_ip,
uint16_t src_port, uint16_t dst_port, uint8_t proto) {
struct flow_key key = {0};
uint8_t value = 1;
inet_pton(AF_INET, src_ip, &key.src_ip);
inet_pton(AF_INET, dst_ip, &key.dst_ip);
key.src_port = htons(src_port);
key.dst_port = htons(dst_port);
key.protocol = proto;
if (bpf_map_update_elem(bpf_map__fd(bpf_object__find_map_by_name(obj, "whitelist_map")),
&key, &value, BPF_ANY) < 0) {
fprintf(stderr, "Failed to add rule: %s\n", strerror(errno));
return;
}
printf("Added rule: %s:%d -> %s:%d/%s\n",
src_ip, src_port, dst_ip, dst_port,
proto == IPPROTO_TCP ? "tcp" : "udp");
}
// 删除白名单规则
static void del_rule(const char *src_ip, const char *dst_ip,
uint16_t src_port, uint16_t dst_port, uint8_t proto) {
struct flow_key key = {0};
inet_pton(AF_INET, src_ip, &key.src_ip);
inet_pton(AF_INET, dst_ip, &key.dst_ip);
key.src_port = htons(src_port);
key.dst_port = htons(dst_port);
key.protocol = proto;
if (bpf_map_delete_elem(bpf_map__fd(bpf_object__find_map_by_name(obj, "whitelist_map")),
&key) < 0) {
fprintf(stderr, "Failed to delete rule: %s\n", strerror(errno));
return;
}
printf("Deleted rule: %s:%d -> %s:%d/%s\n",
src_ip, src_port, dst_ip, dst_port,
proto == IPPROTO_TCP ? "tcp" : "udp");
}
// 加载XDP程序
static int load_xdp(const char *iface) {
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
// 设置资源限制
if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
fprintf(stderr, "setrlimit failed: %s\n", strerror(errno));
return -1;
}
// 获取接口索引
ifindex = if_nametoindex(iface);
if (!ifindex) {
fprintf(stderr, "Failed to get ifindex: %s\n", strerror(errno));
return -1;
}
// 加载BPF对象
obj = bpf_object__open("xdp_whitelist.o");
if (libbpf_get_error(obj)) {
fprintf(stderr, "Failed to open BPF object\n");
return -1;
}
// 加载BPF程序
if (bpf_object__load(obj)) {
fprintf(stderr, "Failed to load BPF object\n");
return -1;
}
// 获取程序FD
struct bpf_program *prog = bpf_object__find_program_by_name(obj, "xdp_whitelist");
if (!prog) {
fprintf(stderr, "Failed to find BPF program\n");
return -1;
}
prog_fd = bpf_program__fd(prog);
if (prog_fd < 0) {
fprintf(stderr, "Failed to get prog FD\n");
return -1;
}
// 附加XDP程序
if (bpf_set_link_xdp_fd(ifindex, prog_fd, XDP_FLAGS_UPDATE_IF_NOEXIST) < 0) {
fprintf(stderr, "Failed to attach XDP program\n");
return -1;
}
// 获取映射FD(违规事件映射)
struct bpf_map *map = bpf_object__find_map_by_name(obj, "violation_events");
if (!map) {
fprintf(stderr, "Failed to find violation_events map\n");
return -1;
}
map_fd = bpf_map__fd(map);
if (map_fd < 0) {
fprintf(stderr, "Failed to get map FD\n");
return -1;
}
printf("XDP program loaded on %s (ifindex %d)\n", iface, ifindex);
return 0;
}
// 卸载XDP程序
static void unload_xdp(void) {
if (ifindex > 0) {
bpf_set_link_xdp_fd(ifindex, -1, XDP_FLAGS_UPDATE_IF_NOEXIST);
printf("XDP program unloaded\n");
}
if (obj) {
bpf_object__close(obj);
}
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <interface> [options]\n", argv[0]);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -a <src_ip> <dst_ip> <src_port> <dst_port> <proto> Add rule\n");
fprintf(stderr, " -d <src_ip> <dst_ip> <src_port> <dst_port> <proto> Delete rule\n");
fprintf(stderr, " -m Monitor violations\n");
return 1;
}
const char *iface = argv[1];
// 加载XDP程序
if (load_xdp(iface) < 0) {
return 1;
}
// 处理命令行参数
int opt_idx = 2;
while (opt_idx < argc) {
if (strcmp(argv[opt_idx], "-a") == 0 && argc > opt_idx + 5) {
uint8_t proto = (strcmp(argv[opt_idx + 5], "tcp") == 0) ? IPPROTO_TCP : IPPROTO_UDP;
add_rule(argv[opt_idx + 1], argv[opt_idx + 2],
atoi(argv[opt_idx + 3]), atoi(argv[opt_idx + 4]), proto);
opt_idx += 6;
}
else if (strcmp(argv[opt_idx], "-d") == 0 && argc > opt_idx + 5) {
uint8_t proto = (strcmp(argv[opt_idx + 5], "tcp") == 0) ? IPPROTO_TCP : IPPROTO_UDP;
del_rule(argv[opt_idx + 1], argv[opt_idx + 2],
atoi(argv[opt_idx + 3]), atoi(argv[opt_idx + 4]), proto);
opt_idx += 6;
}
else if (strcmp(argv[opt_idx], "-m") == 0) {
read_violations();
opt_idx++;
}
else {
fprintf(stderr, "Invalid option: %s\n", argv[opt_idx]);
opt_idx++;
}
}
unload_xdp();
return 0;
}
```
### 修正说明
1. **添加必要的头文件**:
- `<sys/syscall.h>`:用于`__NR_perf_event_open`
- `<sys/mman.h>`:用于`mmap`, `munmap`, `PROT_READ`, `PROT_WRITE`, `MAP_SHARED`, `MAP_FAILED`
- `<time.h>`:用于`localtime`, `strftime`
- `<linux/perf_event.h>`:用于`perf_event_attr`和相关常量
2. **修正函数声明**:
- 将`read_violations()`和`unload_xdp()`的参数列表改为`(void)`,以符合严格的原型要求。
3. **修正`perf_event_attr`初始化**:
- 使用`struct perf_event_attr`时,确保`size`字段设置为结构体大小。
- 使用正确的常量:`PERF_SAMPLE_RAW`, `PERF_TYPE_SOFTWARE`, `PERF_COUNT_SW_BPF_OUTPUT`。
4. **修正`bpf_object__find_program_by_name`的用法**:
- 先获取`bpf_program`指针,再通过`bpf_program__fd`获取文件描述符,避免隐式声明和类型转换问题。
5. **错误处理**:
- 增加了对`bpf_object__find_program_by_name`和`bpf_object__find_map_by_name`返回值的检查。
### 编译步骤
1. 编译BPF程序(确保使用正确的内核头文件路径):
```bash
clang -O2 -target bpf -I /path/to/kernel-headers/ -c xdp_whitelist.c -o xdp_whitelist.o
```
2. 编译用户态程序(链接libbpf):
```bash
gcc -o xdp_whitelist_ctl xdp_whitelist_user.c -I /path/to/libbpf/include -L /path/to/libbpf/lib -lbpf -lelf -lz
```
如果使用系统自带的libbpf,可能只需要:
```bash
gcc -o xdp_whitelist_ctl xdp_whitelist_user.c -lbpf -lelf -lz
```
### 注意事项
1. 内核头文件路径:编译BPF程序时,需要指定内核头文件的路径,通常为`/usr/include/`或`/lib/modules/$(uname -r)/build`。
2. 用户态程序链接:确保链接`libbpf`、`libelf`和`libz`。
3. 权限:运行用户态程序需要root权限。
4. 卸载XDP程序:程序退出时会自动卸载XDP程序,但也可以手动卸载(例如,`ip link set dev eth0 xdp off`)。
§§相关问题§§:
1. 如何扩展此程序以支持IPv6五元组过滤?
2. 在内核4.19上使用XDP时,如何确保BPF程序的兼容性?
3. 如何优化高负载下的违规事件记录性能?
4. 如何实现持久化存储违规事件记录?</think>根据您提供的编译错误,主要问题集中在用户态程序 `xdp_whitelist_user.c` 中。以下是完整的问题分析和修复方案:
### 问题分析与修复方案
#### 1. 缺少头文件
```c
// 在文件开头添加以下头文件
#include <time.h> // 解决 localtime 和 strftime 问题
#include <sys/mman.h> // 解决 mmap, munmap 问题
#include <linux/perf_event.h> // 解决 perf_event_attr 问题
#include <sys/syscall.h> // 解决 __NR_perf_event_open 问题
```
#### 2. 函数声明不完整
```c
// 将函数声明改为完整形式
static void read_violations(void); // 添加 void 参数
static void unload_xdp(void); // 添加 void 参数
```
#### 3. perf_event_attr 结构体初始化
```c
// 修正后的 perf_event_attr 初始化
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.size = sizeof(struct perf_event_attr),
.config = PERF_COUNT_SW_BPF_OUTPUT,
.sample_type = PERF_SAMPLE_RAW,
.sample_period = 1,
.wakeup_events = 1,
};
```
#### 4. 常量未定义问题
确保在头文件包含后定义缺失的常量:
```c
#ifndef PERF_COUNT_SW_BPF_OUTPUT
#define PERF_COUNT_SW_BPF_OUTPUT 10
#endif
```
#### 5. 其他问题修复
```c
// 修正 bpf_program__find_by_name 的使用
struct bpf_program *prog = bpf_object__find_program_by_name(obj, "xdp_whitelist");
prog_fd = bpf_program__fd(prog);
```
### 完整修复后的用户态程序
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <linux/if_link.h>
#include <time.h> // 添加
#include <sys/mman.h> // 添加
#include <linux/perf_event.h> // 添加
#include <sys/syscall.h> // 添加
// 确保常量定义
#ifndef PERF_COUNT_SW_BPF_OUTPUT
#define PERF_COUNT_SW_BPF_OUTPUT 10
#endif
// 与内核一致的结构定义
struct flow_key {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
__u8 protocol;
};
struct violation_event {
struct flow_key key;
unsigned char src_mac[6];
unsigned char dst_mac[6];
__u64 timestamp;
};
// 打印MAC地址
static void print_mac(const unsigned char *mac) {
printf("%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// 打印违规事件
static void print_violation(const struct violation_event *e) {
char src_ip[INET_ADDRSTRLEN];
char dst_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &e->key.src_ip, src_ip, sizeof(src_ip));
inet_ntop(AF_INET, &e->key.dst_ip, dst_ip, sizeof(dst_ip));
time_t ts = e->timestamp / 1000000000;
struct tm *tm_info = localtime(&ts);
char time_buf[20];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
printf("\n[!] VIOLATION DETECTED (%s)\n", time_buf);
printf(" SRC: %s:%d (MAC: ", src_ip, ntohs(e->key.src_port));
print_mac(e->src_mac);
printf(")\n");
printf(" DST: %s:%d (MAC: ", dst_ip, ntohs(e->key.dst_port));
print_mac(e->dst_mac);
printf(")\n");
printf(" PROTO: %s\n", e->key.protocol == IPPROTO_TCP ? "TCP" :
e->key.protocol == IPPROTO_UDP ? "UDP" : "OTHER");
}
// 读取性能事件
static void read_violations(void) { // 添加 void
struct perf_event_mmap_page *header;
struct violation_event *events;
char *buf = NULL;
size_t page_size = sysconf(_SC_PAGESIZE);
size_t mmap_size = page_size * 8; // 8 pages buffer
// 设置性能事件
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.size = sizeof(struct perf_event_attr),
.config = PERF_COUNT_SW_BPF_OUTPUT,
.sample_type = PERF_SAMPLE_RAW,
.sample_period = 1,
.wakeup_events = 1,
};
perf_fd = syscall(__NR_perf_event_open, &attr, -1, 0, -1, 0);
if (perf_fd < 0) {
fprintf(stderr, "Failed to open perf event: %s\n", strerror(errno));
return;
}
// 将perf事件附加到BPF映射
int key = 0;
if (bpf_map_update_elem(map_fd, &key, &perf_fd, BPF_ANY) < 0) {
fprintf(stderr, "Failed to attach perf event: %s\n", strerror(errno));
close(perf_fd);
return;
}
// 内存映射
buf = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, perf_fd, 0);
if (buf == MAP_FAILED) {
fprintf(stderr, "Failed to mmap perf buffer: %s\n", strerror(errno));
close(perf_fd);
return;
}
header = (struct perf_event_mmap_page *)buf;
events = (struct violation_event *)(buf + page_size);
printf("Monitoring for violations... (Ctrl+C to stop)\n");
while (1) {
__u64 data_head = __sync_fetch_and_add(&header->data_head, 0);
__u64 data_tail = header->data_tail;
if (data_head == data_tail) {
usleep(1000); // 1ms sleep
continue;
}
while (data_tail < data_head) {
struct violation_event *e = (struct violation_event *)((char *)events + (data_tail % (mmap_size - page_size)));
print_violation(e);
data_tail += sizeof(*e);
}
__sync_synchronize();
header->data_tail = data_tail;
}
munmap(buf, mmap_size);
close(perf_fd);
}
// 添加白名单规则
static void add_rule(const char *src_ip, const char *dst_ip,
uint16_t src_port, uint16_t dst_port, uint8_t proto) {
struct flow_key key = {0};
uint8_t value = 1;
inet_pton(AF_INET, src_ip, &key.src_ip);
inet_pton(AF_INET, dst_ip, &key.dst_ip);
key.src_port = htons(src_port);
key.dst_port = htons(dst_port);
key.protocol = proto;
if (bpf_map_update_elem(bpf_map__fd(bpf_object__find_map_by_name(obj, "whitelist_map")),
&key, &value, BPF_ANY) < 0) {
fprintf(stderr, "Failed to add rule: %s\n", strerror(errno));
return;
}
printf("Added rule: %s:%d -> %s:%d/%s\n",
src_ip, src_port, dst_ip, dst_port,
proto == IPPROTO_TCP ? "tcp" : "udp");
}
// 删除白名单规则
static void del_rule(const char *src_ip, const char *dst_ip,
uint16_t src_port, uint16_t dst_port, uint8_t proto) {
struct flow_key key = {0};
inet_pton(AF_INET, src_ip, &key.src_ip);
inet_pton(AF_INET, dst_ip, &key.dst_ip);
key.src_port = htons(src_port);
key.dst_port = htons(dst_port);
key.protocol = proto;
if (bpf_map_delete_elem(bpf_map__fd(bpf_object__find_map_by_name(obj, "whitelist_map")),
&key) < 0) {
fprintf(stderr, "Failed to delete rule: %s\n", strerror(errno));
return;
}
printf("Deleted rule: %s:%d -> %s:%d/%s\n",
src_ip, src_port, dst_ip, dst_port,
proto == IPPROTO_TCP ? "tcp" : "udp");
}
// 全局变量
static int ifindex = -1;
static int prog_fd = -1;
static int map_fd = -1;
static int perf_fd = -1;
static struct bpf_object *obj = NULL;
// 加载XDP程序
static int load_xdp(const char *iface) {
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
// 设置资源限制
if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
fprintf(stderr, "setrlimit failed: %s\n", strerror(errno));
return -1;
}
// 获取接口索引
ifindex = if_nametoindex(iface);
if (!ifindex) {
fprintf(stderr, "Failed to get ifindex: %s\n", strerror(errno));
return -1;
}
// 加载BPF对象
obj = bpf_object__open("xdp_whitelist.o");
if (libbpf_get_error(obj)) {
fprintf(stderr, "Failed to open BPF object\n");
return -1;
}
// 加载BPF程序
if (bpf_object__load(obj)) {
fprintf(stderr, "Failed to load BPF object\n");
return -1;
}
// 获取程序FD - 修正此处
struct bpf_program *prog = bpf_object__find_program_by_name(obj, "xdp_whitelist");
if (!prog) {
fprintf(stderr, "Failed to find program\n");
return -1;
}
prog_fd = bpf_program__fd(prog);
if (prog_fd < 0) {
fprintf(stderr, "Failed to get prog FD\n");
return -1;
}
// 附加XDP程序
if (bpf_set_link_xdp_fd(ifindex, prog_fd, XDP_FLAGS_UPDATE_IF_NOEXIST) < 0) {
fprintf(stderr, "Failed to attach XDP program\n");
return -1;
}
// 获取映射FD - 修正此处
struct bpf_map *map = bpf_object__find_map_by_name(obj, "violation_events");
if (!map) {
fprintf(stderr, "Failed to find violation_events map\n");
return -1;
}
map_fd = bpf_map__fd(map);
if (map_fd < 0) {
fprintf(stderr, "Failed to get map FD\n");
return -1;
}
printf("XDP program loaded on %s (ifindex %d)\n", iface, ifindex);
return 0;
}
// 卸载XDP程序
static void unload_xdp(void) { // 添加 void
if (ifindex > 0) {
bpf_set_link_xdp_fd(ifindex, -1, XDP_FLAGS_UPDATE_IF_NOEXIST);
printf("XDP program unloaded\n");
}
if (obj) {
bpf_object__close(obj);
}
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <interface> [options]\n", argv[0]);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -a <src_ip> <dst_ip> <src_port> <dst_port> <proto> Add rule\n");
fprintf(stderr, " -d <src_ip> <dst_ip> <src_port> <dst_port> <proto> Delete rule\n");
fprintf(stderr, " -m Monitor violations\n");
return 1;
}
const char *iface = argv[1];
// 加载XDP程序
if (load_xdp(iface) < 0) {
return 1;
}
// 处理命令行参数
int opt_idx = 2;
while (opt_idx < argc) {
if (strcmp(argv[opt_idx], "-a") == 0 && argc > opt_idx + 5) {
uint8_t proto = (strcmp(argv[opt_idx + 5], "tcp") == 0) ? IPPROTO_TCP : IPPROTO_UDP;
add_rule(argv[opt_idx + 1], argv[opt_idx + 2],
atoi(argv[opt_idx + 3]), atoi(argv[opt_idx + 4]), proto);
opt_idx += 6;
}
else if (strcmp(argv[opt_idx], "-d") == 0 && argc > opt_idx + 5) {
uint8_t proto = (strcmp(argv[opt_idx + 5], "tcp") == 0) ? IPPROTO_TCP : IPPROTO_UDP;
del_rule(argv[opt_idx + 1], argv[opt_idx + 2],
atoi(argv[opt_idx + 3]), atoi(argv[opt_idx + 4]), proto);
opt_idx += 6;
}
else if (strcmp(argv[opt_idx], "-m") == 0) {
read_violations();
opt_idx++;
}
else {
fprintf(stderr, "Invalid option: %s\n", argv[opt_idx]);
opt_idx++;
}
}
unload_xdp();
return 0;
}
```
### 关键修复点说明
1. **头文件缺失修复**:
- 添加了 `<time.h>` 解决 `localtime` 和 `strftime` 问题
- 添加了 `<sys/mman.h>` 解决 `mmap` 和 `munmap` 问题
- 添加了 `<linux/perf_event.h>` 解决 `perf_event_attr` 问题
- 添加了 `<sys/syscall.h>` 解决 `__NR_perf_event_open` 问题
2. **函数声明修复**:
- 将所有函数声明改为完整形式 `(void)` 而不仅仅是 `()`
- 修正了函数原型不匹配的警告
3. **perf_event_attr 初始化修复**:
- 使用正确的结构体初始化方式
- 确保所有字段都是 `perf_event_attr` 的有效成员
- 添加了必要的常量定义
4. **libbpf API 使用修复**:
- 修正了 `bpf_object__find_program_by_name` 的使用方式
- 确保先获取程序指针再获取文件描述符
- 添加了错误检查逻辑
5. **常量定义修复**:
- 为 `PERF_COUNT_SW_BPF_OUTPUT` 添加了备用定义
- 确保所有使用的常量都有明确定义
### 编译和运行步骤
1. **编译 BPF 程序**:
```bash
clang -O2 -target bpf -c xdp_whitelist.c -o xdp_whitelist.o
```
2. **编译用户态程序**:
```bash
gcc -o xdp_whitelist_ctl xdp_whitelist_user.c -lbpf -lelf -lz
```
3. **运行程序**:
```bash
# 加载并监控
sudo ./xdp_whitelist_ctl eth0 -m
# 添加规则
sudo ./xdp_whitelist_ctl eth0 -a 192.168.1.100 10.0.0.1 8080 80 tcp
# 删除规则
sudo ./xdp_whitelist_ctl eth0 -d 192.168.1.100 10.0.0.1 8080 80 tcp
```
### 内核头文件版本问题解决方案
如果仍然遇到头文件版本不匹配的问题(如 `bpf.h` 版本不一致),可以尝试:
1. **使用系统头文件**:
```bash
# 编译时指定内核头文件路径
clang -O2 -target bpf -I /lib/modules/$(uname -r)/build/arch/x86/include \
-I /lib/modules/$(uname -r)/build/include -c xdp_whitelist.c -o xdp_whitelist.o
```
2. **统一头文件版本**:
```bash
# 更新内核头文件
sudo apt-get install linux-headers-$(uname -r)
```
3. **使用 BTF 支持(内核 5.10+)**:
```bash
# 编译时添加 BTF 支持
clang -O2 -g -target bpf -D__TARGET_ARCH_x86 -I /usr/include/ -c xdp_whitelist.c -o xdp_whitelist.o
```