把asp.net写的网站别人怎么访问,深圳和胜建设公司,有网站怎么做app,长沙网站推广¥做下拉去118cr简介#xff1a;如何将网络报文与内核协议栈清晰关联起来精准追踪到关注的报文行进路径呢#xff1f; 文/系统运维 SIG
Surftrace 是由系统运维 SIG 推出的一个 ftrace 封装器和开发编译平台#xff0c;让用户既能基于 libbpf 快速构建工程进行开发#xff0c;也能作为 ft…简介如何将网络报文与内核协议栈清晰关联起来精准追踪到关注的报文行进路径呢 文/系统运维 SIG
Surftrace 是由系统运维 SIG 推出的一个 ftrace 封装器和开发编译平台让用户既能基于 libbpf 快速构建工程进行开发也能作为 ftrace 的封装器进行 trace 命令编写。项目包含 Surftrace 工具集和 pylcc、glcc(python or generic C language for libbpf Compiler Collection)提供远程和本地 eBPF 的编译能力。
通过对 krobe 和 ftrace 相关功能最大化抽象同时对各种场景下的追踪能力增强比如网络协议抓包使得用户非常快速的上手对定位问题效率提升 10 倍以上。另外现如今火到天际的技术——eBPFSurftrace 支持通过 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:proto17ip_dst10.0.1.221b16_dest9988追踪结果
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:proto17ip_dst10.0.1.221b16_dest9988 echo p:f0 ip_output proto0x9(0xe8(%dx)):u8 ip_dst0x10(0xe8(%dx)):u32 b16_dest0x16(0xe8(%dx)):u16 comm$comm body0x1c(0xe8(%dx)):string /sys/kernel/debug/tracing/kprobe_events echo proto17ip_dst0xdd01000ab16_dest1063 /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_output0x0/0xd0) proto17 ip_dst10.0.1.221 b16_dest9988 commnc bodyHello 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:proto1ip_dst192.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_xmit678 pfun%bx然后将 pfun 值在 /proc/kallsyms 查找匹配。 至此可以明确是 htb qdisc 导致丢包。确认相关配置存在问题后将相关配置回退网络性能得以恢复。
四、总结
Surftrace 在网络层面的增强使得用户只需要有相关的网络基础和一定的内核知识储备就可以用较低编码工作量达到精准追踪网络报文在 Linux 内核的完整处理过程。适合用于追踪 Linux 内核协议栈代码、定位深层次网络问题。
参考文献
【1】《TCP/IP详解》
【2】《Linux内核设计与实现》
【3】《深入理解 Linux 网络技术内幕》
【4】surftrace readmde
surftrace/ReadMe.md at master · aliyun/surftrace · GitHub
【5】https://lxr.missinglinkelectronics.com
原文链接
本文为阿里云原创内容未经允许不得转载。