首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用 BPF 记录 TCP 重传和丢包记录

使用 BPF 记录 TCP 重传和丢包记录

原创
作者头像
ritchiechen
发布于 2021-03-07 14:51:07
发布于 2021-03-07 14:51:07
3.6K0
举报
文章被收录于专栏:Serverless+Serverless+

背景

云函数的日常运营中,经常有用户提出要求协助排查网络问题。一般的手段就是使用 tcpdump 抓包,但是部署抓包往往是在问题发生之后,而且抓包后复现的时机也不确定,往往费时费力。本文讲述使用 BPF 记录 TCP 的重传和丢包记录,作为定位网络问题的一种辅助手段。

在 BPF 出现之前

BPF 出现之前,在 Linux 上我们也是可以解决这个问题的,只不过比较繁琐,需要对内核、调试器编译器等许多基础知识有较深理解,参见这里

TCP 重传为例,我们使用 perf 工具来查找跟踪的点位:

代码语言:txt
AI代码解释
复制
$ sudo perf list 'tcp:*'

List of pre-defined events (to be used in -e):

  tcp:tcp_destroy_sock                               [Tracepoint event]
  tcp:tcp_probe                                      [Tracepoint event]
  tcp:tcp_rcv_space_adjust                           [Tracepoint event]
  tcp:tcp_receive_reset                              [Tracepoint event]
  tcp:tcp_retransmit_skb                             [Tracepoint event]
  tcp:tcp_retransmit_synack                          [Tracepoint event]
  tcp:tcp_send_reset                                 [Tracepoint event]


Metric Groups:

对于 TCP 重传,显然 tcp_retransmit_skb 是一个合适的跟踪点位。让我们来看看这个函数在内核代码中的签名:

代码语言:txt
AI代码解释
复制
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs);

struct sock 的定义在这里,它包含了我们所需要的信息。这是一个庞大且复杂的结构体,而且对于 kprobe 来说,我们只能使用寄存器以及偏移来输出其值。

要知道这个结构体中每个字段的真实偏移,我们需要内核的符号表,使用 GDB 来确定其值:

代码语言:txt
AI代码解释
复制
$ sudo apt install linux-image-unsigned-5.8.0-37-generic-dbgsym
$ gdb /usr/lib/debug/boot/vmlinux-5.8.0-37-generic
(gdb) ptype struct sock
...
(gdb) print (int)&((struct sock*)0)->__sk_common.skc_dport
$1 = 12

除此之外,我们还需要了解 X86_64调用约定,诸如函数调用的第一个参数使用 di 寄存器传递等。

有了这些背景知识后,我们可以使用 kprobe 来达成这一目标,记录 TCP 重传的例子如下:

代码语言:txt
AI代码解释
复制
$ echo 'p:kprobes/tcp_retransmit tcp_retransmit_skb port=+12(%di):u16 dst=+0(%di):u32 state=+18(%di):u8' >> /sys/kernel/debug/tracing/kprobe_events
$ echo 1 > /sys/kernel/debug/tracing/events/kprobes/tcp_retransmit/enable

这个例子不仅晦涩难懂,而且不易开发及调试,好在现在我们有了 BPF

BPF 横空出世

BPF 是一项革命性技术,它能在内核中运行沙箱程序, 而无需修改内核源码或者加载内核模块。

对于上面的例子,一个等价的 BPF 程序如下:

代码语言:txt
AI代码解释
复制
#include <uapi/linux/ptrace.h>
#include <net/sock.h>

int log_tcp_retransmit(struct pt_regs *ctx, struct sock *sk) {
    u16 port = sk->__sk_common.skc_dport;
    u32 daddr = sk->__sk_common.skc_daddr;
    u8 state = sk->__sk_common.skc_state;
    bpf_trace_printk("tcp_retransmit port=%d dst=%d state=%d\n", port, daddr, state);
    return 0;
}

使用 C 编写,不需要理解 ABI 等细节,而且方便调试。你可以在这里找到完整的代码。对于 TCP 重传,也是一样的道理。

重传的日志记录在 /sys/kernel/debug/tracing/trace,下面是一些真实的记录:

代码语言:txt
AI代码解释
复制
$ sudo cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 103/103   #P:1
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
...
          <idle>-0       [000] ..s. 2621728.977959: 0: tcp_retransmit port=37897 dst=831376843 state=1
           <...>-3202753 [000] ..s. 2622104.077378: 0: tcp_retransmit port=37897 dst=831376843 state=1
...

转换一下格式,可以看到重传的目的地址及端口等信息:

