在Linux中模拟网络问题

大家好,我叫 Sasha,我在 FunCorp 负责后端测试。 与许多其他人一样,我们已经实现了面向服务的架构。 一方面,这简化了工作,因为...... 单独测试每个服务更容易,但另一方面,需要测试服务之间的交互,这通常发生在网络上。

在本文中,我将讨论两个实用程序,可用于检查描述存在网络问题时应用程序操作的基本场景。

在Linux中模拟网络问题

模拟网络问题

通常,软件在具有良好互联网连接的测试服务器上进行测试。 在恶劣的生产环境中,事情可能不会那么顺利,所以有时你需要在连接条件较差的情况下测试程序。 在 Linux 上,该实用程序将帮助完成模拟此类条件的任务 tc.

TC(缩写。 来自交通管制)允许您配置系统中网络数据包的传输。 该实用程序具有强大的功能,您可以阅读有关它们的更多信息 这里。 在这里我只考虑其中的几个:我们对流量调度感兴趣,为此我们使用 队列盘,并且由于我们需要模拟不稳定的网络,因此我们将使用无类 qdisc 网络.

让我们在服务器上启动一个 echo 服务器(我用过 nmap-ncat):

ncat -l 127.0.0.1 12345 -k -c 'xargs -n1 -i echo "Response: {}"'

为了详细显示客户端和服务器交互的每一步的所有时间戳,我编写了一个简单的Python脚本来发送请求 测试 到我们的回显服务器。

客户端源码

#!/bin/python

import socket
import time

HOST = '127.0.0.1'
PORT = 12345
BUFFER_SIZE = 1024
MESSAGE = "Testn"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
t1 = time.time()
print "[time before connection: %.5f]" % t1
s.connect((HOST, PORT))
print "[time after connection, before sending: %.5f]" % time.time()
s.send(MESSAGE)
print "[time after sending, before receiving: %.5f]" % time.time()
data = s.recv(BUFFER_SIZE)
print "[time after receiving, before closing: %.5f]" % time.time()
s.close()
t2 = time.time()
print "[time after closing: %.5f]" % t2
print "[total duration: %.5f]" % (t2 - t1)

print data

让我们启动它并查看界面上的流量 lo 和端口 12345:

[user@host ~]# python client.py
[time before connection: 1578652979.44837]
[time after connection, before sending: 1578652979.44889]
[time after sending, before receiving: 1578652979.44894]
[time after receiving, before closing: 1578652979.45922]
[time after closing: 1578652979.45928]
[total duration: 0.01091]
Response: Test

交通转储

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:42:59.448601 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [S], seq 3383332866, win 43690, options [mss 65495,sackOK,TS val 606325685 ecr 0,nop,wscale 7], length 0
10:42:59.448612 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [S.], seq 2584700178, ack 3383332867, win 43690, options [mss 65495,sackOK,TS val 606325685 ecr 606325685,nop,wscale 7], length 0
10:42:59.448622 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 0
10:42:59.448923 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 5
10:42:59.448930 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [.], ack 6, win 342, options [nop,nop,TS val 606325685 ecr 606325685], length 0
10:42:59.459118 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 606325696 ecr 606325685], length 14
10:42:59.459213 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 606325696 ecr 606325696], length 0
10:42:59.459268 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 606325696 ecr 606325696], length 0
10:42:59.460184 IP 127.0.0.1.12345 > 127.0.0.1.54054: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 606325697 ecr 606325696], length 0
10:42:59.460196 IP 127.0.0.1.54054 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 606325697 ecr 606325697], length 0

一切都是标准的:三次握手,两次 PSH/ACK 和 ACK 响应——这是客户端和服务器之间请求和响应的交换,两次 FIN/ACK 和 ACK——完成连接。

数据包延迟

现在让我们将延迟设置为 500 毫秒:

tc qdisc add dev lo root netem delay 500ms

我们启动客户端并看到脚本现在运行了 2 秒:

[user@host ~]# ./client.py
[time before connection: 1578662612.71044]
[time after connection, before sending: 1578662613.71059]
[time after sending, before receiving: 1578662613.71065]
[time after receiving, before closing: 1578662614.72011]
[time after closing: 1578662614.72019]
[total duration: 2.00974]
Response: Test

