关于内核:龙蜥开源内核追踪利器-Surftrace协议包解析效率提升-10-倍-龙蜥技术

45次阅读

共计 4909 个字符,预计需要花费 13 分钟才能阅读完成。

文 / 零碎运维 SIG

Surftrace 是由零碎运维 SIG 推出的一个 ftrace 封装器和开发编译平台,让用户既能基于 libbpf 疾速构建工程进行开发,也能作为 ftrace 的封装器进行 trace 命令编写。我的项目蕴含 Surftrace 工具集和 pylcc、glcc(python or generic C language for libbpf Compiler Collection),提供近程和本地 eBPF 的编译能力。

通过对 krobe 和 ftrace 相干性能最大化形象,同时对各种场景下的追踪能力加强(比方网络协议抓包),使得用户十分疾速的上手,对定位问题效率晋升 10 倍以上。另外,现如今火到天际的技术——eBPF,Surftrace 反对通过 libbpf 及 CO-RE 能力,对 bpf 的 map 和 prog 等罕用函数进行了封装和形象,基于此平台开发的 libbpf 程序能够无差别运行在各个支流内核版本上,开发、部署和运行效率晋升了一个数量级。

Surftrace 最大的劣势在于将以后支流的 trace 技术一并提供给宽广开发者,能够通过 ftrace 也能够应用 eBPF,利用场景笼罩内存、IO 等 Linux 各个子系统,特地是在网络协议栈跟踪下面,对 skb 外部数据结构,网络字节序解决做到行云流水,把简单留给本人,简略留给你。明天就让咱们来见识一下 Surftrace 在网络畛域的强劲体现吧。

一、了解 Linux 内核协定栈

定位网络问题是一个软件开发者必备一项根底技能,诸如 ping 连通性、tcpdump 抓包剖析等伎俩,能够对网络问题进行初步定界。然而,当问题深刻内核协定栈外部,如何将网络报文与内核协定栈清晰关联起来,精准追踪到关注的报文前进门路呢?

1.1 网络报文分层构造

援用自《TCP/IP 详解》卷一。

如上图所示,网络报文对数据报文数据在不同层进行封装。不同 OS 均采纳统一的报文封装形式,达到跨软件平台通信的目标。

1.2 sk_buff 构造体

sk_buff 是网络报文在 Linux 内核中的理论承载者,它在 include/linux/skbuff.h 文件中定义,构造体成员较多,本文不逐个开展。

用户须要重点关注上面两个构造体成员:

unsignedchar *head, *data;

其中 head 指向了缓冲区开始,data 指向了以后报文解决所在协定层的起始地位,如以后协定解决位于 tcp 层,data 指针就会指向 struct tcphdr。在 IP 层,则指向了 struct iphdr。因而,data 指针成员,是报文在内核处理过程中的要害信标。

1.3 内核网络协议栈地图

下图是协定栈解决地图,能够保留后放大观看(图源网络)。

不难发现,上图中简直所有函数都波及到 skb 构造体解决,因而要想深刻理解网络报文在内核的处理过程,skb->data 应该就是最现实的引路蜂。

二、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(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:

  • 能够跨协定层向上解析报文构造体,如在 l3 层去剖析 struct icmphdr 中的数据成员
  • 不能够跨协定层向下解析报文构造体,如在 l4 层去剖析 struct iphdr 中的成员

2.2 裁减下一层报文内容获取形式

surftrace 为 ethhdr、iphdr、icmphdr、udphdr、tcphdr 构造体增加了 xdata 成员,用于获取下一层报文内容。xdata 有以下 5 类类型:

数组下标是依照位宽进行对齐的,比方要提取 icmp 报文中的 2~3 字节内容,组成一个 unsigned short 的数据,能够通过以下办法获取:

data=@(struct icmphdr*)l3%0->sdata[1]`

2.3 IP 和字节序模式转换

网络报文字节序采取的是大端模式,而咱们的操作系统个别采纳小端模式。同时,ipv4 采纳了一个 unsigned int 数据类型来示意一个 IP,而咱们通常习惯采纳 1.2.3.4 的形式来示意一个 ipv4 地址。上述差别导致间接去解读网络报文内容的时候十分费劲。surftrace 通过往变量减少前缀的形式,在数据出现以及过滤的时候,将原始数据依据前缀命名规定进行转换,晋升可读性和便利性。

2.4 牛刀小试

咱们在一个实例上抓到一个非预期的 udp 报文,它会往指标 ip 10.0.1.221 端口号 9988 发送数据,当初想要确定这个报文的发送过程。因为 udp 是一种面向无连贯的通信协定,无奈间接通过 netstat 等形式锁定发送者。
用 Surftrace 能够在 ip_output 函数处中下钩子:

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 定位网络问题。

3.1 问题背景

咱们有两个实例通信存在性能问题,经抓包排查,确认性能上不去的根因是存在丢包导致的。侥幸的是,该问题能够通过 ping 对端复现,确认丢包率在 10% 左右。

通过进一步抓包剖析,能够明确报文失落在实例 B 外部。

通过查看 /proc/net/snmp 以及剖析内核日志,没有发现可疑的中央。

3.2 surftrace 跟踪

在 1.1 节的地图中,咱们能够查到网络报文是内核由 dev_queue_xmit 函数将报文推送到网卡驱动。因而,能够在这个进口先进行 probe,过滤 ping 报文,加上 -s 选项,打出调用栈:

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 点,追踪下回包到底隐没在哪里。

3.3 锁定丢包点

问题追踪到了这里,对于经验丰富的同学应该是能够猜出丢包起因。咱们无妨纯正从代码角度登程,再找一下精确的丢包地位。联合代码剖析,咱们能够在函数外部找到以下两处 drop 点:

通过 Surftrace 函数外部追踪性能,联合汇编代码信息,能够明确丢包点是出在了 qdisc->enqueue 钩子函数中。

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 在网络层面的加强,使得用户只须要有相干的网络根底和肯定的内核常识储备,就能够用较低编码工作量达到精准追踪网络报文在 Linux 内核的残缺处理过程。适宜用于追踪 Linux 内核协定栈代码、定位深层次网络问题。
参考文献:

【1】《TCP/IP 详解》

【2】《Linux 内核设计与实现》

【3】《深刻了解 Linux 网络技术底细》

【4】surftrace readmde:
https://github.com/aliyun/sur…

【5】https://lxr.missinglinkelectr…

龙蜥社区零碎运维 SIG 致力于打造一个集主机治理、配置部署、监控报警、异样诊断、平安审计等一系列性能的自动化运维平台。

 欢送更多开发者退出零碎运维 SIG:网址:https://openanolis.cn/sig/sysom
邮件列表:sysom@lists.openanolis.cn
surftracel 链接:https://gitee.com/anolis/surftrace

—— 完 ——

正文完
 0