如何解决网络数据包计数:无法从BPF套接字过滤器读取数据包数据 问题:从套接字缓冲区读取解决方案1:使用绝对负载解决方案2:只需使用skb->len
我想对传入的网络数据包进行计数,每个TOS值的字节数为len。我创建了两个映射,第一个具有256个条目,其中包含每个TOS值的数据包计数,第二个具有数据包字节。因此,我编写了以下eBPF套接字过滤器:
struct bpf_insn prog[]{
BPF_MOV64_REG(BPF_REG_6,BPF_REG_1),//we use dgram socket,so packet starts directly from IP header
// BPF_LD_ABS(BPF_H,offsetof(struct ethhdr,h_proto)),// r0 = header type
// BPF_JMP_IMM(BPF_JEQ,BPF_REG_0,ETH_P_IP,2),// if (r0 == IPv4) skip 2
// BPF_MOV64_IMM(BPF_REG_0,0),// r0 = 0
// BPF_EXIT_INSN(),// return
//check for IP version,we only interested in v4
BPF_LD_ABS(BPF_B,// R0 = ip->vers: offsetof(struct iphdr,version)
BPF_ALU64_IMM(BPF_AND,0xF0),// r0 = r0 & 0xF0
BPF_JMP_IMM(BPF_JEQ,0x40,// if (r0 == 0x40) goto pc+2
BPF_MOV64_IMM(BPF_REG_0,// r0 = 0
BPF_EXIT_INSN(),// return
// load packet TOS value
BPF_LD_ABS(BPF_B,offsetof(struct iphdr,tos)),// R0 = ip->tos
BPF_STX_MEM(BPF_W,BPF_REG_10,-4),// *(u32 *)(fp - 4) = r0
BPF_MOV64_REG(BPF_REG_2,BPF_REG_10),// r2 = fp
BPF_ALU64_IMM(BPF_ADD,BPF_REG_2,// r2 = fp - 4
//first map with packet counters
BPF_LD_MAP_FD(BPF_REG_1,map_cnt_fd),// r1 = map_fd
BPF_RAW_INSN(BPF_JMP | BPF_CALL,BPF_FUNC_map_lookup_elem),// r0 = map_lookup(r1,r2)
BPF_JMP_IMM(BPF_JEQ,// if (r0 == 0) goto pc+2
BPF_MOV64_IMM(BPF_REG_1,1),// r1 = 1
BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW,BPF_REG_1,// xadd r0 += r1
BPF_LD_ABS(BPF_B,BPF_ALU64_IMM(BPF_ADD,// r2 = fp - 4
//second map with packet bytes
BPF_LD_MAP_FD(BPF_REG_1,map_bytes_fd),// if (r0 == 0) goto pc+2
// FIXME big endian
BPF_LDX_MEM(BPF_H,BPF_REG_6,tot_len)),// r1 = tot_len
BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW,// xadd r0 += r1
BPF_MOV64_IMM(BPF_REG_0,};
映射创建无误,套接字过滤器程序也创建良好,数据包计数器部分也可以正常工作。但是字节数计数器始终为0。该代码有什么问题?
我试图写一个简单的example。要进行编译,您只需要包含bpf_insn.h。
解决方法
问题:从套接字缓冲区读取
程序启动前放在BPF_REG_1
中的上下文不是指向数据开头的指针。相反,它是指向in the UAPI headers定义的struct __sk_buff
的指针,如下所示:
struct __sk_buff {
__u32 len;
...
}
因此,当您尝试从IP标头中读取数据时:
BPF_LDX_MEM(BPF_H,BPF_REG_1,BPF_REG_6,offsetof(struct iphdr,tot_len)),
实际上,您正在从struct __sk_buff
偏移2处读取两个字节(我们叫它的指针skb
)。因为您的系统采用小尾数法,所以这对应于skb->len
的最高有效位,除非您的数据包大于2 ^ 16字节(不太可能),否则它们为0。
我们在这里有两种可能的解决方案。
解决方案1:使用绝对负载
我们可以更新您的程序以在正确的位置读取IP长度。我相信使用BPF_LDX_MEM()
是不可能的,因为套接字过滤器do not permit direct packet access。解决方法是改用绝对负载。您的程序将变为:
struct bpf_insn prog[]{
BPF_MOV64_REG(BPF_REG_6,BPF_REG_1),// ... packet number counter,skipped for brevity
// Read IP length and store to r7 (preserved during helper calls)
BPF_LD_ABS(BPF_H,// r0 = tot_len
BPF_MOV64_REG(BPF_REG_7,BPF_REG_0),// r7 = r0
// No need to parse ToS a second time here,skipped
BPF_MOV64_REG(BPF_REG_2,BPF_REG_10),BPF_ALU64_IMM(BPF_ADD,BPF_REG_2,-4),// r2 = fp - 4
//second map with packet bytes
BPF_LD_MAP_FD(BPF_REG_1,map_bytes_fd),// r1 = map_fd
BPF_RAW_INSN(BPF_JMP | BPF_CALL,BPF_FUNC_map_lookup_elem),// r0 = map_lookup(r1,r2)
BPF_JMP_IMM(BPF_JEQ,BPF_REG_0,2),// if (r0 == 0) goto pc+2
// Now add length to the counter
BPF_STX_XADD(BPF_DW,BPF_REG_7,0),// xadd r0 += r7
BPF_MOV64_IMM(BPF_REG_0,// r0 = 0
BPF_EXIT_INSN(),};
解决方案2:只需使用skb->len
另一种解决方案是从skb
获取长度,因为内核已经为我们计算了长度。这只是固定负载的偏移量和长度的问题,您的BPF_STX_MEM(),BPF_XADD()
将变为:
BPF_LDX_MEM(BPF_W,offsetof(struct __sk_buff,len)),// r1 = skb->len
BPF_STX_XADD(BPF_DW,// xadd r0 += r1
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。