交通中有什么? 我们看看吧:

交通转储

13:23:33.210520 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [S], seq 1720950927, win 43690, options [mss 65495,sackOK,TS val 615958947 ecr 0,nop,wscale 7], length 0
13:23:33.710554 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [S.], seq 1801168125, ack 1720950928, win 43690, options [mss 65495,sackOK,TS val 615959447 ecr 615958947,nop,wscale 7], length 0
13:23:34.210590 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 615959947 ecr 615959447], length 0
13:23:34.210657 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 615959947 ecr 615959447], length 5
13:23:34.710680 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [.], ack 6, win 342, options [nop,nop,TS val 615960447 ecr 615959947], length 0
13:23:34.719371 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 615960456 ecr 615959947], length 14
13:23:35.220106 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 615960957 ecr 615960456], length 0
13:23:35.220188 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 615960957 ecr 615960456], length 0
13:23:35.720994 IP 127.0.0.1.12345 > 127.0.0.1.58694: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 615961457 ecr 615960957], length 0
13:23:36.221025 IP 127.0.0.1.58694 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 615961957 ecr 615961457], length 0

可以看到客户端和服务端的交互出现了预期的半秒延迟。 如果滞后更大,系统的行为会更加有趣:内核开始重新发送一些 TCP 数据包。 让我们将延迟更改为 1 秒并查看流量(我不会显示客户端的输出,预计总持续时间为 4 秒):

tc qdisc change dev lo root netem delay 1s

交通转储

13:29:07.709981 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [S], seq 283338334, win 43690, options [mss 65495,sackOK,TS val 616292946 ecr 0,nop,wscale 7], length 0
13:29:08.710018 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [S.], seq 3514208179, ack 283338335, win 43690, options [mss 65495,sackOK,TS val 616293946 ecr 616292946,nop,wscale 7], length 0
13:29:08.711094 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [S], seq 283338334, win 43690, options [mss 65495,sackOK,TS val 616293948 ecr 0,nop,wscale 7], length 0
13:29:09.710048 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 616294946 ecr 616293946], length 0
13:29:09.710152 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 616294947 ecr 616293946], length 5
13:29:09.711120 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [S.], seq 3514208179, ack 283338335, win 43690, options [mss 65495,sackOK,TS val 616294948 ecr 616292946,nop,wscale 7], length 0
13:29:10.710173 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [.], ack 6, win 342, options [nop,nop,TS val 616295947 ecr 616294947], length 0
13:29:10.711140 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 616295948 ecr 616293946], length 0
13:29:10.714782 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 616295951 ecr 616294947], length 14
13:29:11.714819 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 15, win 342, options [nop,nop,TS val 616296951 ecr 616295951], length 0
13:29:11.714893 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 616296951 ecr 616295951], length 0
13:29:12.715562 IP 127.0.0.1.12345 > 127.0.0.1.39306: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 616297952 ecr 616296951], length 0
13:29:13.715596 IP 127.0.0.1.39306 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 616298952 ecr 616297952], length 0

可以看到客户端发送了两次SYN包,服务器发送了两次SYN/ACK。

除了常数值之外,延迟还可以设置为偏差、分布函数和相关性(与前一个数据包的值)。 这是按如下方式完成的:

tc qdisc change dev lo root netem delay 500ms 400ms 50 distribution normal

这里我们将延迟设置在 100 到 900 毫秒之间,这些值将根据正态分布进行选择,并且与前一个数据包的延迟值有 50% 的相关性。

您可能已经注意到,在我使用的第一个命令中 然后 更改。 这些命令的含义很明显,我就补充一下,还有更多 ,可用于删除配置。

数据包丢失

现在让我们尝试处理丢包问题。 从文档中可以看出,这可以通过三种方式完成:以一定概率随机丢包,使用 2、3 或 4 状态的马尔可夫链来计算丢包,或者使用 Elliott-Gilbert 模型。 在本文中,我将考虑第一种(最简单且最明显的)方法,您可以阅读其他方法 这里.

让我们假设丢失 50% 的数据包,相关性为 25%:

tc qdisc add dev lo root netem loss 50% 25%

不幸的是, 转储 将无法清楚地向我们显示数据包丢失情况,我们只能假设它确实有效。 而脚本运行时间的增加和不稳定将帮助我们验证这一点。 客户端.py (可以立即完成,也可能在 20 秒内完成),以及增加重传数据包的数量:

