make -C …/…/ /root/bpf/linux-4.19.90/samples/bpf/ BPF_SAMPLES_PATH=/root/bpf/linux-4.19.90/samples/bpf make[1]: Entering directory ‘/root/bpf/linux-4.19.90’ CALL scripts/checksyscalls.sh DESCEND objtool make -C /root/bpf/linux-4.19.90/samples/bpf/…/…/tools/lib/bpf/ RM=‘rm -rf’ LDFLAGS= srctree=/root/bpf/linux-4.19.90/samples/bpf/…/…/ O= Warning: Kernel ABI header at ‘tools/include/uapi/linux/bpf.h’ differs from latest version at ‘include/uapi/linux/bpf.h’ CLANG-bpf /root/bpf/linux-4.19.90/samples/bpf/xdp_whitelist_kern.o In file included from /root/bpf/linux-4.19.90/samples/bpf/xdp_whitelist_kern.c:4: In file included from ./include/linux/tcp.h:24: ./include/net/inet_connection_sock.h:214:3: error: use of undeclared identifier ‘KBUILD_MODNAME’ pr_debug(“inet_csk BUG: unknown timer value\n”); ^ ./include/linux/printk.h:340:2: note: expanded from macro ‘pr_debug’ dynamic_pr_debug(fmt, ##VA_ARGS) ^ ./include/linux/dynamic_debug.h:125:2: note: expanded from macro ‘dynamic_pr_debug’ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); ^ ./include/linux/dynamic_debug.h:99:2: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA’ DEFINE_DYNAMIC_DEBUG_METADATA_KEY(name, fmt, .key.dd_key_false, ^ ./include/linux/dynamic_debug.h:77:14: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA_KEY’ .modname = KBUILD_MODNAME, ^ In file included from /root/bpf/linux-4.19.90/samples/bpf/xdp_whitelist_kern.c:4: In file included from ./include/linux/tcp.h:24: ./include/net/inet_connection_sock.h:228:3: error: use of undeclared identifier ‘KBUILD_MODNAME’ pr_debug(“reset_xmit_timer: sk=%p %d when=0x%lx, caller=%p\n”, ^ ./include/linux/printk.h:340:2: note: expanded from macro ‘pr_debug’ dynamic_pr_debug(fmt, ##VA_ARGS) ^ ./include/linux/dynamic_debug.h:125:2: note: expanded from macro ‘dynamic_pr_debug’ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); ^ ./include/linux/dynamic_debug.h:99:2: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA’ DEFINE_DYNAMIC_DEBUG_METADATA_KEY(name, fmt, .key.dd_key_false, ^ ./include/linux/dynamic_debug.h:77:14: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA_KEY’ .modname = KBUILD_MODNAME, ^ In file included from /root/bpf/linux-4.19.90/samples/bpf/xdp_whitelist_kern.c:4: In file included from ./include/linux/tcp.h:24: ./include/net/inet_connection_sock.h:244:3: error: use of undeclared identifier ‘KBUILD_MODNAME’ pr_debug(“inet_csk BUG: unknown timer value\n”); ^ ./include/linux/printk.h:340:2: note: expanded from macro ‘pr_debug’ dynamic_pr_debug(fmt, ##VA_ARGS) ^ ./include/linux/dynamic_debug.h:125:2: note: expanded from macro ‘dynamic_pr_debug’ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); ^ ./include/linux/dynamic_debug.h:99:2: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA’ DEFINE_DYNAMIC_DEBUG_METADATA_KEY(name, fmt, .key.dd_key_false, ^ ./include/linux/dynamic_debug.h:77:14: note: expanded from macro ‘DEFINE_DYNAMIC_DEBUG_METADATA_KEY’ .modname = KBUILD_MODNAME, ^ 3 errors generated. make[1]: Leaving directory ‘/root/bpf/linux-4.19.90’
时间: 2025-07-21 20:51:42 浏览: 4
<think>我们正在编译Linux内核4.19.90的samples/bpf/目录下的BPF示例程序,但在编译xdp_whitelist_kern.c时出现了错误。
错误信息显示在包含头文件时,`pr_debug`宏展开后使用了未定义的标识符`KBUILD_MODNAME`。
问题分析:
在BPF程序(内核部分)中,我们通常不会定义`KBUILD_MODNAME`,因为BPF程序不是内核模块。但是,某些内核头文件(如`include/net/inet_connection_sock.h`)中的函数使用了`pr_debug`,而该宏又依赖于`KBUILD_MODNAME`。
解决方案:
1. 避免包含不必要的内核头文件。检查我们的BPF程序是否真的需要`include/linux/tcp.h`。如果不需要,则移除它。
2. 如果必须包含这些头文件,我们可以尝试在包含之前定义`KBUILD_MODNAME`为一个空字符串或任意字符串(因为BPF程序中没有模块名)。
3. 或者,我们可以通过定义`DEBUG`宏来控制`pr_debug`的行为,但这里的问题不是调试信息开关,而是缺少`KBUILD_MODNAME`。
在我们的例子中,错误来自于包含`<linux/tcp.h>`,而该头文件又包含了`<net/inet_connection_sock.h>`。我们的XDP程序可能只需要`<linux/tcp.h>`中的一些结构体定义(如`struct tcphdr`),但不需要整个头文件。
实际上,我们可能只需要包含`<linux/tcp.h>`和`<linux/udp.h>`来获取端口号。但是,在Linux内核中,`<linux/tcp.h>`会包含`<linux/socket.h>`和`<linux/skbuff.h>`等,这些头文件可能会包含更多的依赖。
因此,我们可以尝试只包含必要的头文件,或者使用更轻量级的头文件。
观察我们的代码,我们实际上只需要:
- <linux/ip.h> 用于IP头
- <linux/tcp.h> 用于TCP头
- <linux/udp.h> 用于UDP头
但是,在包含这些头文件时,可能会引起级联包含。因此,我们可以尝试重新组织头文件包含顺序,或者定义缺失的宏。
由于错误是在包含`<linux/tcp.h>`后出现的,我们可以尝试在包含之前定义`KBUILD_MODNAME`。
修改代码:
在xdp_whitelist_kern.c的开头,在包含任何头文件之前,添加:
#define KBUILD_MODNAME "xdp_whitelist"
这样,当包含的头文件展开时,`KBUILD_MODNAME`就已经被定义了。
但是,请注意,BPF程序并不是内核模块,所以这里我们只是为了避免编译错误而随意定义一个字符串。
修改后的代码片段:
```c
// 在包含任何头文件之前定义KBUILD_MODNAME
#define KBUILD_MODNAME "xdp_whitelist"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
```
这样,当包含`<net/inet_connection_sock.h>`时,`pr_debug`宏展开时就能找到`KBUILD_MODNAME`的定义,从而避免错误。
但是,我们也要注意,这样定义可能会带来其他问题?因为内核头文件可能会假设`KBUILD_MODNAME`是一个有效的模块名,但实际上在BPF程序中并不存在模块。不过,我们只是用来编译通过,而且这些调试信息在BPF程序中并不会被使用(因为BPF程序不能使用普通的内核打印函数)。
另外,我们也可以考虑不包含`<linux/tcp.h>`,而是只包含`<linux/tcp.h>`中定义的结构体。但是,这可能会很麻烦,因为我们需要确保结构体的定义与内核一致。
实际上,在BPF程序中,我们通常只需要TCP和UDP头的结构体定义,这些定义在`<uapi/linux/tcp.h>`和`<uapi/linux/udp.h>`中。我们可以尝试包含这些用户态API头文件,而不是内核头文件,因为内核头文件可能会包含过多的内部实现细节。
修改为:
```c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <uapi/linux/tcp.h> // 使用uapi头文件
#include <uapi/linux/udp.h> // 使用uapi头文件
```
这样,我们避免了包含内核内部头文件,从而避免了`KBUILD_MODNAME`的问题。
因此,我们有两种解决方案:
方案1:在包含头文件前定义`KBUILD_MODNAME`
方案2:使用uapi头文件代替内核头文件
由于方案2更干净,不会引入内核内部头文件,因此更推荐。
但是,请注意,在BPF程序中,我们通常使用`#include <linux/tcp.h>`,因为uapi头文件可能不包含一些必要的内核定义(比如`bpf_ntohs`等函数)。不过,我们只需要结构体定义,所以uapi头文件就足够了。
另外,注意`<linux/ip.h>`也是内核头文件,它可能会包含一些内部内容。但我们可以先尝试使用uapi头文件:
```c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/udp.h>
```
但是,`<uapi/linux/ip.h>`中定义的是用户态可见的IP头结构体,与内核的`<linux/ip.h>`定义相同。所以这样是安全的。
因此,我们修改xdp_whitelist_kern.c的头文件包含部分。
具体修改如下:
步骤1:将包含的头文件替换为uapi版本(对于tcp和udp)
步骤2:如果仍然出现错误,再考虑定义`KBUILD_MODNAME`
由于错误是在包含`<linux/tcp.h>`时出现的,而`<linux/tcp.h>`包含了`<net/inet_connection_sock.h>`(这是一个内核内部头文件),所以使用uapi的tcp.h可以避免这个问题。
我们尝试修改代码:
```c
// 原代码
#include <linux/tcp.h>
#include <linux/udp.h>
// 改为
#include <uapi/linux/tcp.h>
#include <uapi/linux/udp.h>
```
同时,确保`<linux/ip.h>`不会引起问题。如果`<linux/ip.h>`也引起类似问题,则同样替换为`<uapi/linux/ip.h>`。
但是注意,在BPF程序中,我们可能需要使用一些内核定义的函数(如内联函数),但uapi头文件只包含结构体和常量定义。因此,我们需要分开处理:结构体用uapi,而辅助函数用其他头文件。
在我们的程序中,我们使用了`bpf_htons`和`bpf_ntohs`,这些函数在`<bpf/bpf_endian.h>`中定义。因此,我们可以这样包含:
```c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <uapi/linux/ip.h> // 提供struct iphdr
#include <uapi/linux/tcp.h> // 提供struct tcphdr
#include <uapi/linux/udp.h> // 提供struct udphdr
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h> // 提供bpf_ntohs等
```
这样,我们就避免了包含内核内部头文件,从而避免`KBUILD_MODNAME`的问题。
因此,我们修改头文件包含部分,并重新编译。
如果问题仍然存在,我们可以同时采用两种方法:使用uapi头文件并定义`KBUILD_MODNAME`。
但是,我们首先尝试只使用uapi头文件。
修改后的代码示例:
```c
#define KBUILD_MODNAME "xdp_whitelist" // 如果使用uapi后仍然需要,则加上;否则可以不加
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
```
注意:在包含任何头文件之前定义`KBUILD_MODNAME`。
我们建议先不加`KBUILD_MODNAME`,如果编译通过则不需要;如果编译仍然报错,再加上。
最后,重新编译。
如果使用uapi头文件后,编译通过了,那么问题解决。
如果还有其他错误,请根据错误信息再调整。
由于我们是在内核源码树中编译,所以头文件路径是正确的。
现在,我们提供修改后的xdp_whitelist_kern.c的完整代码(仅头文件部分和关键部分):</think># 解决内核头文件依赖问题:KBUILD_MODNAME 未定义错误
您遇到的编译错误是由于内核头文件依赖问题导致的,特别是在 BPF 程序中使用网络协议栈相关头文件时。让我们详细分析并解决这个问题。
## 问题分析
错误信息显示:
```
error: use of undeclared identifier ‘KBUILD_MODNAME’
```
问题原因:
1. `KBUILD_MODNAME` 是内核模块编译时使用的宏,用于标识模块名称
2. BPF 程序不是传统的内核模块,因此没有定义这个宏
3. 某些内核头文件(如 `inet_connection_sock.h`)在调试信息中使用了 `pr_debug`,而它依赖于 `KBUILD_MODNAME`
4. 当 BPF 程序包含这些头文件时,会因为缺少定义而失败
## 解决方案
### 方法一:避免包含不必要的头文件(推荐)
修改您的 BPF 代码,只包含必要的头文件:
```c
// xdp_whitelist_kern.c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h> // 包含 IPPROTO_TCP/IPPROTO_UDP 定义
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// 手动定义 TCP/UDP 结构体(避免包含 tcp.h/udp.h)
struct tcphdr {
__be16 source;
__be16 dest;
};
struct udphdr {
__be16 source;
__be16 dest;
};
// ... [其余代码保持不变] ...
```
### 方法二:定义 KBUILD_MODNAME 宏
在包含任何内核头文件之前定义 `KBUILD_MODNAME`:
```c
// 在文件开头添加
#define KBUILD_MODNAME "xdp_whitelist"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// ... [其余代码保持不变] ...
```
### 方法三:禁用调试宏
在包含头文件前禁用调试宏:
```c
// 禁用调试宏
#undef pr_debug
#define pr_debug(fmt, ...)
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// ... [其余代码保持不变] ...
```
## 完整修复后的代码
以下是结合所有修复方法的完整代码:
```c
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h> // IP 协议定义
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
// 定义 KBUILD_MODNAME 避免头文件错误
#define KBUILD_MODNAME "xdp_whitelist"
// 手动定义 TCP/UDP 头结构(避免包含 tcp.h/udp.h)
struct my_tcphdr {
__be16 source;
__be16 dest;
// 其他字段可以忽略,因为我们只使用源/目的端口
};
struct my_udphdr {
__be16 source;
__be16 dest;
};
// 流键结构
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;
};
// 映射定义
struct bpf_map_def SEC("maps") whitelist_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(struct flow_key),
.value_size = sizeof(__u8),
.max_entries = 1024,
};
struct bpf_map_def SEC("maps") violation_events = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(__u32),
.max_entries = 0,
};
SEC("xdp_whitelist")
int xdp_whitelist_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if (eth + 1 > data_end) {
return XDP_PASS;
}
// 仅处理 IPv4
if (eth->h_proto != bpf_htons(ETH_P_IP)) {
return XDP_PASS;
}
struct iphdr *ip = (struct iphdr *)(eth + 1);
if (ip + 1 > data_end) {
return XDP_PASS;
}
// 仅处理 TCP 和 UDP
if (ip->protocol != IPPROTO_TCP && ip->protocol != IPPROTO_UDP) {
return XDP_PASS;
}
__u16 sport = 0, dport = 0;
void *trans_header = ip + 1;
if (ip->protocol == IPPROTO_TCP) {
struct my_tcphdr *tcp = trans_header;
if (tcp + 1 > data_end) {
return XDP_PASS;
}
sport = bpf_ntohs(tcp->source);
dport = bpf_ntohs(tcp->dest);
} else if (ip->protocol == IPPROTO_UDP) {
struct my_udphdr *udp = trans_header;
if (udp + 1 > data_end) {
return XDP_PASS;
}
sport = bpf_ntohs(udp->source);
dport = bpf_ntohs(udp->dest);
}
// 构建流键
struct flow_key key = {
.src_ip = ip->saddr,
.dst_ip = ip->daddr,
.src_port = sport,
.dst_port = dport,
.protocol = ip->protocol,
};
// 检查白名单
__u8 *allowed = bpf_map_lookup_elem(&whitelist_map, &key);
if (allowed) {
return XDP_PASS;
}
// 记录违规事件
struct violation_event ve = {
.key = key,
.timestamp = bpf_ktime_get_ns(),
};
__builtin_memcpy(ve.src_mac, eth->h_source, 6);
__builtin_memcpy(ve.dst_mac, eth->h_dest, 6);
bpf_perf_event_output(ctx, &violation_events, BPF_F_CURRENT_CPU, &ve, sizeof(ve));
return XDP_DROP;
}
char _license[] SEC("license") = "GPL";
```
## 修改编译命令
更新您的 Makefile 或编译命令,添加 `-DKBUILD_MODNAME` 定义:
```makefile
# 在 samples/bpf/Makefile 中修改
always := xdp_whitelist_kern.o
xdp_whitelist_kern-objs := xdp_whitelist_kern.o
# 添加编译选项
EXTRA_CFLAGS += -DKBUILD_MODNAME=\"xdp_whitelist\"
```
或者直接在命令行编译:
```bash
clang -O2 -target bpf -DKBUILD_MODNAME=\"xdp_whitelist\" \
-I/lib/modules/$(uname -r)/build/include \
-I/lib/modules/$(uname -r)/build/tools/lib/bpf \
-c xdp_whitelist_kern.c -o xdp_whitelist_kern.o
```
## 替代方案:使用内核源码树编译
如果您在编译内核示例,可以使用内核的构建系统:
```bash
# 在内核源码根目录
make M=samples/bpf
```
但需要确保在 `samples/bpf/Makefile` 中添加:
```makefile
# 在 samples/bpf/Makefile 中添加
CFLAGS_xdp_whitelist_kern.o += -DKBUILD_MODNAME=\"xdp_whitelist\"
```
## 验证修复
编译后检查是否还有相关错误:
```bash
make -C /lib/modules/$(uname -r)/build M=$PWD/samples/bpf
```
## 其他注意事项
1. **内核版本兼容性**:
- Linux 4.19 使用较旧的 BPF 语法
- 新内核(5.x+)使用更新的 BPF CO-RE 技术
2. **头文件路径**:
```bash
# 确保包含正确的头文件路径
-I/lib/modules/$(uname -r)/build/include
-I/lib/modules/$(uname -r)/build/arch/x86/include # 根据架构调整
```
3. **协议处理优化**:
```c
// 使用偏移量计算替代直接指针运算
__u8 *trans_header = (__u8 *)(ip + 1);
if (trans_header + sizeof(struct my_tcphdr) > data_end) {
return XDP_PASS;
}
```
## 完整的内核树编译解决方案
如果您在编译内核树中的示例,可以修改内核的构建系统:
1. 编辑 `samples/bpf/Makefile`:
```makefile
# 添加全局编译选项
ccflags-y += -DKBUILD_MODNAME=\"bpf_prog\"
# 或针对特定文件
CFLAGS_xdp_whitelist_kern.o += -DKBUILD_MODNAME=\"xdp_whitelist\"
```
2. 或者修改内核头文件(不推荐):
```diff
// 在 include/linux/dynamic_debug.h 中添加
#ifndef KBUILD_MODNAME
#define KBUILD_MODNAME "unknown"
#endif
```
阅读全文
相关推荐


