我正在尝试读取 redis-cli 实用程序通过 eBPF 发送到远程 Redis 数据库的传出 TCP 数据包的有效负载,以测试 eBPF 的功能。我的计划是按目标端口(已完成)过滤所有传出的 TCP 数据包,如 tcp_sendmsg() 的第一个参数 sock* sk 访问的那样,然后通过 &(msghdr->msg_iter. iov)->iov_base,其中 msghdr 是第二个参数。
但是,复制的数据似乎已损坏,并且与任何 TCP 数据包的任何有效负载都没有关系(我正在通过 Wireshark 跟踪真实的有效负载)。这是代码:
struct redis_sys_packet {
u16 lport;
u16 dport;
u32 nr_segments;
u32 packet_length;
char packet[128];
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, struct redis_sys_packet);
__type(value, u64);
} redis_sys_write_calls SEC(".maps");
//... other code
// kprobe handler for tcp_sendmsg
SEC("kprobe/tcp_sendmsg")
int kprobe__tcp_sendmsg(struct pt_regs *ctx) {
if (!ctx || ctx == NULL) return 0;
struct sock *sk = (struct sock *)(ctx->di);
if (!sk || sk == NULL) return 0;
u16 lport = 0;
u16 dport = 0;
bpf_probe_read_kernel(&lport, sizeof(lport), &sk->__sk_common.skc_num);
bpf_probe_read_kernel(&dport, sizeof(dport), &sk->__sk_common.skc_dport);
struct redis_sys_packet pack = {};
pack.lport = lport;
pack.dport = bpf_ntohs(dport);
if (pack.dport != 19795) return 0; // filter by port
// second parameter
struct msghdr* msg = (struct msghdr *)(ctx->si);
if (!msg || msg == NULL) return 0;
// Get the pointer to the iov (IO vector) array that holds the payload
struct iov_iter *iter = &(msg->msg_iter);
if (!iter || iter == NULL) return 0;
bpf_probe_read(&pack.nr_segments, sizeof(unsigned long), &(iter->nr_segs));
const struct iovec *iov = (const struct iovec *)&(iter->iov);
// Check if we can access the first iovec
if (!iov)
return 0;
// Get the base address of the payload
void *iovbase;
bpf_probe_read(&iovbase, sizeof(void *), &iov[0].iov_base);
unsigned long iov_len;
// read from count instead of iov->iov_len - for some reason this always returns 1, not the true length
bpf_probe_read(&iov_len, sizeof(size_t), &(iter->count));
pack.packet_length = (u32)iov_len;
u32 copy_length = (u32)iov_len;
if (copy_length > 128) {
copy_length = 128;
} else if (copy_length <= 0) {
return 0;
}
bpf_probe_read_kernel(pack.packet, copy_length, iovbase);
increment_map(&redis_sys_write_calls, &pack, 1); // returns irrelevant data
return 0;
}
如果有任何其他跟踪点/函数可以附加 kprobe/uprobe 来跟踪传出的 TCP 数据包并嗅探有效负载,我也会非常感激。我已经尝试附加到 xdp,但由于不相关的错误而无法工作。
我在 WSL2 之上的 Ubuntu 22.04 上执行所有这些操作,内核为 5.15.153.1-microsoft-standard-WSL2。
您可以使用套接字过滤器、TC(流量控制)或 XDP(eXpress 数据路径)来捕获 TCP、UDP 和 ICMP 有效负载,而无需求助于 kprobe、uprobe 或跟踪点。
以下是您可以探索的一些示例实现:
套接字过滤器:main.bpf.c
TC:main.bpf.c
XDP:main.bpf.c