[user@host ~]# netstat -s | grep retransmited; sleep 10; netstat -s | grep retransmited
    17147 segments retransmited
    17185 segments retransmited

向数据包添加噪音

除了数据包丢失之外,您还可以模拟数据包损坏:随机数据包位置会出现噪声。 让我们以 50% 的概率进行数据包损坏,并且没有相关性:

tc qdisc change dev lo root netem corrupt 50%

我们运行客户端脚本(没有什么有趣的,但花了 2 秒才能完成),查看流量:

交通转储

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:20:54.812434 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [S], seq 2023663770, win 43690, options [mss 65495,sackOK,TS val 1037001049 ecr 0,nop,wscale 7], length 0
10:20:54.812449 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [S.], seq 2104268044, ack 2023663771, win 43690, options [mss 65495,sackOK,TS val 1037001049 ecr 1037001049,nop,wscale 7], length 0
10:20:54.812458 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1037001049 ecr 1037001049], length 0
10:20:54.812509 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1037001049 ecr 1037001049], length 5
10:20:55.013093 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1037001250 ecr 1037001049], length 5
10:20:55.013122 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [.], ack 6, win 342, options [nop,nop,TS val 1037001250 ecr 1037001250], length 0
10:20:55.014681 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [P.], seq 1:15, ack 6, win 342, options [nop,nop,TS val 1037001251 ecr 1037001250], length 14
10:20:55.014745 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 15, win 340, options [nop,nop,TS val 1037001251 ecr 1037001251], length 0
10:20:55.014823 IP 127.0.0.1.43666 > 127.0.0.5.12345: Flags [F.], seq 2023663776, ack 2104268059, win 342, options [nop,nop,TS val 1037001251 ecr 1037001251], length 0
10:20:55.214088 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [P.], seq 1:15, ack 6, win 342, options [nop,unknown-65 0x0a3dcf62eb3d,[bad opt]>
10:20:55.416087 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [F.], seq 6, ack 15, win 342, options [nop,nop,TS val 1037001653 ecr 1037001251], length 0
10:20:55.416804 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 1037001653 ecr 1037001653], length 0
10:20:55.416818 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 343, options [nop,nop,TS val 1037001653 ecr 1037001653], length 0
10:20:56.147086 IP 127.0.0.1.12345 > 127.0.0.1.43666: Flags [F.], seq 15, ack 7, win 342, options [nop,nop,TS val 1037002384 ecr 1037001653], length 0
10:20:56.147101 IP 127.0.0.1.43666 > 127.0.0.1.12345: Flags [.], ack 16, win 342, options [nop,nop,TS val 1037002384 ecr 1037001653], length 0

