当前位置: 首页 > Linux

DragonLizard开源内核跟踪工具Surftrace:协议包解析效率提升10倍!-龙力科技介绍

时间:2023-04-06 22:06:19 Linux

:如何明确网络包与内核协议栈的关联,并准确追踪相关包的行进路径?正文/系统运维SIGSurftrace是系统运维SIG推出的ftracewrapper和开发编译平台,可以让用户快速构建基于libbpf的项目进行开发,并将trace命令编写为ftracewrapper。该项目包括Surftrace工具集和pylcc、glcc(libbpfCompilerCollection的python或通用C语言),提供远程和本地eBPF编译功能。通过最大限度地抽象krobe和ftrace相关功能,增强各种场景下的跟踪能力(如网络协议抓包),用户可以非常快速上手,定位问题效率提升10倍以上。另外,时下流行的技术eBPF支持libbpf和CO-RE能力,对bpf的map、prog等常用功能进行封装和抽象。基于该平台开发的libbpf程序可以无差别地运行在各个主流内核版本上,开发、部署、运行效率都提高了一个数量级。Surftrace最大的优势在于它为广大开发者提供了目前主流的trace技术。您还可以通过ftrace使用eBPF。应用场景涵盖了内存、IO等各种Linux子系统,尤其是在网络协议栈追踪方面。对于skb内部的数据结构和网络字节序处理都顺畅顺畅,复杂留给自己,简单留给自己。今天就来看看Surftrace在网络领域的强势表现吧。一、了解Linux内核协议栈定位网络问题是软件开发人员必须具备的一项基本技能。ping连通性、tcpdump抓包分析等方法可以初步定界网络问题。然而,当问题深入到内核协议栈时,如何明确地将网络包与内核协议栈关联起来,并准确追踪相关包的行进路径?1.1网络数据包的层级结构引用《TCP/IP 详解》卷一。如上图所示,网络数据包在不同层封装了数据包数据。不同的OS采用一致的消息封装方式来达到跨软件平台通信的目的。1.2sk_buff结构sk_buff是Linux内核中网络包的实际载体。它在include/linux/skbuff.h文件中定义。结构体成员较多,本文不一一展开。用户需要关注以下两个结构成员:unsignedchar*head,*data;其中head指向buffer的开头,data指向当前消息处理所在协议层的起始位置。比如当前的协议处理是位于tcp层,而data指针会指向structtcphdr。在IP层,它指向structiphdr。因此,数据指针成员是内核处理消息过程中的一个关键信标。1.3内核网络协议栈图下图是协议栈处理图,可以保存放大查看(图片来源网络)。不难发现,上图中几乎所有的函数都涉及到skb结构体的处理,所以如果你想了解内核中对网络数据包的处理,skb->data应该是比较理想的引导。2.Surftrace增强对网络包的处理Surftrace在ftrace封装的基础上,采用了接近C语言的参数语法风格,将原来繁琐的配置过程优化为一行命令语句,大大简化了ftrace的部署步骤,是一个非常方便的内核跟踪工具。但是要跟踪网络数据包,仅仅解析一个skb->data指针是不够的。存在以下障碍:不同网络层中skb->data指针指向的协议头不固定;除了获取当前结构体的内容,还需要获取上层消息的内容。比如在udphdr结构体中,我们不能直接获取udp报文的内容;源数据的呈现不够人性化。例如ipv4报文IP是u32数据类型,不便于读取,也难以配置过滤器。针对以上难点,Surftrace对skb传参做了相应的特殊处理,以达到方便易用的效果。2.1网络协议层标记处理以__netif_receive_skb_core函数为例,它是跟踪网络协议栈消息接收的入口点。函数原型定义:staticint__netif_receive_skb_core(structsk_buff*skb,boolpfmemalloc,structpacket_type**ppt_prev);解析每个skb对应的消息三层协议成员的方法:surftrace'p__netif_receive_skb_coreproto=@(structiphdr*)l3%0->protocol`获取协议成员的方法是@(structiphdr*)l3%0->协议。Tips:可以跨协议层向上分析消息结构,比如在l3层分析structicmphdr中的数据成员。不能跨协议层向下分析消息结构,比如在l4层分析structiphdr中的成员2.2扩展获取下一层消息内容的方式。surftrace将xdata成员添加到ethhdr、iphdr、icmphdr、udphdr、tcphdr的结构中,获取下一层消息的内容。xdata有以下5种类型:数组下标按照位宽对齐,例如提取icmp报文中的2~3个字节组成一个unsignedshortdata,可以通过以下方法获取:data=@(structicmphdr*)l3%0->sdata[1]2.3IP与字节序模式的转换网络数据包的字节序模式采用big-endian模式,而我们的操作系统一般采用little-endian模式。同时ipv4使用一个unsignedint数据类型来表示一个IP,我们通常用1.2.3.4来表示一个ipv4地址。上述差异使得直接解读网络数据包的内容非常费力。surftrace在展示和过滤数据时,根据前缀命名规则转换原始数据,通过给变量添加前缀来提高可读性和便利性。2.4小测试我们在一个实例上抓到一条意外的udp报文,它会向目标ip10.0.1.221端口号9988发送数据,现在我们要确定这条报文的发送过程。由于udp是一种无连接的通信协议,无法通过netstat等方式直接锁定发送方。使用Surftracehookip_output函数:intip_output(structnet*net,structsock*sk,structsk_buff*skb)跟踪表达式:surftrace'pip_outputproto=@(structiphdr*)l3%2->protocolip_dst=@(结构iphdr*)l3%2->daddrb16_dest=@(structudphdr*)l3%2->destcomm=$commbody=@(structudphdr*)l3%2->Sdata[0]f:proto==17&&ip_dst==10.0.1.221&&b16_dest==9988'跟踪结果:surftrace'pip_outputproto=@(structiphdr*)l3%2->protocolip_dst=@(structiphdr*)l3%2->daddrb16_dest=@(structudphdr*)l3%2->destcomm=$commbody=@(structudphdr*)l3%2->Sdata[0]f:proto==17&&ip_dst==10.0.1.221&&b16_dest==9988'echo'p:f0ip_outputproto=+0x9(+0xe8(%dx)):u8ip_dst=+0x10(+0xe8(%dx)):u32b16_dest=+0x16(+0xe8(%dx)):u16comm=$commbody=+0x1c(+0xe8(%dx)):string'>>/sys/kernel/debug/tracing/kprobe_eventsecho'proto==17&&ip_dst==0xdd01000a&&b16_dest==1063'>/sys/kernel/debug/tracing/instances/surftrace/事件/kprobes/f0/filterecho1>/sys/kernel/debug/tracing/instances/surftrace/events/kprobes/f0/enableecho0>/sys/kernel/debug/tracing/instances/surftrace/options/stacktraceecho1>/sys/kernel/debug/tracing/instances/surftrace/tracing_on<...>-2733784[014]....12648619.219880:f0:(ip_output+0x0/0xd0)proto=17ip_d通过上面的命令,消息的传输可以确认pid为2733784,进程名为nc。3、实战:定位网络问题接下来,我们从一个实际的网络问题出发,介绍如何使用Surftrace定位网络问题。3.1问题背景我们有两个存在性能问题的通信实例。经过抓包排查,确认性能不佳的根本原因是丢包。好在通过ping对端可以重现问题,确认丢包率在10%左右。通过进一步的抓包分析,可以确定丢包是在实例B内部。通过查看/proc/net/snmp和分析内核日志,没有发现任何可疑的地方。3.2surftracetrace在1.1节的图中,我们可以发现网络消息是由内核通过dev_queue_xmit函数推送给网卡驱动的。所以可以先在这个出口探测,过滤ping消息,加上-s选项,显示调用栈:surftrace'pdev_queue_xmitproto=@(structiphdr*)l2%0->protocolip_dst=@(structiphdr*)l2%0->daddrf:proto==1&&ip_dst==192.168.1.3'-s可以得到如下调用栈:由于问题重现概率比较高,我们可以把怀疑的关键方向放在发包中先处理,即从icmp_echo函数往上,用Surftrace给每个符号加一个trace点,跟踪下一个回包消失的地方。3.3锁定丢包点的问题到这里已经追到了,有经验的同学应该能猜到丢包的原因。我们不妨从代码的角度入手,然后找到丢包的具体位置。结合代码分析,我们可以发现函数内部有如下两个丢包点:通过Surftrace函数内部的跟踪功能,结合汇编代码信息,可以确定丢包点在qdisc->入队挂钩函数。rc=q->enqueue(skb,q,&to_free)&NET_XMIT_MASK;这时候可以结合汇编信息:找到hook函数中存放的寄存器名bx,然后通过surftrace打印出来。surftrace'pdev_queue_xmit+678pfun=%bx'然后在/proc/kallsyms中查找pfun值的匹配项。至此,很明显是htbqdisc造成了丢包。确认相关配置有问题后,回滚相关配置,恢复网络性能。4.总结Surftrace在网络层面进行了增强,让用户只要有相关的网络基础和一定的内核知识储备,就能以相对较低的编码工作量,在Linux内核中准确跟踪网络数据包的完整处理过程。适用于跟踪Linux内核协议栈的代码,定位深层次的网络问题。参考文献:【1】《TCP/IP详解》【2】《Linux内核设计与实现》【3】《深入理解 Linux 网络技术内幕》【4】surftracereadmde:https://github.com/aliyun/sur...【5】https://lxr.missinglinkelectr...原文链接本文为阿里云原创内容,未经允许不得转载。