TCP 连接排故:使用 BPF BCC工具包进行网络跟踪

2024-05-29 18:34:12 浏览数 (2)

写在前面

  • 博文内容为 BCC 进行网络跟踪常见工具介绍
  • tcpconnect:主动的 TCP 连接跟踪
  • tcpaccept:被动的 TCP 连接跟踪
  • tcpretrans:重传的 TCP 连接跟踪
  • tcptracer:已建立的 TCP 连接跟踪
  • tcpconnlat:测量出站 TCP 连接的延迟
  • tcpdrop:被内核丢弃的 TCP 数据包跟踪
  • tcplife: TCP 会话追踪
  • tcpstates: TCP 状态更改跟踪
  • tcpsubnet:统计发送到特定子网的 TCP 流量
  • tcptop: IP 端口的网络吞吐量跟踪
  • solisten: 本机 IPv4 和 IPv6 侦听跟踪
  • softirqs:软中断的服务时间统计
  • netqtop:统计网卡上数据包大小和计数
  • 理解不足小伙伴帮忙指正 :),生活加油

不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树


关于 BPF/eBPF , BCC/bpftrace 是什么这里不多讲,小伙伴可以看我之前的文章

Linux 可观测性 BPF&eBPF 以及 BCC&bpftrace 认知

认识之前,简单回忆一下,TCP 三次握手,四次挥手

三次握手

代码语言:javascript复制
客户端                       服务器
SYN-SENT  ------ SYN(seq=x) ---->
                 <---- SYN ACK(seq=y, ack=x 1) ---- SYN_RECV
ESTABLISHED  ---- ACK(ack=y 1, seq=x 1) ---->
  • 第一次握手:客户端发送一个SYN包(SYN=1,seq=x)到服务器,请求建立连接。客户端进入SYN_SENT状态。
  • 第二次握手:服务器收到 SYN 包,必须确认客户端的 SYN,发送 ACK(ACK=1,ack=x 1),同时自己也发送一个SYN包(SYN=1,seq=y)。服务器进入SYN_RECV状态。
  • 第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=1,ack=y 1,seq=x 1)。客户端和服务器进入ESTABLISHED状态,连接建立完成。

四次挥手

代码语言:javascript复制
客户端                       服务器
FIN_WAIT1  ------ FIN(seq=u) ---->
                 <---- ACK(ack=u 1) ---- CLOSE_WAIT
                                    <---- FIN(seq=w) ----
TIME_WAIT  ---- ACK(ack=w 1) ----->
                   (等待2MSL后关闭连接)
                                    (服务器也关闭连接)
  • 第一次挥手:客户端发送一个FIN包(FIN=1,seq=u)给服务器端,请求关闭连接。客户端进入FIN_WAIT1状态。
  • 第二次挥手:服务器端收到FIN包后发送一个ACK包(ACK=1,ack=u 1)给客户端,表示已经收到客户端的关闭连接请求。服务器端进入CLOSE_WAIT状态。
  • 第三次挥手: 服务器端关闭连接,发送一个FIN包(FIN=1,seq=w)给客户端。服务器端进入 LAST_ACK 状态。
  • 第四次挥手: 客户端收到FIN包后发送一个ACK包(ACK=1,ack=w 1)给服务器端,表示已经收到服务器端的关闭连接请求。客户端进入TIME_WAIT状态,等待2MSL时间后关闭连接。服务器端收到 ACK 包后关闭连接。

tcpconnect:主动的 TCP 连接跟踪

tcpconnect(8)会在每次主动的TCP连接(从当前机器发起的)建立(跟踪内核函数connect()调用)时,打印一行信息,包含源地址、目的地址。在输出中应该寻找不寻常的连接请求,它们可能会暴露出软件配置的低效,也可能暴露入侵行为

代码语言:javascript复制
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcpconnect #/usr/share/bcc/tools/tcpconnect 
Tracing connect ... Hit Ctrl-C to end
PID     COMM         IP SADDR            DADDR            DPORT
103502  curl         4  192.168.26.100   192.168.26.100   8000
103509  curl         4  192.168.26.100   192.168.26.100   8780
103512  mysql        4  192.168.26.100   192.168.26.100   3306
3053    haproxy      4  192.168.26.100   192.168.26.100   3306
3053    haproxy      4  192.168.26.100   192.168.26.102   4569
3053    haproxy      4  192.168.26.100   192.168.26.100   3306
103550  curl         4  192.168.26.100   192.168.26.100   5000
3053    haproxy      4  192.168.26.100   192.168.26.100   3306
3053    haproxy      4  192.168.26.100   192.168.26.100   4569
103572  mysql        4  192.168.26.100   192.168.26.100   3306
3053    haproxy      4  192.168.26.100   192.168.26.102   5000
3053    haproxy      4  192.168.26.100   192.168.26.102   8775
3053    haproxy      4  192.168.26.100   192.168.26.102   8000
................................
3053    haproxy      4  192.168.26.100   192.168.26.100   3306
3053    haproxy      4  192.168.26.100   192.168.26.102   35357
3053    haproxy      4  192.168.26.100   192.168.26.101   80

