如何在SK_MSG钩子中正确打印消息数据?代码无报错但内容为空
问题分析与解决方案
问题现象
运行sk_msg类型的BPF程序时,程序无报错,但通过/sys/kernel/debug/tracing/trace_pipe查看输出,Socket Message:后始终无内容。切换为sk_skb类型程序后,问题同样存在。
原始代码
#include <stdio.h> #include <linux/bpf.h> #include <sys/socket.h> #include <bpf/bpf_helpers.h> char LICENSE[] SEC("license") = "GPL"; SEC("sk_msg") int http_state_machine(struct sk_msg_md *msg) { int err; int *msg_id_ptr; long len = (long)msg->data_end - (long)msg->data; if (len > 0) { bpf_printk("Message length: %ld\n", len); } else { bpf_printk("ERR: Message length is negative\n"); return SK_PASS; } // Try to print the message char msg_str[300]; if (len < 300) { bpf_printk("Message length is less than 300\n"); } else { err = bpf_msg_pull_data(msg, 0, 300, 0); if (err < 0) { bpf_printk("ERR: bpf_msg_pull_data failed\n"); return SK_PASS; } err = bpf_probe_read_kernel(msg_str, 300, msg->data); if (err < 0) { bpf_printk("ERR: bpf_probe_read_kernel failed\n"); return SK_PASS; } bpf_printk("Socket Message: %s\n", msg_str); } return SK_PASS; }
输出示例
ComposePostServ-1918515 [008] d...1 1322265.977032: bpf_trace_printk: Message length: 1295 ComposePostServ-1918515 [008] d...1 1322265.977039: bpf_trace_printk: Socket Message: ComposePostServ-1918516 [004] d...1 1322266.019906: bpf_trace_printk: Message length: 872 ComposePostServ-1918516 [004] d...1 1322266.019914: bpf_trace_printk: Socket Message:
核心原因与解决办法
1. 字符串未以NULL终止
bpf_printk的%s格式要求字符串必须以\0结尾,但从网络数据中复制的msg_str是原始二进制数据,大概率没有终止符,甚至可能开头就包含\0字节,导致%s无法正常解析输出。
解决方法:手动添加NULL终止符,确保数组最后一位为终止符:
// 在bpf_probe_read_kernel之后添加 msg_str[299] = '\0';
2. 网络数据含非ASCII或不可打印字符
网络报文(比如HTTP)可能包含二进制数据、控制字符,或者编码不是纯ASCII,直接用%s打印会被截断或无法显示。
解决方法:改用十六进制打印,或者过滤可打印字符后输出:
- 十六进制打印示例:
// 替换原有的bpf_printk("Socket Message: %s\n", msg_str); for (int i = 0; i < 300; i++) { bpf_printk("%02x ", msg_str[i]); if ((i+1) % 16 == 0) { bpf_printk("\n"); } }
- 过滤可打印字符示例:
char printable_str[300] = {0}; int idx = 0; for (int i = 0; i < 300; i++) { if (msg_str[i] >= 32 && msg_str[i] <= 126) { // 可打印ASCII范围 printable_str[idx++] = msg_str[i]; if (idx >= 299) break; } } printable_str[idx] = '\0'; bpf_printk("Socket Message: %s\n", printable_str);
3. 验证bpf_msg_pull_data的实际效果
虽然代码检查了err < 0,但可以打印err值确认是否成功拉取了预期长度的数据:
bpf_printk("bpf_msg_pull_data returned: %d\n", err);
如果返回值不等于300,说明拉取的数据长度不足,需进一步排查报文分段等问题。
4. 确认bpf_probe_read_kernel的使用正确性
对于sk_msg程序,msg->data指向内核空间的报文数据,bpf_probe_read_kernel的使用是正确的,但需确保复制长度不超过msg->data_end - msg->data的实际值,避免越界。
内容的提问来源于stack exchange,提问作者diviquery
相关产品推荐
相关产品推荐

