前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 BPF 记录 TCP 重传和丢包记录

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

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

背景

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

在 BPF 出现之前

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

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

代码语言:txt
复制
$ 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
复制
int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs);

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

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

代码语言:txt
复制
$ 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
复制
$ 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
复制
#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
复制
$ 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
复制
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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 在 BPF 出现之前
  • BPF 横空出世
  • 结论
相关产品与服务
云函数
云函数(Serverless Cloud Function,SCF)是腾讯云为企业和开发者们提供的无服务器执行环境,帮助您在无需购买和管理服务器的情况下运行代码。您只需使用平台支持的语言编写核心代码并设置代码运行的条件,即可在腾讯云基础设施上弹性、安全地运行代码。云函数是实时文件处理和数据处理等场景下理想的计算平台。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档