这个工具的开销应该可以忽略不计,因为它只跟踪执行连接connect.内核函数。它不是跟踪每个包然后过滤。

t选项打印一个时间戳列:

代码语言:javascript复制
# ./tcpconnect -t
TIME(s)  PID    COMM         IP SADDR            DADDR            DPORT
31.871   2482   local_agent  4  10.103.219.236   10.251.148.38    7001
31.874   2482   local_agent  4  10.103.219.236   10.101.3.132     7001

d选项跟踪 DNS 响应,并尝试将每个连接与之前发出的 DNS 查询关联起来。如果找到与该 IP 匹配的 DNS 响应,则将打印该响应。如果未找到匹配项,则在此列中打印"No DNS Query"

代码语言:javascript复制
# ./tcpconnect -d
PID    COMM         IP SADDR            DADDR            DPORT QUERY
1543   amazon-ssm-a 4  10.66.75.54      176.32.119.67    443   ec2messages.us-west-1.amazonaws.com
1479   telnet       4  127.0.0.1        127.0.0.1        23    localhost
1469   curl         4  10.201.219.236   54.245.105.25    80    www.domain.com (123.342ms)
1469   curl         4  10.201.219.236   54.67.101.145    80    No DNS Query
1991   telnet       6  ::1              ::1              23    localhost
2015   ssh          6  fe80::2000:bff:fe82:3ac fe80::2000:bff:fe82:3ac 22    anotherhost.org

127.0.0.1的查询和::1自动与 localhost 相关联。如果收到DNS响应和跟踪连接调用之间的时间超过100毫秒,该工具将在查询名称之后打印时间差

L选项打印 LPORT 列: 目标端口

代码语言:javascript复制
# ./tcpconnect -L
PID    COMM         IP SADDR            LPORT  DADDR            DPORT
3706   nc           4  192.168.122.205  57266  192.168.122.150  5000
3722   ssh          4  192.168.122.205  50966  192.168.122.150  22
3779   ssh          6  fe80::1          52328  fe80::2          22

显示 UID 和 PID

代码语言:javascript复制
# ./tcpconnect -Uu 1000
UID   PID    COMM         IP SADDR            DADDR            DPORT
1000  31338  telnet       6  ::1              ::1              23
1000  31338  telnet       4  127.0.0.1        127.0.0.1        23

其他的一些操作 Demo

代码语言:javascript复制
examples:
    ./tcpconnect           # trace all TCP connect()s
    ./tcpconnect -t        # include timestamps
    ./tcpconnect -d        # include DNS queries associated with connects
    ./tcpconnect -p 181    # only trace PID 181
    ./tcpconnect -P 80     # only trace port 80
    ./tcpconnect -P 80,81  # only trace port 80 and 81
    ./tcpconnect -4        # only trace IPv4 family
    ./tcpconnect -6        # only trace IPv6 family
    ./tcpconnect -U        # include UID
    ./tcpconnect -u 1000   # only trace UID 1000
    ./tcpconnect -c        # count connects per src ip and dest ip/port
    ./tcpconnect -L        # include LPORT while printing outputs
    ./tcpconnect --cgroupmap mappath  # only trace cgroups in this BPF map
    ./tcpconnect --mntnsmap mappath   # only trace mount namespaces in the map

tcpaccept:被动的 TCP 连接跟踪

tepaccept(8)是 tcpconnect(8)工具的搭档。每当有被动的TCP连接建立(接受的一方)时(通 tcpaccept()),就会打印一行信息,同样包含源地址和目的地址。

tcpconnect 工具使用 eBPF 特性来跟踪出去的 TCP 连接尝试。该工具的输出还包括失败的连,同样 tcpconnect 工具是轻量级的,因为它跟踪内核的 connect() 函数,而不是捕获和过滤数据包。

内核在 TCP 3 次握手中接收 ACK 数据包后,内核会将来自 SYN 队列的连接移到 accept 队列,直到连接的状态变为 ESTABLISHED。因此,只有成功的 TCP 连接才能在此队列中看到。

可以使用 tcpaccept 进行常规故障排除,来显示服务器已接受的新连接