可以看到,有些数据包被重复发送,并且有一个数据包元数据损坏: 选项 [nop,未知-65 0x0a3dcf62eb3d,[错误选择]>。 但最重要的是,最终一切正常 - TCP 完成了它的任务。

数据包重复

你还能做什么 网络? 例如,模拟丢包的逆向情况——数据包重复。 该命令还采用 2 个参数:概率和相关性。

tc qdisc change dev lo root netem duplicate 50% 25%

更改包的顺序

您可以通过两种方式混合袋子。

第一种情况下,一些数据包会立即发送,其余数据包会延迟指定的时间。 文档中的示例:

tc qdisc change dev lo root netem delay 10ms reorder 25% 50%

数据包有 25% 的概率(相关性为 50%)立即发送,其余数据包将延迟 10 毫秒发送。

第二种方法是每第 N 个数据包以给定的概率(和相关性)立即发送,其余数据包以给定的延迟发送。 文档中的示例:

tc qdisc change dev lo root netem delay 10ms reorder 25% 50% gap 5

每五个包裹中有 25% 的机会立即发送。

改变带宽

通常他们所指的任何地方 待定,但在帮助下 网络 您还可以更改接口带宽:

tc qdisc change dev lo root netem rate 56kbit

这支队伍将长途跋涉 本地 就像通过拨号调制解调器上网一样痛苦。 除了设置比特率之外,您还可以模拟链路层协议模型:设置数据包的开销、单元大小和单元的开销。 例如可以这样模拟 标准大气压 比特率 56 kbit/秒:

tc qdisc change dev lo root netem rate 56kbit 0 48 5

模拟连接超时

接受软件时测试计划中的另一个重要点是超时。 这很重要,因为在分布式系统中,当其中一个服务被禁用时,其他服务必须及时回退到其他服务或向客户端返回错误,并且在任何情况下都不应该简单地挂起,等待响应或连接待成立。

有几种方法可以做到这一点:例如,使用不响应的模拟,或者使用调试器连接到进程,在正确的位置放置断点并停止进程(这可能是最变态的方法)。 但最明显的之一是防火墙端口或主机。 它将帮助我们解决这个问题 iptables的.

为了进行演示,我们将防火墙端口 12345 并运行我们的客户端脚本。 您可以在发送方对传出此端口的数据包进行防火墙,或在接收方对传入数据包进行防火墙。 在我的示例中,传入数据包将受到防火墙保护(我们使用链 INPUT 和选项 --d端口)。 此类数据包可以被 DROP、REJECT 或带有 TCP 标志 RST 的 REJECT,或者 ICMP 主机不可达(事实上,默认行为是 icmp 端口不可达,还有机会发送回复 icmp 网络不可达, icmp 协议不可达, icmp 网络禁止 и icmp 主机禁止).

下降

如果有 DROP 规则,数据包将简单地“消失”。

iptables -A INPUT -p tcp --dport 12345 -j DROP

我们启动客户端,发现它在连接到服务器的阶段冻结了。 我们来看看流量:
交通转储

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
08:28:20.213506 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203046450 ecr 0,nop,wscale 7], length 0
08:28:21.215086 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203047452 ecr 0,nop,wscale 7], length 0
08:28:23.219092 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203049456 ecr 0,nop,wscale 7], length 0
08:28:27.227087 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203053464 ecr 0,nop,wscale 7], length 0
08:28:35.235102 IP 127.0.0.1.32856 > 127.0.0.1.12345: Flags [S], seq 3019694933, win 43690, options [mss 65495,sackOK,TS val 1203061472 ecr 0,nop,wscale 7], length 0

可以看到,客户端发送SYN包的超时时间呈指数增长。 所以我们发现客户端有一个小bug:需要使用方法 设置超时()限制客户端尝试连接服务器的时间。

我们立即删除该规则:

iptables -D INPUT -p tcp --dport 12345 -j DROP

您可以一次删除所有规则:

iptables -F

如果您使用 Docker 并且需要对进入容器的所有流量进行防火墙,那么您可以按如下方式执行操作:

iptables -I DOCKER-USER -p tcp -d CONTAINER_IP -j DROP

拒绝

现在让我们添加一个类似的规则,但使用 REJECT:

iptables -A INPUT -p tcp --dport 12345 -j REJECT

客户端一秒后退出并出现错误 [Errno 111] 连接被拒绝。 让我们看一下 ICMP 流量:

[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
08:45:32.871414 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp port 12345 unreachable, length 68
08:45:33.873097 IP 127.0.0.1 > 127.0.0.1: ICMP 127.0.0.1 tcp port 12345 unreachable, length 68

可以看到客户端收到了两次 端口不可达 然后以错误结束。

使用 tcp-reset 拒绝

让我们尝试添加该选项 --reject-with tcp-reset:

iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with tcp-reset

在这种情况下,客户端立即错误退出,因为第一个请求收到了 RST 数据包:

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
09:02:52.766175 IP 127.0.0.1.60658 > 127.0.0.1.12345: Flags [S], seq 1889460883, win 43690, options [mss 65495,sackOK,TS val 1205119003 ecr 0,nop,wscale 7], length 0
09:02:52.766184 IP 127.0.0.1.12345 > 127.0.0.1.60658: Flags [R.], seq 0, ack 1889460884, win 0, length 0

使用 icmp-host-unreachable 拒绝

让我们尝试使用 REJECT 的另一个选项:

iptables -A INPUT -p tcp --dport 12345 -j REJECT --reject-with icmp-host-unreachable

客户端一秒后退出并出现错误 [Errno 113] 没有到主机的路由,我们在 ICMP 流量中看到 ICMP 主机 127.0.0.1 无法访问.

您还可以尝试其他 REJECT 参数,我将重点介绍这些:)