代码语言:txt
AI代码解释
复制
2621728.977959 2021-03-05 19:48:18.959403       tcp_retransmit  203.205.141.49           2452   TCP_ESTABLISHED
2622104.077378 2021-03-05 19:54:34.058822       tcp_retransmit  203.205.141.49           2452   TCP_ESTABLISHED

结论

本文讲述使用 BPF 带来的可观测性能力,获取 TCP 的重传及丢包记录,作为辅助定位网络问题的手段。与传统的 kprobe 方式相比, BPF 带来的可编程性极大地提升了开发效率,既没有增加系统的复杂度,也不会牺牲执行效率和安全性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
TCP经典异常问题探讨与解决
最近一年的时间里,现网碰到RST问题屡屡出现,一旦TCP连接中收到了RST包,大概率会导致连接中止或用户异常。如何正确解决RST异常是较为棘手的问题。
腾讯技术工程官方号
2024/05/08
1K0
TCP经典异常问题探讨与解决
利用动态 tracing 技术来 trace 内核中的网络请求
这周帮朋友用 eBPF/SystemTap 这样的动态 tracing 工具做了一些很有趣的功能。这篇文章算是一个总结
青南
2021/06/23
2K0
利用动态 tracing 技术来 trace 内核中的网络请求
ebpf技术_EBM技术
BPF,及伯克利包过滤器Berkeley Packet Filter,最初构想提出于 1992 年,其目的是为了提供一种过滤包的方法,并且要避免从内核空间到用户空间的无用的数据包复制行为。它最初是由从用户空间注入到内核的一个简单的字节码构成,它在那个位置利用一个校验器进行检查 —— 以避免内核崩溃或者安全问题 —— 并附着到一个套接字上,接着在每个接收到的包上运行。几年后它被移植到 Linux 上,并且应用于一小部分应用程序上(例如,tcpdump)。其简化的语言以及存在于内核中的即时编译器(JIT),使 BPF 成为一个性能卓越的工具。
全栈程序员站长
2022/11/08
2.6K0
ebpf技术_EBM技术
【云原生技术研究】 从bpftrace看如何利用eBPF实现内核追踪
bpftrace提供了一种快速利用eBPF实现动态追踪的方法,可以作为简单的命令行工具或者入门级编程工具来使用。本文以bpftrace为例,介绍如何利用eBPF实现内核的动态追踪。
绿盟科技研究通讯
2020/09/01
2.6K0
【云原生技术研究】 从bpftrace看如何利用eBPF实现内核追踪
eBPF入门开发实践教程十三:统计 TCP 连接延时,并使用 libbpf 在用户态处理数据
eBPF (Extended Berkeley Packet Filter) 是一项强大的网络和性能分析工具,被应用在 Linux 内核上。eBPF 允许开发者动态加载、更新和运行用户定义的代码,而无需重启内核或更改内核源代码。
云微
2023/08/14
1.1K0
eBPF入门实践教程十四:记录 TCP 连接状态与 TCP RTT
eBPF (扩展的伯克利数据包过滤器) 是一项强大的网络和性能分析工具,被广泛应用在 Linux 内核上。eBPF 使得开发者能够动态地加载、更新和运行用户定义的代码,而无需重启内核或更改内核源代码。
云微
2023/08/14
8740
TCP的定时器实现(1)——重传定时器
TCP协议是一个相当复杂的协议,其实现依赖于多个定时器的实现。在TCP套接字的初始化函数tcp_v4_init_sock中,会调用tcp_init_xmit_timers初始化TCP的各个定时器。
glinuxer
2019/04/10
2.5K0
Linux探测工具BCC(网络)
首先看下促使我学习bcc的这篇文章中的程序traceicmpsoftirq.py,使用该程序的本意是找出对ping响应的进程位于哪个CPU core上,然后使用perf扫描该core,找出造成网络延迟的原因。源码如下:
charlieroro
2020/07/13
2.7K0
Kubernetes 中的 eBPF
BPF (Berkeley Packet Filter) 最早是用在 tcpdump 里面的,比如 tcpdump tcp and dst port 80 这样的过滤规则会单独复制 tcp 协议并且目的端口是 80 的包到用户态。整个实现是基于内核中的一个虚拟机来实现的,通过翻译 BPF 规则到字节码运行到内核中的虚拟机当中。最早的论文是这篇,这篇论文我大概翻了一下,主要讲的是原本的基于栈的过滤太重了,而 BPF 是一套能充分利用 CPU 寄存器,动态注册 filter 的虚拟机实现,相对于基于内存的实现更高效,不过那个时候的内存比较小才几十兆。bpf 会从链路层复制 pakcet 并根据 filter 的规则选择抛弃或者复制,字节码是这样的,具体语法就不介绍了,一般也不会去直接写这些字节码,然后通过内核中实现的一个虚拟机翻译这些字节码,注册过滤规则,这样不修改内核的虚拟机也能实现很多功能。
黑光技术
2020/05/14
1.5K0
Kubernetes 中的 eBPF
TCP源码分析 - 三次握手之 connect 过程
本文主要分析 TCP 协议的实现,但由于 TCP 协议比较复杂,所以分几篇文章进行分析,这篇主要介绍 TCP 协议建立连接时的三次握手过程。
用户7686797
2021/03/16
2.2K0
TCP源码分析 - 三次握手之 connect 过程
深入理解TCP/IP协议的实现之三次握手(基于linux1.2.13)
上篇我们分析了accept函数,他是消费者,这篇我们看看生产者是怎么实现的。我们从tcp_rcv函数开始,这个函数是一个分发器。当接收到一个tcp包的时候,底层就会调这个函数交给tcp层处理。
theanarkh
2020/03/12
6680
如何用 eBPF 实现 Kubernetes 网络可观测性?实战指南
在Kubernetes生产环境中,我们曾遇到一个典型故障:某Node上的Pod间歇性无法访问Service,但tcpdump和kubectl describe endpoints均未显示异常。最终发现是CNI插件的ARP表溢出导致,这个案例暴露了传统工具的三大缺陷:
大熊计算机
2025/07/15
1200
如何用 eBPF 实现 Kubernetes 网络可观测性?实战指南
Linux系统研究 - 操作系统是如何管理tcp连接的 (2)
接上一篇文章 Linux系统研究 - 操作系统是如何管理tcp连接的 (1),我们再来继续讲。
KINGYT
2019/11/07
3.3K1
漫画 | 理解了TCP连接的实现以后,客户端的并发也爆发了!
漫画 | 一台Linux服务器最多能支撑多少个TCP连接?文章里有介绍服务器能支撑的连接数远不止65535,但客服端呢,是不是受限于此呢,本文给你解惑。
桶哥
2021/01/04
1.4K0
漫画 | 理解了TCP连接的实现以后,客户端的并发也爆发了!
Linux内核网络udp数据包发送(二)——UDP协议层分析
本文分享了Linux内核网络数据包发送在UDP协议层的处理,主要分析了udp_sendmsg和udp_send_skb函数,并分享了UDP层的数据统计和监控以及socket发送队列大小的调优。
Linux阅码场
2021/08/26
6.5K0
UDP协议源码分析
UDP协议是 User Datagram Protocol 的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,位于 TCP/IP协议 模型的 传输层,如下图:
用户7686797
2021/03/15
1K0
能将三次握手理解到这个深度,面试官拍案叫绝!
在后端相关岗位的入职面试中,三次握手的出场频率非常的高,甚至说它是必考题也不为过。一般的答案都是说客户端如何发起 SYN 握手进入 SYN_SENT 状态,服务器响应 SYN 并回复 SYNACK,然后进入 SYN_RECV,...... , 吧啦吧啦诸如此类。
开发内功修炼
2022/03/24
5110
能将三次握手理解到这个深度,面试官拍案叫绝!
UDP协议源码解析之发送
UDP不是面向连接的协议,发送数据的时候指定目的地址的信息即可以发送。下面是入口函数的代码。
theanarkh
2019/03/06
1.3K0
socket连接和通信过程解析
网络通信的标准流程是,服务端新建一个socket,然后在该socket中绑定一个地址,再设置该socket为监听socket,然后阻塞在accept等待连接。客户端新建一个socket,然后connect到一个服务端的地址。下面分析一下这个过程。看多个客户端或者多个连接是如何在一个监听的socket中完成通信的。 服务器收到一个syn包的时候,在tcp_rcv中进行处理。该函数根据tcp数据包中的目的端口和ip,源端口和ip找到一个最匹配的socket
theanarkh
2019/05/06
1.6K0
socket连接和通信过程解析
从linux源码看socket(tcp)的timeout
网络编程中超时时间是一个重要但又容易被忽略的问题,对其的设置需要仔细斟酌。在经历了数次物理机宕机之后,笔者详细的考察了在网络编程(tcp)中的各种超时设置,于是就有了本篇博文。本文大部分讨论的是socket设置为block的情况,即setNonblock(false),仅在最后提及了nonblock socket(本文基于linux 2.6.32-431内核)。
无毁的湖光-Al
2020/01/15
5.1K0
从linux源码看socket(tcp)的timeout
相关推荐
TCP经典异常问题探讨与解决
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档