代码语言:javascript复制
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcpaccept #/usr/share/bcc/tools/tcpaccept 
PID     COMM         IP RADDR            RPORT LADDR            LPORT
3193    xinetd       6  ::ffff:192.168.26.100 55384 ::ffff:192.168.26.100 4569
6621    mariadbd     4  192.168.26.100   42682 192.168.26.100   3306
3193    xinetd       6  ::ffff:192.168.26.102 56114 ::ffff:192.168.26.100 4569
6621    mariadbd     4  192.168.26.100   42696 192.168.26.100   3306
3636    httpd        4  192.168.26.100   38142 192.168.26.100   80
3193    xinetd       6  ::ffff:192.168.26.101 47122 ::ffff:192.168.26.100 4569
6621    mariadbd     4  192.168.26.100   42700 192.168.26.100   3306
3994    httpd        4  192.168.26.100   57924 192.168.26.100   8004
3053    haproxy      4  192.168.26.101   51722 192.168.26.99    3306
6621    mariadbd     4  192.168.26.100   42702 192.168.26.100   3306
.......................................
6621    mariadbd     4  192.168.26.100   52492 192.168.26.100   3306
^C┌──[root@vms100.liruilongs.github.io]-[~]
└─$

每次内核处理一个出去的连接时,tcpconnect 都会显示连接的详情。其他的一些选项

代码语言:javascript复制
examples:
    ./tcpaccept           # trace all TCP accept()s
    ./tcpaccept -t        # include timestamps
    ./tcpaccept -P 80,81  # only trace port 80 and 81
    ./tcpaccept -p 181    # only trace PID 181
    ./tcpaccept --cgroupmap mappath  # only trace cgroups in this BPF map
    ./tcpaccept --mntnsmap mappath   # only trace mount namespaces in the map
    ./tcpaccept -4        # trace IPv4 family only
    ./tcpaccept -6        # trace IPv6 family only

tcpretrans:重传的 TCP 连接跟踪

每次TCP重传数据包时tcpretrans(8)会打印一行记录,包含源地址和目的地址,以及当时该 TCP 连接所处的内核状态。TCP 重传会导致延迟和吞吐量方面的问题。

如果重传发生在 TCPESTABLISHED状态下,会进一步寻找外部网络可能存在的问题。如果重传发在SYNSENT状态下,这可能是 CPU 饱和的一个征兆,也可能是内核丢包引发的。

tcpretrans 工具显示有关 TCP 重新传输的详细信息,如本地和远程的 IP 地址和端口号,以及重新传输时 TCP 的状态。

该工具使用 eBPF 功能,因此开销非常低。每次内核调用 TCP 重新传输函数时,tcpretrans 都会显示连接的详情。

代码语言:javascript复制
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcpretrans #/usr/share/bcc/tools/tcpretrans
Tracing retransmits ... Hit Ctrl-C to end
TIME     PID     IP LADDR:LPORT          T> RADDR:RPORT          STATE
12:35:36 107232  4  192.168.26.100:39122 R> 192.168.26.100:3306  FIN_WAIT1
12:35:36 0       4  192.168.26.99:3306   R> 192.168.26.101:57360 ESTABLISHED
12:35:55 0       4  192.168.26.99:48370  R> 192.168.26.99:3306   FIN_WAIT1
12:36:07 11139   4  192.168.26.100:43004 R> 192.168.26.100:3306  FIN_WAIT1
12:36:07 11139   4  192.168.26.99:45960  R> 192.168.26.99:3306   FIN_WAIT1
12:36:07 11139   4  192.168.26.99:45950  R> 192.168.26.99:3306   FIN_WAIT1
12:36:07 3053    4  192.168.26.99:46030  R> 192.168.26.99:3306   FIN_WAIT1
12:36:07 27      4  192.168.26.99:46014  R> 192.168.26.99:3306   FIN_WAIT1
^C┌──[root@vms100.liruilongs.github.io]-[~]
└─$
  • ESTABLISHED 状态表示连接已经建立,并且数据可以在两个方向上进行传输。
  • FIN_WAIT1 表明本地端已经收到来自远程端的 FIN 分片,但本地应用还未完全关闭连接,因此本地端等待应用层的关闭指示。如果这种状态持续很长时间,那么可能存在应用程序关闭不当或网络问题导致远程端未能及时收到 ACK 分片。

重传通常是网络健康状况不佳的标志,这个工具对他们的调查很有用。与使用tcpdump不同,该工具的开销非常低,因为它只跟踪重传函数

