编译XDP eBPF内核程序报错,求排查与修正方案
eBPF XDP流量统计程序编译错误分析与修正
原程序代码
//This program is used to analyse network traffic (packet count, packet loss, latency and packet size). //The required headers #include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/in.h> #include <bpf/bpf_helpers.h> BPF_HASH(packet_count, struct iphdr*, u64); BPF_HASH(packet_size, struct iphdr*, u64); BPF_HASH(latency, struct iphdr*, u64); BPF_HASH(packet_loss, struct iphdr*, u64); SEC("packet_stats") int monitor_packets(struct xdp_md *ctx){ void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; struct iphdr *iph = data + sizeof(struct ethhdr); if (iph + 1 > data_end) { return XDP_PASS; } // Count the number of packets u64 *count = packet_count.lookup(&iph); if (count) { *count += 1; } else { packet_count.insert(&iph, &(u64){1}); } // Track the size of packets u64 *size = packet_size.lookup(&iph); if (size) { *size += ntohs(iph->tot_len); } else { packet_size.insert(&iph, &(u64){ntohs(iph->tot_len)}); } // Track the latency of packets u64 *ts = latency.lookup(&iph); if (ts) { u64 now = bpf_ktime_get_ns(); *ts = now - *ts; latency.delete(&iph); } else { u64 now = bpf_ktime_get_ns(); latency.insert(&iph, &now); } u64 *loss = packet_loss.lookup(&iph); if (!value) { packet_loss.insert(&key, &(u64){1}); } else { (*value)++; } return XDP_PASS } char _license[] SEC("license") = "GPL";
编译错误信息
BPF_HASH(packet_count, struct iphdr*, u64); ^ xdp_pass.c:10:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] BPF_HASH(packet_count, struct iphdr*, u64); ^ xdp_pass.c:11:23: error: expected identifier BPF_HASH(packet_size, struct iphdr*, u64); ^ xdp_pass.c:11:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] BPF_HASH(packet_size, struct iphdr*, u64); ^ xdp_pass.c:12:19: error: expected identifier BPF_HASH(latency, struct iphdr*, u64); ^ xdp_pass.c:12:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] BPF_HASH(latency, struct iphdr*, u64); ^ xdp_pass.c:13:23: error: expected identifier BPF_HASH(packet_loss, struct iphdr*, u64); ^ xdp_pass.c:13:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int] BPF_HASH(packet_loss, struct iphdr*, u64); ^ xdp_pass.c:22:13: warning: comparison of distinct pointer types ('struct iphdr *' and 'void *') [-Wcompare-distinct-pointer-types] if (iph + 1 > data_end) { ~~~~~~~ ^ ~~~~~~~~ xdp_pass.c:27:1: error: use of undeclared identifier 'u64' u64 *count = packet_count.lookup(&iph); ^ xdp_pass.c:27:6: error: use of undeclared identifier 'count' u64 *count = packet_count.lookup(&iph); ^ xdp_pass.c:27:14: error: use of undeclared identifier 'packet_count' u64 *count = packet_count.lookup(&iph); ^ xdp_pass.c:28:5: error: use of undeclared identifier 'count' if (count) { ^ xdp_pass.c:29:6: error: use of undeclared identifier 'count' *count += 1; ^ xdp_pass.c:31:5: error: use of undeclared identifier 'packet_count' packet_count.insert(&iph, &(u64){1}); ^ xdp_pass.c:31:33: error: use of undeclared identifier 'u64' packet_count.insert(&iph, &(u64){1}); ^ xdp_pass.c:35:1: error: use of undeclared identifier 'u64' u64 *size = packet_size.lookup(&iph); ^ xdp_pass.c:35:6: error: use of undeclared identifier 'size' u64 *size = packet_size.lookup(&iph); ^ xdp_pass.c:35:13: error: use of undeclared identifier 'packet_size' u64 *size = packet_size.lookup(&iph); ^ xdp_pass.c:36:5: error: use of undeclared identifier 'size' if (size) { ^ xdp_pass.c:37:14: warning: implicit declaration of function 'ntohs' is invalid in C99 [-Wimplicit-int] *size += ntohs(iph->tot_len); ^ xdp_pass.c:37:6: error: use of undeclared identifier 'size' *size += ntohs(iph->tot_len); ^ xdp_pass.c:39:5: error: use of undeclared identifier 'packet_size' packet_size.insert(&iph, &(u64){ntohs(iph->tot_len)}); ^ xdp_pass.c:39:32: error: use of undeclared identifier 'u64' packet_size.insert(&iph, &(u64){ntohs(iph->tot_len)}); ^ xdp_pass.c:43:1: error: use of undeclared identifier 'u64' u64 *ts = latency.lookup(&iph); ^ fatal error: too many errors emitted, stopping now [-ferror-limit=] 6 warnings and 20 errors generated.
问题分析与修正点
1. 缺少必要头文件与类型定义
u64类型未定义:需包含<linux/types.h>引入内核标准类型ntohs函数未声明:添加<linux/byteorder/generic.h>,适配eBPF环境的字节序转换
2. BPF哈希表键类型错误
原代码用 struct iphdr* 作为哈希表键,这是错误的:
- 内核态指针是虚拟地址,不同CPU/上下文地址不唯一,无法作为唯一标识
- 应使用数据包的源IP地址(
__be32类型)作为键,确保统计的是同一IP的流量
3. 指针越界检查类型不匹配
iph + 1 是 struct iphdr* 类型,data_end 是 void* 类型,直接比较会触发类型警告,需将 iph + 1 强制转换为 void*
4. 哈希表操作参数错误
原代码调用 lookup(&iph) 传递的是指针的地址(struct iphdr**),但哈希表的键是IP地址,应直接传递 &iph->saddr 作为键值
5. 丢包统计逻辑与变量错误
- 原代码使用未定义变量
value、key,需改为对应变量名loss - 原丢包逻辑无效:XDP层统计丢包需跟踪被丢弃的包(如返回
XDP_DROP的情况),可通过统计总包数和丢弃包数计算丢包率
6. 语法错误
return XDP_PASS末尾缺少分号
修正后的完整代码
// 统计入站网络包:包数量、包大小、双向延迟、丢包情况 #include <linux/bpf.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/in.h> #include <linux/types.h> #include <linux/byteorder/generic.h> #include <bpf/bpf_helpers.h> // 用源IP地址作为哈希表键,统计各IP的数据包信息 BPF_HASH(packet_count, __be32, u64); BPF_HASH(packet_size, __be32, u64); // 存储包的接收时间戳,用于计算双向延迟 BPF_HASH(packet_ts, __be32, u64); // 统计总处理包数和丢弃包数 BPF_ARRAY(total_packets, u64, 1); BPF_ARRAY(dropped_packets, u64, 1); SEC("xdp_stats") int monitor_packets(struct xdp_md *ctx) { void *data_end = (void *)(long)ctx->data_end; void *data = (void *)(long)ctx->data; struct ethhdr *eth = data; struct iphdr *iph; // 检查以太网头部完整性 if (eth + 1 > data_end) { return XDP_PASS; } // 仅处理IPv4包 if (eth->h_proto != __constant_htons(ETH_P_IP)) { return XDP_PASS; } iph = (struct iphdr *)(eth + 1); // 检查IP头部完整性 if ((void *)(iph + 1) > data_end) { return XDP_PASS; } __be32 src_ip = iph->saddr; u64 *total = total_packets.lookup(&((u32){0})); if (total) { (*total)++; } else { u64 init = 1; total_packets.update(&((u32){0}), &init); } // 统计包数量 u64 *count = packet_count.lookup(&src_ip); if (count) { (*count)++; } else { u64 init = 1; packet_count.update(&src_ip, &init); } // 统计包大小(转换为主机字节序) u16 pkt_size = __ntohs(iph->tot_len); u64 *size = packet_size.lookup(&src_ip); if (size) { (*size) += pkt_size; } else { packet_size.update(&src_ip, &((u64){pkt_size})); } // 计算双向延迟:如果是回应包,计算与请求包的时间差 u64 *ts = packet_ts.lookup(&src_ip); if (ts) { u64 now = bpf_ktime_get_ns(); bpf_printk("Latency for IP %x: %llu ns", src_ip, now - *ts); packet_ts.delete(&src_ip); } else { // 记录请求包的时间戳 u64 now = bpf_ktime_get_ns(); packet_ts.update(&src_ip, &now); } // 模拟丢包统计:包大小超过100字节则丢弃(示例逻辑) if (pkt_size > 100) { u64 *dropped = dropped_packets.lookup(&((u32){0})); if (dropped) { (*dropped)++; } else { u64 init = 1; dropped_packets.update(&((u32){0}), &init); } return XDP_DROP; } return XDP_PASS; } char _license[] SEC("license") = "GPL";
编译命令
使用原命令即可编译:
clang -O2 -target bpf -c demo.c -o demo.o
内容的提问来源于stack exchange,提问作者Anvay
相关产品推荐
相关产品推荐

