2024-10-24 21:00
一、背景介绍
在Linux内核网络开发过程中,网络丢包是一个常见的挑战。传统的网络抓包工具(如)虽然可以帮助开发者定位问题,但效率较低,定位深层网络问题的能力有限。随着eBPF技术的快速发展,更先进的问题跟踪能力也随之出现。基于该技术,字节跳动STE团队开发了下一代内核网络抓包工具:(net,原内部名称:xcap),并正式对外开源,地址:.
与只能作用于内核网络协议栈准备发送和接收数据包的固定点的工具相比,几乎可以跟踪整个内核网络协议栈(以 skb 作为参数的函数)。字节跳动STE团队以语法为过滤条件,以skb()为上下文,轻松掌握整个报文在内核网络协议栈中的完整踪迹,从而帮助开发者大幅提升定位内核网络丢包问题的效率。
2. 使用示例
例1:检查IP 10.227.0.45的icmp数据包是否到达内核期望的函数调用点。这样做的好处是,在定位和排查网络问题时,可以轻松缩小怀疑范围,提高效率。
复制
netcap skb -f icmp_rcv@1 -i eth0 -e 'host 10.227.0.45' -t '-nnv'
- 1.
-f后面的参数是or的具体函数(默认是),需要告诉skb这个函数的哪个参数(从1开始)(本例中),本例中是第一个。
示例2:查看内核向TCP端口9000发送的数据包的丢包位置
复制
netcap skb -f tracepoint:skb:kfree_skb -e 'tcp port 9000' -S
- 1.
其中-f后面的参数是or的具体功能,不需要传递skb是哪个参数。
-S 表示同时打印出本次调用的堆栈。在此示例中,您可以使用堆栈来查看数据包丢失的位置。
例如,在机器上配置丢包规则,丢弃传入的tcp 9000数据包,如下图所示:
复制
iptables -A INPUT -p tcp --dport 9000 -j DROP
- 1.
然后使用上面的命令观察丢包情况:
其他命令行参数可以通过阅读开源代码或者命令help skb来详细了解。
3、主要框架的设计与实现
通过/方法实现函数hook,通过函数参数获取skb和sock key结构体,获取网络包数据,通过bpf映射和用户态传输数据。
实现原理
工作原理大致如下:在eBPF程序中完成对数据包的过滤,找到经过语法过滤的数据包,然后将此数据包发送给应用程序。然后应用程序将数据包发送到显示器,或直接输出 pcap 文件。如下图:
1. 如何按语法过滤
过滤语法基于cBPF,使用开源库:这里可以将过滤语法转换为C函数,并且这个C函数可以嵌入到eBPF程序中。转换为C函数的基本原理是:首先使用库将过滤语法转换为cBPF指令代码,然后根据该指令代码将其转换为C语言函数。如下图:
2. 如何使用显示数据包的内容
程序启动后,还会启动一个程序,该程序的标准输入接收pcap格式的输入流,然后用不同的参数从其标准输出中打印出解析后的格式(例如-e是显示mac 地址)。如下图:
3. 如何查找数据包内容
在内核中,skb用于描述数据包。如果找到skb中指定的不同位置,就可以找到整个数据包。 skb的结构大致如下:
4、发送方向的数据包不完整。如何过滤数据包?
发送数据包时,比如函数,有时会没有填写完整的eth头、ip头、tcp头。那么如何获取完整的数据包呢?
我们会尽力根据skb的sock结构来推演并还原数据包。这时,捕获的数据包的一些非关键信息将与实际情况不一致(例如ip头的id字段)。 skb通过sock导出数据包内容的逻辑大致如下图所示:
4.其他用法及扩展多Trace点汇总分析
您可以统计数据包经过多个点所花费的时间,然后汇总输出以分析性能。例如,使用以下命令:
复制
netcap skb -f tracepoint:net:netif_receive_skb,ip_local_deliver@1,ip_local_deliver_finish@3,icmp_rcv@1 -e 'host 10.227.0.72 and icmp' -i eth0 --gather --gather-output-color cyan
- 1.
您可以按如下方式观察输出。根据到达跟踪点的时间,您可以分析数据包性能丢失的位置,或者可能引入延迟的位置。
扩展功能
用户可以自定义自己的过滤函数和输出函数。以下是示例:
复制
netcap skb -f icmp_rcv@1 -e 'host 10.227.0.72' -i eth0 --user-filter skb_user_filter.c --user-action skb_user_action.c --user-output-color green
- 1.
扩展过滤器文件.c如下:
复制
// Return 0 means it's not need, pls filter out it.
static inline int xcap_user_filter(void *ctx, void *pkt, u16 trace_index)
{
return 1;
}
- 1.
- 2.
- 3.
- 4.
- 5.
如果该扩展函数的返回值为0,则表示在语法过滤之后,进行了用户自定义的过滤。比如你可以很方便的写几行脚本,然后根据skb->mark进行过滤。
扩展输出文件.c如下:
复制
struct xcap_user_extend {
int a; // format: 0x%x
uint32_t b; //
int64_t c;
uint8_t x1; // format: %c
uint8_t x2; // format: 0x%x
uint16_t x3; // format: 0x%x
};
// Return 0 means not need to ouput
static inline int xcap_user_action(void *ctx, void *pkt, u32 pkt_len, struct xcap_user_extend *user, u16 trace_index)
{
user->a = 0x12345678;
user->b = 1000;
user->c = 2002;
user->x1 = 'M';
user->x2 = 0x11;
user->x3 = 0xabcd;
return 1;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
它是一个用户定义的结构。如果要输出任何信息,只需定义该结构并为其赋值即可。结构体支持的类型如下(注:不支持指针和数组): , , char, , , int, ,, 。
这样就可以输出一些信息了,如下图:
5. 未来展望
在开发者的日常工作中,网络抓包工具已经成为网络工程师、测试工程师等必备技能之一。字节跳动STE团队开源的网络抓包工具希望能够帮助大家完善对内核网络丢包问题的定位。为了提高效率,非常欢迎开发者加入并贡献PR,共同推动开源项目的发展。未来我们还将在以下方向进行优化,敬请期待。
#artContent h1{font-size:16px;font-weight: 400;}