代码语言:javascript复制
# ./tcpretrans -l
TIME     PID    IP LADDR:LPORT          T> RADDR:RPORT          STATE
01:55:45 0      4  10.153.223.157:22    R> 69.53.245.40:51601   ESTABLISHED
01:55:46 0      4  10.153.223.157:22    R> 69.53.245.40:51601   ESTABLISHED
01:55:46 0      4  10.153.223.157:22    R> 69.53.245.40:51601   ESTABLISHED
01:55:53 0      4  10.153.223.157:22    L> 69.53.245.40:46444   ESTABLISHED
01:56:06 0      4  10.153.223.157:22    R> 69.53.245.40:46444   ESTABLISHED

T 列 中的“L”这些都是尝试:内核可能发送了一个TLP,但在某些情况下它可能最终没有被发送。

  • L>: 表示数据包是从本地地址(LADDR)发送到远程地址(RADDR)的。
  • R>: 表示数据包是从远程地址(RADDR)发送到本地地址(LADDR)的。

要快速发现重传流,可以使用-c标志。它将计算每个流中发生的重传次数。

代码语言:javascript复制
# ./tcpretrans.py -c
Tracing retransmits ... Hit Ctrl-C to end
^C
LADDR:LPORT              RADDR:RPORT             RETRANSMITS
192.168.10.50:60366  <-> 172.217.21.194:443         700
192.168.10.50:666    <-> 172.213.11.195:443         345
192.168.10.50:366    <-> 172.212.22.194:443         211

其他的一些操作

代码语言:javascript复制
optional arguments:
  -h, --help       show this help message and exit
  -s, --sequence   display TCP sequence numbers
  -l, --lossprobe  include tail loss probe attempts
  -c, --count      count occurred retransmits per flow
  -4, --ipv4       trace IPv4 family only
  -6, --ipv6       trace IPv6 family only

examples:
    ./tcpretrans           # trace TCP retransmits
    ./tcpretrans -l        # include TLP attempts
    ./tcpretrans -4        # trace IPv4 family only
    ./tcpretrans -6        # trace IPv6 family only

tcptracer:已建立的 TCP 连接跟踪

tcptracer 工具追踪内核中与 TCP 连接建立(如通过 connect()或 accept()系统调用)和关闭(显式关闭或进程死亡时)相关的函数。同样该工具使用 eBPF 功能,因此开销非常低。

代码语言:javascript复制
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcptracer   #/usr/share/bcc/tools/tcptracer
Tracing TCP established connections. Ctrl-C to end.
T  PID    COMM             IP SADDR            DADDR            SPORT  DPORT
C  28943  telnet           4  192.168.1.2      192.168.1.1      59306  23
C  28818  curl             6  [::1]            [::1]            55758  80
X  28943  telnet           4  192.168.1.2      192.168.1.1      59306  23
A  28817  nc               6  [::1]            [::1]            80     55758
X  28818  curl             6  [::1]            [::1]            55758  80
X  28817  nc               6  [::1]            [::1]            80     55758
A  28978  nc               4  10.202.210.1     10.202.109.12    8080   59160
X  28978  nc               4  10.202.210.1     10.202.109.12    8080   59160

关于事件类型的解释:

  • C 表示连接(Connect):这通常表示一个 TCP 连接请求已经发送或接收。
  • X 表示关闭(Close):这表示 TCP 连接已经关闭,可能是由于正常关闭(如通过 FIN/ACK 握手)或由于某种错误导致的异常关闭。
  • A 表示接受(Accept):这通常表示服务器已经接受了一个来自客户端的连接请求,并创建了一个新的连接。然而,

每当内核连接、接受或关闭连接时,tcptracer 都会显示连接的详情。

通过可以基于 Cgroup 来进行过滤,同时提供接基于 网络命名空间的过滤

代码语言:javascript复制
[root@liruilongs ~]# /usr/share/bcc/tools/tcptracer -h
usage: tcptracer [-h] [-t] [-p PID] [-N NETNS] [--cgroupmap CGROUPMAP] [--mntnsmap MNTNSMAP] [-4 | -6] [-v]

Trace TCP connections

optional arguments:
  -h, --help            show this help message and exit
  -t, --timestamp       include timestamp on output
  -p PID, --pid PID     trace this PID only
  -N NETNS, --netns NETNS
                        trace this Network Namespace only
  --cgroupmap CGROUPMAP
                        trace cgroups in this BPF map only
  --mntnsmap MNTNSMAP   trace mount namespaces in this BPF map only
  -4, --ipv4            trace IPv4 family only
  -6, --ipv6            trace IPv6 family only
  -v, --verbose         include Network Namespace in the output
[root@liruilongs ~]#

tcpconnlat:测量出站 TCP 连接的延迟

