Linux中的网络故障模拟
1. 概述
在本教程中,我们将着眼于在 Linux 中模拟一些网络故障。特别是,我们将使用tc命令行和 netem排队规则来实现模拟。
2. 网络流量控制
网络流量控制是一种管理系统中网络流量特性的方法。具体来说,我们主要通过操纵 Linux 内核网络堆栈中数据包的排队特性来控制流量。
换句话说,在将数据包发送到网络接口之前,调度程序会将数据包排入队列。然后,我们可以应用不同的规则和逻辑来改变排队行为。因此,我们可以通过配置这些队列来影响网络流量特性。
一般来说,控制网络流量有四个方面:
- Shaping
- 调度
- 分类
- 治安
2.1. Shaping
在网络流量控制中,Shaping是指操纵流量传输速率以达到所需速率的行为。例如,我们可以通过Shaping来限制接口的带宽。
此外,在传送数据包之前延迟数据包也是一种Shaping形式。
2.2. Scheduling
当数据包在队列中时,Scheduling程序可以重新排列数据包。换句话说,我们可以通过重新排列队列来优先处理重要的数据包。因此,这为用户提供了一种实现服务质量的方法。
2.3. Classifying
网络流量控制中的Classifying是指根据数据包的属性对数据包进行分组。例如,我们可以根据数据包的目的地或来源将数据包分组。
然后我们可以将不同的控制机制附加到不同的类,从而使我们能够更精细地控制对不同类型数据包的处理。
2.4. Policing
Policing是网络流量控制中的一种机制,它允许或阻止数据包传输到下一步。一个监管示例是丢弃发往某个远程主机的数据包。
3. 排队规则
排队规则(或简称 qdisc)是一个管理数据包队列调度的调度程序。我们将通过 qdisc 来控制网络流量。qdisc 进一步分为有类或无类 qdisc。
有类的qdisc 可以通过使用类来形成规则的层次结构。通过这些类,可以应用不同的规则和行为。因此,它提供了根据不同规则对不同类别的数据包进行处理的可能性。
另一方面,无类的qdisc 没有任何类的概念。换句话说,无类别 qdisc 的规则适用于通过它们的每个数据包——它们是非歧视性的。
在本教程中,我们将重点关注无类 qdisc。特别是,我们将研究 netem无类 qdisc,它足以模拟各种典型的网络故障模式。
4. TC
tc 是 Linux 中的流量控制命令行工具。具体来说,我们是通过tc命令来配置Linux内核网络栈中的流量控制设置的。
4.1. 安装
大多数最新的 Linux 发行版都已经附带了tc命令。如果系统中没有该命令,我们可以通过安装包含该命令的包来获取。
在基于 Debian 的 Linux 上,我们可以通过使用apt-get 安装iproute2包来获取tc命令:
$ sudo apt-get update
$ sudo apt-get install -y iproute2
另一方面,我们可以 在基于 RHEL 的 Linux(例如 CentOS)中使用yum 包管理器安装包iproute-tc包:
$ sudo yum update
$ sudo yum install -y iproute-tc
安装完成后,我们可以 通过运行tc -help来验证tc命令是否可用 :
$ sudo tc -help
Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }
tc [-force] -batch filename
where OBJECT := { qdisc | class | filter | chain |
action | monitor | exec }
OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[aw] |
-o[neline] | -j[son] | -p[retty] | -c[olor]
-b[atch] [filename] | -n[etns] name | -N[umeric] |
-nm | -nam[es] | { -cf | -conf } path }
如果出现帮助页面,我们就可以确认安装成功了。
5. 列出 Qdisc
要查看我们系统上每个网络接口上应用的 qdisc,我们可以运行tc qdisc show:
$ sudo tc qdisc show
qdisc noqueue 0: dev lo root refcnt 2
qdisc noqueue 0: dev eth0 root refcnt 2
qdisc noqueue 0: dev eth1 root refcnt 2
从输出中,我们可以看到系统中有三个网络接口。此外,它们都附有一个noqueue qdisc。 无队列qdisc 是一个简单的 qdisc,没有类、调度程序、监管或速率限制。换句话说,它只是在收到任何数据包后立即将其发送出去。
除此之外,我们还可以通过在命令末尾指定接口名称来只显示一个接口的信息。例如,仅显示接口eth0的信息:
$ sudo tc qdisc show dev eth0
qdisc noqueue 0: dev eth0 root refcnt 2
6. 模拟固定延迟
要为数据包添加固定延迟,我们可以使用netem上的delay 选项。具体来说,我们可以运行带有netem delay选项的tc qdisc add命令。
例如,我们可以为任何通过eth0的数据包添加 100 毫秒的固定延迟:
$ sudo tc qdisc add dev eth0 root netem delay 100ms
$ sudo tc qdisc list
qdisc noqueue 0: dev lo root refcnt 2
qdisc netem 8003: dev eth0 root refcnt 2 limit 1000 delay 100.0ms
qdisc noqueue 0: dev eth1 root refcnt 2
上面的命令指定了一个netem delay选项。此外,我们指定了 100 毫秒的固定延迟。
然后,tc qdisc add命令只是将延迟规则附加到eth0接口的根级别。由于我们在本文中不看有类 qdisc,我们总是可以将 qdisc 添加到我们界面的根级别。
要查看 qdisc 的运行情况,让我们运行一个*ping * 实验。特别是,我们将尝试 ping google.com并获取读数。然后我们可以对读数进行比较:
ping -c 5 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=1 ttl=37 time=8.21 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=2 ttl=37 time=10.4 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=3 ttl=37 time=9.63 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=4 ttl=37 time=11.2 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=5 ttl=37 time=8.33 ms
--- google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4008ms
rtt min/avg/max/mdev = 8.210/9.560/11.248/1.171 ms
在我们应用规则之前,每个 ping请求大约需要 9.56 毫秒才能完成。
在我们添加 100ms 的固定延迟之后,使用本节前面显示的命令,我们可以看到完成时间大致增加了 100ms:
ping -c 5 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=1 ttl=37 time=127 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=2 ttl=37 time=112 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=3 ttl=37 time=109 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=4 ttl=37 time=111 ms
64 bytes from 172.217.27.238 (172.217.27.238): icmp_seq=5 ttl=37 time=111 ms
--- google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4007ms
rtt min/avg/max/mdev = 109.429/114.275/127.325/6.579 ms
完成实验后,我们将使用tc qdisc delete命令从界面中删除 qdisc。
让我们删除附加到接口 eth0的 qdisc :
$ sudo tc qdisc delete dev eth0 root
$ sudo tc qdisc show eth0
qdisc noqueue 0: dev eth0 root refcnt 2
7. 模拟正态分布延迟
除了固定延迟,netem还提供了根据分布模拟延迟的可能性。特别是,我们可以通过netem delay选项模拟正态分布的延迟。
为此,我们将指定两个参数,分别代表分布的均值和标准差:
netem delay <mean> <standard deviation> distribution <distribution name>
如果我们指定标准偏差值并省略分布参数,netem将默认使用正态分布。 例如,我们可以模拟均值为 100 毫秒、标准差为 50 毫秒的正态分布延迟:
$ sudo tc qdisc add dev eth0 root netem delay 100ms 50ms distribution normal
为了验证新配置,我们可以再次运行ping实验。关于运行统计实验的一个注意事项是我们应该在每次运行中收集更多数据。这是为了确保获得的统计读数更准确。
让我们 ping google.com 240 次并获得往返时间的统计数据:
$ ping -c 240 -q google.com
PING google.com (216.58.196.14) 56(84) bytes of data.
--- google.com ping statistics ---
240 packets transmitted, 240 received, 0% packet loss, time 239363ms
rtt min/avg/max/mdev = 9.256/113.478/238.708/50.648 ms
从总结中,我们可以观察到 240 个 ICMP 请求平均需要 113.48 毫秒才能完成。考虑到大约 10 毫秒的实际完成时间,结果符合我们的预期。
此外,记录的 50.65 毫秒标准偏差确实接近我们配置的标准偏差值。
8. 模拟丢包
通过 netem中的loss选项,我们可以模拟网络数据包的随机丢弃。例如,我们可以模拟队列以 30% 的概率随机丢弃数据包的场景:
$ sudo tc qdisc add dev eth0 root netem loss 30%
让我们再次运行我们的ping实验来验证行为:
$ ping -q -c 60 google.com
PING google.com (172.217.27.238) 56(84) bytes of data.
--- google.com ping statistics ---
60 packets transmitted, 42 received, 30% packet loss, time 63590ms
rtt min/avg/max/mdev = 8.015/11.345/23.986/2.988 ms
从输出中,我们可以看到我们实验的丢包率为 30%。这与我们配置的界面完全一样。
实际上,数据包丢失通常是在一系列数据包中发生的,而不是纯粹偶然发生的。为了说明这种相关性,我们可以将相关性百分比值指定为最后一个参数:
$ sudo tc qdisc add dev eth0 root netem loss 30% 50%
在上面的命令中,我们正在配置 qdisc,使其丢弃 30% 的收到数据包。此外,下一个数据包丢失概率的 50% 将取决于为前一个数据包生成的概率。
9.模拟数据包复制
使用netem中的duplicate选项,我们可以配置 qdisc 随机复制数据包。例如,我们可以在eth0上以 50% 的几率模拟数据包复制:
$ sudo tc qdisc add dev eth0 root duplicate 50%
要查看它的实际效果,我们可以ping google.com:
$ ping -c 2 google.com
PING google.com (142.250.199.46) 56(84) bytes of data.
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=1 ttl=37 time=7.48 ms
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=1 ttl=37 time=7.51 ms (DUP!)
64 bytes from 142.250.199.46 (142.250.199.46): icmp_seq=2 ttl=37 time=8.51 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, +1 duplicates, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 7.484/7.834/8.506/0.475 ms
从输出中可以看出,ICMP 序列 1 有一个重复的 ICMP 请求。
10. 模拟数据包损坏
接下来,我们可以使用netem将数据损坏随机注入数据包。具体来说,我们可以使用corrupt选项来做到这一点。
当指定corrupt选项时,netem将根据配置的百分比在数据包上随机引入单位错误。
例如,我们可以引入 30% 的数据包损坏几率:
$ sudo tc qdisc add dev eth0 root netem corrupt 30%
现在,当我们运行ping命令时,我们应该看到由于损坏的数据包导致大约 30% 的数据包丢失:
$ ping -q -c 240 google.com
PING google.com (172.217.174.174) 56(84) bytes of data.
--- google.com ping statistics ---
240 packets transmitted, 165 received, 31.25% packet loss, time 241364ms
rtt min/avg/max/mdev = 7.392/9.126/29.282/1.968 ms
11. 限制传输速率
**netem qdisc 通过limit选项支持传输速率限制。**例如,我们可以将eth0接口的网络传输速率限制为 10Mbit:
$ sudo tc qdisc add dev eth0 root netem rate 10Mbit
让我们进行一个实验,使用iperf 命令测试配置。iperf命令 是 Linux 中的一个命令,主要用于网络负载测试目的。
在172.18.0.3 的远程主机上运行iperf显示带宽容量为 33.4Gbits:
$ iperf3 -c 172.18.0.3 -p 8080
Connecting to host 172.18.0.3, port 8080
[ 5] local 172.18.0.2 port 50164 connected to 172.18.0.3 port 8080
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 3.36 GBytes 28.9 Gbits/sec 0 1.33 MBytes
[ 5] 1.00-2.00 sec 3.86 GBytes 33.1 Gbits/sec 0 1.33 MBytes
[ 5] 2.00-3.00 sec 3.94 GBytes 33.9 Gbits/sec 0 1.33 MBytes
[ 5] 3.00-4.00 sec 3.97 GBytes 34.1 Gbits/sec 0 1.33 MBytes
[ 5] 4.00-5.00 sec 3.92 GBytes 33.7 Gbits/sec 0 1.33 MBytes
[ 5] 5.00-6.00 sec 4.01 GBytes 34.5 Gbits/sec 0 1.33 MBytes
[ 5] 6.00-7.00 sec 4.02 GBytes 34.5 Gbits/sec 0 1.33 MBytes
^C[ 5] 7.00-7.70 sec 2.82 GBytes 34.5 Gbits/sec 0 1.33 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-7.70 sec 29.9 GBytes 33.4 Gbits/sec 0 sender
[ 5] 0.00-7.70 sec 0.00 Bytes 0.00 bits/sec receiver
一旦我们激活配置,netem就会将该接口上的传输速率限制为 10Mbits。因此,在同一台主机上运行相同的iperf命令会给我们带来较低的传输速率:
$ iperf3 -c 172.18.0.3 -p 8080
Connecting to host 172.18.0.3, port 8080
[ 5] local 172.18.0.2 port 50172 connected to 172.18.0.3 port 8080
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 2.29 MBytes 19.2 Mbits/sec 0 239 KBytes
[ 5] 1.00-2.00 sec 1.06 MBytes 8.86 Mbits/sec 0 297 KBytes
[ 5] 2.00-3.00 sec 1.30 MBytes 10.9 Mbits/sec 0 355 KBytes
[ 5] 3.00-4.00 sec 1.55 MBytes 13.0 Mbits/sec 0 414 KBytes
[ 5] 4.00-5.00 sec 1.80 MBytes 15.1 Mbits/sec 0 472 KBytes
[ 5] 5.00-6.00 sec 1018 KBytes 8.34 Mbits/sec 0 530 KBytes
[ 5] 6.00-7.00 sec 1.06 MBytes 8.86 Mbits/sec 0 588 KBytes
[ 5] 7.00-8.00 sec 2.42 MBytes 20.3 Mbits/sec 0 648 KBytes
[ 5] 8.00-9.00 sec 1.25 MBytes 10.5 Mbits/sec 0 706 KBytes
[ 5] 9.00-10.00 sec 0.00 Bytes 0.00 bits/sec 0 764 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 13.7 MBytes 11.5 Mbits/sec 0 sender
[ 5] 0.00-10.65 sec 12.1 MBytes 9.56 Mbits/sec receiver