模拟请求超时

另一种情况是客户端能够连接到服务器,但无法向服务器发送请求。 如何过滤数据包以使过滤不会立即开始? 如果您查看客户端和服务器之间的任何通信流量,您会注意到,在建立连接时,仅使用 SYN 和 ACK 标志,但在交换数据时,最后一个请求数据包将包含 PSH 标志。 它会自动安装以避免缓冲。 您可以使用此信息创建过滤器:它将允许除包含 PSH 标志的数据包之外的所有数据包。 因此,连接将建立,但客户端将无法向服务器发送数据。

下降

对于 DROP,命令如下所示:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j DROP

启动客户端并观察流量:

交通转储

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:02:47.549498 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [S], seq 2166014137, win 43690, options [mss 65495,sackOK,TS val 1208713786 ecr 0,nop,wscale 7], length 0
10:02:47.549510 IP 127.0.0.1.12345 > 127.0.0.1.49594: Flags [S.], seq 2341799088, ack 2166014138, win 43690, options [mss 65495,sackOK,TS val 1208713786 ecr 1208713786,nop,wscale 7], length 0
10:02:47.549520 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1208713786 ecr 1208713786], length 0
10:02:47.549568 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208713786 ecr 1208713786], length 5
10:02:47.750084 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208713987 ecr 1208713786], length 5
10:02:47.951088 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208714188 ecr 1208713786], length 5
10:02:48.354089 IP 127.0.0.1.49594 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1208714591 ecr 1208713786], length 5

我们看到连接已建立,客户端无法向服务器发送数据。

拒绝

在这种情况下,行为将是相同的:客户端将无法发送请求,但会接收 ICMP 127.0.0.1 tcp 端口 12345 无法访问 并以指数方式增加请求重新提交之间的时间。 该命令如下所示:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT

使用 tcp-reset 拒绝

该命令如下所示:

iptables -A INPUT -p tcp --tcp-flags PSH PSH --dport 12345 -j REJECT --reject-with tcp-reset

我们已经知道,当使用 --reject-with tcp-reset 客户端将收到一个 RST 数据包作为响应,因此可以预测行为:在建立连接时收到 RST 数据包意味着另一端的套接字意外关闭,这意味着客户端应该接收 对等连接重置。 让我们运行我们的脚本并确保这一点。 流量如下所示:

交通转储

[user@host ~]# tcpdump -i lo -nn port 12345
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:22:14.186269 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [S], seq 2615137531, win 43690, options [mss 65495,sackOK,TS val 1209880423 ecr 0,nop,wscale 7], length 0
10:22:14.186284 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [S.], seq 3999904809, ack 2615137532, win 43690, options [mss 65495,sackOK,TS val 1209880423 ecr 1209880423,nop,wscale 7], length 0
10:22:14.186293 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 1209880423 ecr 1209880423], length 0
10:22:14.186338 IP 127.0.0.1.52536 > 127.0.0.1.12345: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 1209880423 ecr 1209880423], length 5
10:22:14.186344 IP 127.0.0.1.12345 > 127.0.0.1.52536: Flags [R], seq 3999904810, win 0, length 0

使用 icmp-host-unreachable 拒绝

我认为每个人都已经很清楚该命令的样子了:) 在这种情况下,客户端的行为将与简单 REJECT 的行为略有不同:客户端不会增加尝试重新发送数据包之间的超时。

[user@host ~]# tcpdump -i lo -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
10:29:56.149202 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.349107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.549117 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.750125 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:56.951130 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:57.152107 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65
10:29:57.353115 IP 127.0.0.1 > 127.0.0.1: ICMP host 127.0.0.1 unreachable, length 65

结论

没有必要编写模拟来测试服务与挂起的客户端或服务器的交互;有时使用 Linux 中的标准实用程序就足够了。

本文中讨论的实用程序具有比所描述的更多的功能,因此您可以提出一些自己的使用它们的选项。 就我个人而言,我写的内容总是足够的(事实上,甚至更少)。 如果您在公司中使用这些或类似的实用程序进行测试,请具体说明如何使用。 如果没有,那么如果您决定使用建议的方法在网络问题的情况下测试它,我希望您的软件会变得更好。

来源: habr.com

添加评论