TCP 连接延迟是建立连接所需的时间。这通常涉及内核 TCP/IP 处理和网络往返时间,而不是应用程序运行时。

tcpconnlat 工具使用 eBPF 特性来测量发送 SYN 数据包和接收响应数据包之间的时间。

LAT(ms) 列为延迟时间

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$tcpconnlat #/usr/share/bcc/tools/tcpconnlat
PID     COMM         IP SADDR            DADDR            DPORT LAT(ms)
1701    barad_agent  4  10.0.16.15       169.254.0.4      80    9.68
1701    barad_agent  4  10.0.16.15       169.254.0.4      80    8.20
1701    barad_agent  4  10.0.16.15       169.254.0.4      80    9.29
1701    barad_agent  4  10.0.16.15       169.254.0.4      80    7.52
1701    barad_agent  4  10.0.16.15       169.254.0.4      80    7.68
^C┌──[root@liruilongs.github.io]-[~]
└─$

也可以根据延迟时间,pid 来进行过滤,其他的 Demo

代码语言:javascript复制
examples:
    ./tcpconnlat           # trace all TCP connect()s
    ./tcpconnlat 1         # trace connection latency slower than 1 ms
    ./tcpconnlat 0.1       # trace connection latency slower than 100 us
    ./tcpconnlat -t        # include timestamps
    ./tcpconnlat -p 181    # only trace PID 181
    ./tcpconnlat -L        # include LPORT while printing outputs
    ./tcpconnlat -4        # trace IPv4 family only
    ./tcpconnlat -6        # trace IPv6 family only

tcpdrop:被内核丢弃的 TCP 数据包跟踪

tcpdrop 工具使管理员能够显示内核所丢弃的 TCP 数据包和段的详情.tcpdrop 工具使用 eBPF 特性,而不是捕获和过滤资源密集型的数据包,来直接从内核检索信息。

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$$tcpdrop   #/usr/share/bcc/tools/tcpdrop
TIME     PID    IP SADDR:SPORT       > DADDR:DPORT   STATE (FLAGS)
13:28:39 32253  4  192.0.2.85:51616  > 192.0.2.1:22  CLOSE_WAIT (FIN|ACK)
 b'tcp_drop 0x1'
 b'tcp_data_queue 0x2b9'
 ...

13:28:39 1      4  192.0.2.85:51616  > 192.0.2.1:22   CLOSE (ACK)
 b'tcp_drop 0x1'
 b'tcp_rcv_state_process 0xe2'
 ...

每次内核丢弃 TCP 数据包和段时,tcpdrop 都会显示连接的详情,包括导致软件包丢弃的内核堆栈追踪

STATE (FLAGS):TCP 连接的状态和相关的 TCP 标志:

  • CLOSE_WAIT (FIN|ACK)

CLOSE_WAIT状态通常意味着本地应用程序已经接收了关闭连接的 FIN 包,但还没有发送它自己的 FIN 包来关闭连接。如果连接长时间处于 CLOSE_WAIT 状态,可能是因为应用程序有 bug 或者没有正确处理关闭的连接。FIN|ACK 标志表示这个数据包是一个带有 FIN 和 ACK 标志的 TCP 段。这通常是在关闭连接的过程中发送的。

  • CLOSE (ACK)

CLOSE状态表示连接正在关闭,但还没有完全关闭。这通常是正常的,因为 TCP 关闭是一个四次握手的协议,需要双方交换多个数据包来确保连接被正确关闭。ACK标志表示这个数据包是一个TCP确认包,用于确认之前接收到的数据包。

代码语言:javascript复制
examples:
    ./tcpdrop           # trace kernel TCP drops
    ./tcpdrop -4        # trace IPv4 family only
    ./tcpdrop -6        # trace IPv6 family only

tcplife: TCP 会话追踪

tcplife 工具使用 eBPF 跟踪打开和关闭的 TCP 会话,并打印一行输出来总结每一个会话。管理员可以使用 tcplife 来识别连接和传输的流量数

可以显示到端口 22 (SSH)的连接来检索以下信息:

  • 本地进程 ID(PID)
  • 本地进程名称
  • 本地 IP 地址和端口号
  • 远程 IP 地址和端口号
  • 接收和传输的流量的数量(以 KB 为单位)。
  • 连接处于活跃状态的时间(毫秒)
代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/tcplife -L 22
PID   COMM    LADDR      LPORT RADDR       RPORT TX_KB  RX_KB      MS
19392 sshd    192.0.2.1  22    192.0.2.17  43892    53     52 6681.95
19431 sshd    192.0.2.1  22    192.0.2.245 43902    81 249381 7585.09
19487 sshd    192.0.2.1  22    192.0.2.121 43970  6998     7 16740.35

