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

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

写在前面


  • 博文内容为 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:统计在网络接口上数据包大小和计数
  • 理解不足小伙伴帮忙指正 :),生活加油

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


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

三次握手

1
2
3
4
客户端                       服务器
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状态,连接建立完成。

四次挥手

1
2
3
4
5
6
7
客户端                       服务器
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()调用)时,打印一行信息,包含源地址、目的地址。在输出中应该寻找不寻常的连接请求,它们可能会暴露出软件配置的低效,也可能暴露入侵行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcpconnect #/usr/share/bcc/tools/tcpaccept
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选项打印一个时间戳列:

1
2
3
4
# ./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"

1
2
3
4
5
6
7
8
# ./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 列: 目标端口

1
2
3
4
5
# ./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

1
2
3
4
# ./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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 进行常规故障排除,来显示服务器已接受的新连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──[root@vms100.liruilongs.github.io]-[~]
└─$tcpaccept #/usr/share/bcc/tools/tcpconnect
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 都会显示连接的详情。其他的一些选项

1
2
3
4
5
6
7
8
9
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 都会显示连接的详情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──[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不同,该工具的开销非常低,因为它只跟踪重传函数

1
2
3
4
5
6
7
# ./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标志。它将计算每个流中发生的重传次数。

1
2
3
4
5
6
7
# ./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

其他的一些操作

1
2
3
4
5
6
7
8
9
10
11
12
13
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 功能,因此开销非常低。

1
2
3
4
5
6
7
8
9
10
11
12
┌──[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 来进行过滤,同时提供接基于 网络命名空间的过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[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) 列为延迟时间

1
2
3
4
5
6
7
8
9
10
┌──[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

1
2
3
4
5
6
7
8
9
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 特性,而不是捕获和过滤资源密集型的数据包,来直接从内核检索信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──[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确认包,用于确认之前接收到的数据包。

1
2
3
4
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 为单位)。
  • 连接处于活跃状态的时间(毫秒)
1
2
3
4
5
6
┌──[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

列宽调整

1
2
3
4
5
6
7
8
# ./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

1
2
3
4
5
6
7
8
9
10
11
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)来确定哪些条目属于同一个连接

1
2
3
4
5
6
7
8
9
┌──[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 都会显示一个新行,其中包含更新的连接详情。

1
2
3
4
5
6
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

1
2
3
4
5
6
7
8
9
10
11
12
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 要放到最后面

1
2
3
4
5
6
7
8
9
10
11
┌──[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
...

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

1
2
3
4
5
6
7
8
# 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

1
2
3
4
5
6
7
8
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 功能,因此开销非常低。

1
2
3
4
5
6
7
8
┌──[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 连接。如果本地或者远程系统关闭了连接,则该连接在输出中不再可见。

不清除屏幕打印

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
26
27
28
29
30
[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

1
2
3
4
5
6
7
8
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 连接时,程序会追踪内核调用的功能。

1
2
3
4
5
6
7
┌──[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

1
2
3
4
5
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 内核追踪点,是一个稳定的追踪机制。

1
2
3
4
5
6
7
8
9
10
11
┌──[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 的事件的内核功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──[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-至今 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

发布于

2024-05-18

更新于

2024-11-22

许可协议

评论
加载中,最新评论有1分钟缓存...
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×