我有几台服务器,其中 netstat -s(来自 /proc/net/snmp)返回的失败连接尝试指标大约每秒增加一次,我想诊断这些失败连接的来源。
通过使用此 ipTables 规则(在不同的服务器上):
-A 输出 -p tcp --dport 23 -j 拒绝
我正在阻止传出的 telnet,因此我可以运行这个循环:
当为真时;执行
telnet www.google.co.uk
netstat -s | grep “连接失败”
完成尝试 209.85.203.94...
telnet:无法连接到远程主机:连接被拒绝
52 次连接尝试失败
尝试 209.85.203.94... telnet:无法连接到远程主机:连接被拒绝
53 次连接尝试失败
尝试 209.85.203.94... telnet:无法连接到远程主机:连接被拒绝
54 次连接尝试失败
因此,这证明计数器会随着连接远程套接字的失败尝试而增加。(当然,这并不能证明这是导致增加的唯一原因)。
问题是,我如何才能找到失败的远程地址和端口 (或两者的复数) 的特定组合,以便我可以查看下一步;路由/防火墙问题?
顺便说一句,如果我运行这个:
watch -n1 'ss | grep "\<23\>"'
我希望看到 SYN-SENT 状态的套接字,但没有看到。这是因为我使用了 REJECT 而不是 DROP 吗?谢谢
答案1
让我们尝试用另一种方式(困难的方式)来回答这个问题。阅读内核源代码,看看只有一个地方,这个指标会增加 -tcp_done函数。从代码中我们可以看出,只有处于 SYN_SEND 或 SYN_RECV 状态的连接才会增加。然后我们检查一下,从哪里可以调用 tcp_done。我们可以找到几个地方:
- tcp_重置- 在连接中止时调用(收到带有 rst 标志的回复数据包)。是的,它可以在 SYN_SENT 和 SYN_RECV 状态下发生(理论上也可以在其他状态下发生)。
- tcp_rcv_state_process- 在 TCP_FIN_WAIT1 和 TCP_LAST_ACK 状态下调用,因此度量不会增加 - 这不是我们的情况。
- tcp_v4_错误- 在 SYN_SENT 或 SYN_RECV 的情况下调用。ICMP 处理程序调用的 tcp_v4_error 函数。
- tcp_time_wait- 调用将套接字移至时间等待或 fin-wait-2 状态 - 这不是我们的情况。
- tcp_write_error- 在超时和重发次数超出时从多个地方调用。这也可能是我们的怀疑对象。
现在,打开任意 TCP FSM 图来检查,在什么情况下我们的连接可以处于 SYN_SENT 或 SYN_RECV 状态。
在客户端情况下,它只能是 SYN_SENT 状态,其中 syn 数据包正在传输,并且由于收到拒绝(tcp-rst 或 icmp 错误)或未收到答复而中止连接。
在服务器情况下,它只能是 SYN_RECV 状态(syn 已经收到并且 syn+ack 已经发送),并且由于收到拒绝(syn+ack 在某处被拒绝)或超出答复等待超时(未收到 ack)而中止连接。
现在您知道了此指标更新的原因,并且可以检查系统中可能的更新来源。在现代内核中,有强大的工具可用于在内核级别进行故障排除。从开始这个简短的教程来自布伦丹·格雷格。
答案2
连接中断的一个重要原因似乎是尝试连接到无响应的服务器。请记住,我们认为“连接尝试失败”是指传出连 接。
跑步
ss | awk'$1 ~ /SYN-SENT/{打印$NF}'
10.160.32.211:8312
10.160.33.61:8312
10.160.32.146:8312
10.160.33.216:8312 10.160.34.186:8312
10.160.35.18:8312
10.160.32.157:8312
10.160.33.159:8312
10.160.34.246:8312
显示此状态的许多连接。有趣的是,它指出它们都试图连接到同一端口。如果我尝试从该列表中随机选择 IP 地址并尝试使用 telnet 连接到端口 8312 - 例如:
$ telnet 10.160.34.246 8312
telnet:连接到地址 10.160.32.48:连接超时
发送 SYN 数据包是建立连接的第一步。对方应该用 SYN-ACK 数据包进行响应 - 在这种情况下,我们用 ACK 进行响应,连接就建立了。但是,如果两台服务器之间有防火墙阻止连接,则不会发送 SYN-ACK,因此套接字将保持 SYN_SENT 状态,直到超时。
以下是从 lwn.net 窃取的图表:
这个超时时间并不长(我正在尝试找出多长时间,并将进行适当更新) - 据我所知,到目前为止它大约是几秒钟(我原以为是 2x MSL,其中 MSL 是最大段寿命 - 但这只是猜测)。
现在,我们需要区分发送 SYN 但没有返回任何内容的连接尝试和返回 RST 的连接尝试。防火墙通常会非常粗鲁;它会默默地丢弃原始 SYN 数据包 - 它不会发送 RST,这是让客户端知道这里没有任何东西的正常方式。
通过尝试连接到 www.google.co.uk 上的一个你怀疑他们不会监听的端口,你会看到类似的行为 - 例如:
$ telnet www.google.co.uk 32654
尝试 74.125.203.94... telnet:连接到地址 74.125.203.94:连接超时
同时运行如下操作:
当为真时;执行 ss | awk '/SYN-SENT/ && $NF !~ /^10./' ; 睡眠 2 ; 完成
SYN-SENT 0 1 10.137.6.62:46088 74.125.203.94:32654
SYN-SENT 0 1 10.137.6.62:46088 74.125.203.94:32654 SYN-SENT 0 1
10.137.6.62:46088 74.125.203.94:32654
现在,我在公司网络内,几乎可以肯定,通过普通端口 80/443 访问 Google 是通过代理的,其他任何端口都经过防火墙保护,因此我们不希望看到 RST 数据包。这就是为什么我在问题中询问我的 IPTables 规则中 REJECT 和 DROP 之间的区别。我认为,DROP 只是丢弃 IPTables 中的数据包,而 REJECT 会发送 RST。
我接下来要做的是 tcpdump 与非监听端口的连接,并进行适当的更新。
$ tcpdump -nn -t -i eth0 dst 8.8.8.8
tcpdump:警告:eth0:未分配 IPv4 地址
tcpdump:抑制详细输出,使用 -v 或 -vv 进行完整协议解码
在 eth0 上监听,链路类型 EN10MB(以太网),
捕获大小 65535 字节
IP 10.137.6.62.40822 > 8.8.8.8.12345:标志[S],seq 505811469,win 14600,选项[mss 1460,sackOK,TS val 1513647100 ecr 0,nop,wscale 9],长度 0
IP 10.137.6.62.40822 8.8.8.8.12345:标志[S],seq 505811469,win 14600,选项[mss 1460,sackOK,TS val 1513648100 ecr 0,nop,wscale 9],长度0
IP 10.137.6.62.40822>8.8.8.8.12345:标志[S],序列505811469,win 14600,选项[mss 1460,sackOK,TS val 1513650100 ecr 0,nop,wscale 9],长度0
IP 10.137.6.62.40822>8.8.8.8.12345:标志[S],序列505811469,win 14600,选项[mss 1460,sackOK,TS val 1513654100 ecr 0,nop,wscale 9],长度 0
IP 10.137.6.62.40822 > 8.8.8.8.12345:标志[S],seq 505811469,win 14600,选项[mss 1460,sackOK,TS val 1513662100 ecr 0,nop,wscale 9],长度 0
IP 10.137.6.62.40822 > 8.8.8.8.12345:标志[S],seq 505811469,win 14600,选项[mss 1460,sackOK,TS val 1513678100 ecr 0,nop,wscale 9],长度 0
TODO:添加没有防火墙的情况的 tcpdump,以便我们可以看到 RST 数据包。
警告 关于 Linux TCP 连接调试,有许多有用的信息来源。Red Hat 就是其中之一。在他们的一个页面上,他们建议使用 dropwatch 工具来确定在内核网络堆栈中数据包被丢弃的位置。该页面没有说明的是,从软件堆栈“丢弃”数据包是正常的 - 一旦处理完数据包,就会将其丢弃。dropwatch 工具不会区分由于完成而丢弃的数据包和由于缓冲区溢出、中断预算超时或... 而丢弃的数据包。
买者自负。