列宽调整

代码语言:javascript复制
# ./tcplife -w
PID   COMM             IP LADDR                      LPORT RADDR                      RPORT  TX_KB  RX_KB MS
26315 recordProgramSt  4  127.0.0.1                  44188 127.0.0.1                  28527      0      0 0.21
3277  redis-server     4  127.0.0.1                  28527 127.0.0.1                  44188      0      0 0.26
26320 ssh              6  fe80::8a3:9dff:fed5:6b19   22440 fe80::8a3:9dff:fed5:6b19   22         1      1 457.52
26321 sshd             6  fe80::8a3:9dff:fed5:6b19   22    fe80::8a3:9dff:fed5:6b19   22440      1      1 458.69
26341 recordProgramSt  4  127.0.0.1                  44192 127.0.0.1                  28527      0      0 0.27
3277  redis-server     4  127.0.0.1                  28527 127.0.0.1                  44192      0      0 0.32

其他的 Demo

代码语言:javascript复制
examples:
    ./tcplife           # trace all TCP connect()s
    ./tcplife -t        # include time column (HH:MM:SS)
    ./tcplife -w        # wider columns (fit IPv6)
    ./tcplife -stT      # csv output, with times & timestamps
    ./tcplife -p 181    # only trace PID 181
    ./tcplife -L 80     # only trace local port 80
    ./tcplife -L 80,81  # only trace local ports 80 and 81
    ./tcplife -D 80     # only trace remote port 80
    ./tcplife -4        # only trace IPv4 family
    ./tcplife -6        # only trace IPv6 family

tcpstates:显示 TCP 状态更改信息

在 TCP 会话中,TCP 状态会改变。tcpstates 工具使用 eBPF 功能跟踪这些状态变化,并打印包括每个状态持续时间的详细信息。例如,使用 tcpstates 来确定连接是否在初始化状态中花费了太多时间。

如果多个连接同时改变了其状态,使用第一列中的套接字地址(SKADDR)来确定哪些条目属于同一个连接

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$tcpstates  #/usr/share/bcc/tools/tcpstates
SKADDR           C-PID C-COMM     LADDR           LPORT RADDR           RPORT OLDSTATE    -> NEWSTATE    MS
ffff9fd7e8192000 22384 curl       100.66.100.185  0     52.33.159.26    80    CLOSE       -> SYN_SENT    0.000
ffff9fd7e8192000 0     swapper/5  100.66.100.185  63446 52.33.159.26    80    SYN_SENT    -> ESTABLISHED 1.373
ffff9fd7e8192000 22384 curl       100.66.100.185  63446 52.33.159.26    80    ESTABLISHED -> FIN_WAIT1   176.042
ffff9fd7e8192000 0     swapper/5  100.66.100.185  63446 52.33.159.26    80    FIN_WAIT1   -> FIN_WAIT2   0.536
ffff9fd7e8192000 0     swapper/5  100.66.100.185  63446 52.33.159.26    80    FIN_WAIT2   -> CLOSE       0.006
^C

每次连接改变其状态时,tcpstates 都会显示一个新行,其中包含更新的连接详情。

代码语言:javascript复制
OLDSTATE    -> NEWSTATE    MS
CLOSE       -> SYN_SENT    0.000
SYN_SENT    -> ESTABLISHED 1.373
ESTABLISHED -> FIN_WAIT1   176.042
FIN_WAIT1   -> FIN_WAIT2   0.536
FIN_WAIT2   -> CLOSE       0.006

其他的一些 Demo

代码语言:javascript复制
examples:
    ./tcpstates           # trace all TCP state changes
    ./tcpstates -t        # include timestamp column
    ./tcpstates -T        # include time column (HH:MM:SS)
    ./tcpstates -w        # wider columns (fit IPv6)
    ./tcpstates -stT      # csv output, with times & timestamps
    ./tcpstates -Y        # log events to the systemd journal
    ./tcpstates -L 80     # only trace local port 80
    ./tcpstates -L 80,81  # only trace local ports 80 and 81
    ./tcpstates -D 80     # only trace remote port 80
    ./tcpstates -4        # trace IPv4 family only
    ./tcpstates -6        # trace IPv6 family only

tcpsubnet:统计发送到特定子网的 TCP 流量

tcpsubnet 工具汇总并合计了本地主机发往子网的 IPv4 TCP 流量,并按固定间隔显示输出。该工具使用 eBPF 功能来收集并总结数据,以减少开销。

默认情况下,tcpsubnet 为以下子网汇总流量:

  • 127.0.0.1/32
  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.0.2.0/24/16
  • 0.0.0.0/0

