Surftrace 是由系统运维 SIG 推出的一个 ftrace 封装器和开发编译平台,让用户既能基于 libbpf 快速构建工程进行开发,也能作为 ftrace 的封装器进行 trace 命令编写。项目包含 Surftrace 工具集和 pylcc、glcc(python or generic C language for libbpf Compiler Collection),提供远程和本地 eBPF 的编译能力。
用户需要重点关注下面两个结构体成员:
unsignedchar *head, *data;
其中 head 指向了缓冲区开始,data 指向了当前报文处理所在协议层的起始位置,如当前协议处理位于 tcp 层,data 指针就会指向 struct tcphdr。在 IP 层,则指向了struct iphdr。因此,data 指针成员,是报文在内核处理过程中的关键信标。不难发现,上图中几乎所有函数都涉及到 skb 结构体处理,因此要想深入了解网络报文在内核的处理过程,skb->data 应该就是最理想的引路蜂。
staticint__netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc, struct packet_type **ppt_prev);
解析每个 skb 对应报文三层协议成员的方法:
surftrace 'p __netif_receive_skb_core proto=@(struct iphdr *)l3%0->protocol`
协议成员获取方法为@(struct iphdr *)l3%0->protocol。tips:数据 | 数据类型 | 数据长度(字节) |
cdata | unsgined char [] | 1 |
sdata | unsigned short [] | 2 |
ldata | unsigned int [] | 4 |
qdata | unsigned long long [] | 8 |
Sdata | char* [] | 字符串 |
data=@(struct icmphdr*)l3%0->sdata[1]
前缀名 | 数据输出形式 | 数据长度(字节) |
ip_ | a.b.c.d | ip字符串 |
b16_ | 10 进制 | 2 |
b32_ | 10 进制 | 4 |
b64_ | 10 进制 | 8 |
B16_ | 16 进制 | 2 |
B32_ | 16 进制 | 4 |
B64_ | 16 进制 | 8 |
intip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
追踪表达式:
surftrace 'p ip_output proto=@(struct iphdr*)l3%2->protocol ip_dst=@(struct iphdr*)l3%2->daddr b16_dest=@(struct udphdr*)l3%2->dest comm=$comm body=@(struct udphdr*)l3%2->Sdata[0] f:proto==17&&ip_dst==10.0.1.221&&b16_dest==9988'
追踪结果:
surftrace 'p ip_output proto=@(struct iphdr*)l3%2->protocol ip_dst=@(struct iphdr*)l3%2->daddr b16_dest=@(struct udphdr*)l3%2->dest comm=$comm body=@(struct udphdr*)l3%2->Sdata[0] f:proto==17&&ip_dst==10.0.1.221&&b16_dest==9988' echo 'p:f0 ip_output proto=+0x9(+0xe8(%dx)):u8 ip_dst=+0x10(+0xe8(%dx)):u32 b16_dest=+0x16(+0xe8(%dx)):u16 comm=$comm body=+0x1c(+0xe8(%dx)):string' >> /sys/kernel/debug/tracing/kprobe_events echo 'proto==17&&ip_dst==0xdd01000a&&b16_dest==1063' > /sys/kernel/debug/tracing/instances/surftrace/events/kprobes/f0/filter echo 1 > /sys/kernel/debug/tracing/instances/surftrace/events/kprobes/f0/enable echo 0 > /sys/kernel/debug/tracing/instances/surftrace/options/stacktrace echo 1 > /sys/kernel/debug/tracing/instances/surftrace/tracing_on <...>-2733784 [014] .... 12648619.219880: f0: (ip_output+0x0/0xd0) proto=17 ip_dst=10.0.1.221 b16_dest=9988 comm="nc" body="Hello World! @"
通过上述命令,可以确定报文的发送的 pid 为 2733784,进程名为 nc。
surftrace 'p dev_queue_xmit proto=@(struct iphdr *)l2%0->protocol ip_dst=@(struct iphdr *)l2%0->daddr f:proto==1&&ip_dst==192.168.1.3' -s
可以获取到以下调用栈:由于问题复现概率比较高,我们可以将怀疑的重点方向先放在包发送流程中,即从 icmp_echo 函数往上,用 Surftrace 在每一个符号都加一个 trace 点,追踪下回包到底消失在哪里。
rc = q->enqueue(skb, q, &to_free) & NET_XMIT_MASK;
此时,可以结合汇编信息:找到钩子函数存入的寄存名为 bx,然后通过 surftrace 打印出来。
surftrace 'p dev_queue_xmit+678 pfun=%bx'
然后将 pfun 值在 /proc/kallsyms 查找匹配。至此可以明确是 htb qdisc 导致丢包。确认相关配置存在问题后,将相关配置回退,网络性能得以恢复。
原文标题:龙蜥开源内核追踪利器 Surftrace:协议包解析效率提升 10 倍! | 龙蜥技术
文章出处:【微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。
全部0条评论
快来发表一下你的评论吧 !