最后一个子网(0.0.0.0/0)是一个全包括选项。tcpsubnet 工具计算与这个全包括条目中前四个不同的子网的所有流量。按照以下流程计算 192.0.2.0/24198.51.100.0/24 子网的流量。到其他子网的流量将在 0.0.0.0/0 全包括子网条目中跟踪。

开始监控发送到 192.0.2.0/24、198.51.100.0/24 以及其他子网的流量数,需注意 0.0.0.0/0 要放到最后面

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/tcpsubnet 192.0.2.0/24,198.51.100.0/24,0.0.0.0/0
Tracing... Output every 1 secs. Hit Ctrl-C to end
[02/21/20 10:04:50]
192.0.2.0/24           856
198.51.100.0/24       7467
[02/21/20 10:04:51]
192.0.2.0/24          1200
198.51.100.0/24       8763
0.0.0.0/0              673
...

以字节为单位显示指定子网每秒一次的流量。其他的输出格式

代码语言:javascript复制
# tcpsubnet -J -fK 192.130.253.110/27,0.0.0.0/0
{"date": "03/05/18", "entries": {"0.0.0.0/0": 2}, "time": "22:46:27"}
{"date": "03/05/18", "entries": {}, "time": "22:46:28"}
{"date": "03/05/18", "entries": {}, "time": "22:46:29"}
{"date": "03/05/18", "entries": {}, "time": "22:46:30"}
{"date": "03/05/18", "entries": {"192.30.253.110/27": 0}, "time": "22:46:31"}
{"date": "03/05/18", "entries": {"192.30.253.110/27": 1}, "time": "22:46:32"}
{"date": "03/05/18", "entries": {"192.30.253.110/27": 18}, "time": "22:46:32"}

其他的 Example

代码语言:javascript复制
examples:
    ./tcpsubnet                 # Trace TCP sent to the default subnets:
                                # 127.0.0.1/32,10.0.0.0/8,172.16.0.0/12,
                                # 192.168.0.0/16,0.0.0.0/0
    ./tcpsubnet -f K            # Trace TCP sent to the default subnets
                                # aggregated in KBytes.
    ./tcpsubnet 10.80.0.0/24    # Trace TCP sent to 10.80.0.0/24 only
    ./tcpsubnet -J              # Format the output in JSON.

tcptop:跟踪 IP 端口的网络吞吐量

tcptop 工具以 KB 为单位显示主机发送并接收的 TCP 流量。这个报告会自动刷新并只包含活跃的 TCP 连接。该工具使用 eBPF 功能,因此开销非常低。

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/tcptop
13:46:29 loadavg: 0.10 0.03 0.01 1/215 3875

PID    COMM         LADDR           RADDR              RX_KB   TX_KB
3853   3853         192.0.2.1:22    192.0.2.165:41838  32     102626
1285   sshd         192.0.2.1:22    192.0.2.45:39240   0           0
...

对于没有捕获到进程名的连接,会使用 PID 代替进程,说明他的生命周期很短

命令的输出只包括活跃的 TCP 连接。如果本地或者远程系统关闭了连接,则该连接在输出中不再可见。

不清除屏幕打印

代码语言:javascript复制
[root@liruilongs ~]# /usr/share/bcc/tools/tcptop -C
Tracing... Output every 1 secs. Hit Ctrl-C to end

15:52:52 loadavg: 0.03 0.07 0.02 1/708 3057186

PID     COMM         LADDR                 RADDR                  RX_KB  TX_KB
1701    b'barad_agen 10.0.16.15:51796      169.254.0.4:80             0      0
362732  b'tat_agent' 10.0.16.15:52488      169.254.0.138:8186         0      0

15:52:53 loadavg: 0.03 0.07 0.02 5/708 3057187

PID     COMM         LADDR                 RADDR                  RX_KB  TX_KB
362732  b'tat_agent' 10.0.16.15:52488      169.254.0.138:8186         0      0

15:52:54 loadavg: 0.03 0.07 0.02 4/710 3057191

PID     COMM         LADDR                 RADDR                  RX_KB  TX_KB
362732  b'tat_agent' 10.0.16.15:52488      169.254.0.138:8186         0      0

15:52:55 loadavg: 0.03 0.07 0.02 4/709 3057196

PID     COMM         LADDR                 RADDR                  RX_KB  TX_KB
1701    b'barad_agen 10.0.16.15:59568      169.254.0.4:80             0      0
362732  b'tat_agent' 10.0.16.15:52488      169.254.0.138:8186         0      0
^C
15:52:56 loadavg: 0.03 0.07 0.02 3/708 3057199

PID     COMM         LADDR                 RADDR                  RX_KB  TX_KB
362732  b'tat_agent' 10.0.16.15:52488      169.254.0.138:8186         0      0
[root@liruilongs ~]#

其他的 Example

代码语言:javascript复制
examples:
    ./tcptop           # trace TCP send/recv by host
    ./tcptop -C        # don't clear the screen
    ./tcptop -p 181    # only trace PID 181
    ./tcptop --cgroupmap ./mappath  # only trace cgroups in this BPF map
    ./tcptop --mntnsmap mappath   # only trace mount namespaces in the map
    ./tcptop -4        # trace IPv4 family only
    ./tcptop -6        # trace IPv6 family only

solisten:追踪 IPv4 和 IPv6 侦听尝试

solisten 工具追踪所有 IPv4 和 IPv6 侦听尝试。它跟踪监听尝试,包括最终失败或者不接受连接的监听程序。当程序要侦听 TCP 连接时,程序会追踪内核调用的功能。

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/solisten
PID    COMM           PROTO         BACKLOG     PORT     ADDR
3643   nc             TCPv4         1           4242     0.0.0.0
3659   nc             TCPv6         1           4242     2001:db8:1::1
4221   redis-server   TCPv6         128         6379     ::
4221   redis-server   TCPv4         128         6379     0.0.0.0

PID:进程ID,表示哪个进程正在使用该套接字。COMM:与该套接字关联的进程名。PROTO:套接字使用的协议,这里是 TCPv4(IPv4 上的 TCP)或 TCPv6(IPv6 上的 TCP)。BACKLOG:套接字监听队列的长度。当客户端尝试连接到服务器时,如果服务器忙于处理其他连接,那么客户端的连接请求将被放入这个队列中等待。PORT:套接字正在监听的端口号。ADDR:套接字绑定的地址。0.0.0.0 表示该套接字正在监听所有可用的 IPv4 地址,而 :: 表示它正在监听所有可用的 IPv6 地址。对于 2001:db8:1::1,这是一个具体的 IPv6 地址。

其他的 Example

代码语言:javascript复制
Examples:
    ./solisten.py              # Stream socket listen
    ./solisten.py -p 1234      # Stream socket listen for specified PID only
    ./solisten.py --netns 4242 # " for the specified network namespace ID only
    ./solisten.py --show-netns # Show network ns ID (useful for containers)

softirqs:软中断的服务时间统计

softirqs 工具总结了服务软中断(soft IRQ)所花费的时间,并将这个时间显示为总计或直方图分布。这个工具使用 irq:softirq_enterirq:softirq_exit 内核追踪点,是一个稳定的追踪机制。

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/softirqs
Tracing soft irq event time... Hit Ctrl-C to end.
^C
SOFTIRQ          TOTAL_usecs
tasklet                  166
block                   9152
net_rx                 12829
rcu                    53140
sched                 182360
timer                 306256

netqtop:统计网卡上数据包大小和计数

netqtop 工具显示有关特定网络接口的每个网络队列上收到的(RX)和传输的(TX)数据包属性的统计信息。统计包括:

  • 每秒字节数(BPS)
  • 每秒数据包数(PPS)
  • 平均数据包大小
  • 数据包总数

要生成这些统计数据,netqtop 跟踪执行传输数据包 net_dev_start_xmit 和接收数据包 netif_receive_skb 的事件的内核功能。

代码语言:javascript复制
┌──[root@liruilongs.github.io]-[~]
└─$/usr/share/bcc/tools/netqtop -n enp1s0 -i 2
Fri Jan 31 18:08:55 2023
TX
 QueueID avg_size   [0, 64) [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0       0       0       0       0       0       0
 Total   0       0       0       0       0       0

RX
 QueueID avg_size   [0, 64) [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0       38.0    1       0       0       0       0
 Total   38.0    1       0       0       0       0
-----------------------------------------------------------------------------
Fri Jan 31 18:08:57 2023
TX
 QueueID avg_size   [0, 64) [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0       0       0       0       0       0       0
 Total   0       0       0       0       0       0

RX
 QueueID avg_size   [0, 64) [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0       38.0    1       0       0       0       0
 Total   38.0    1       0       0       0       0

显示 2 秒时间间隔的字节大小范围内的数据包数:如果平均队列大小(avg_size)持续较高,这可能表示网络接口正在遭受拥塞或延迟。

博文部分内容参考

© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知 :)

BCC工具包 帮助文档

https://docs.redhat.com/zh_hans/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_networking/network-tracing-using-the-bpf-compiler-collection_configuring-and-managing-networking

© 2018-2024 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

0 人点赞