网络模拟工具实战:tc命令与iptables规则
原理
集成测试中,真实网络环境不可控,而通过Linux内核自带的流量控制(tc)和包过滤(iptables)工具,可以在单机或容器内精确模拟各种网络故障。tc作用于网络栈的出口和入口队列规则(qdisc),通过Netem模块实现延迟、丢包、重复、乱序等损毁;iptables则在Netfilter钩子点丢包(DROP)、拒绝(REJECT)或修改(TOS/MSS等),模拟断网、端口不可达或带宽限速。两种工具组合可以覆盖绝大部分离线场景。
tc利用qdisc(队列规则)在网卡出/入方向挂载Netem调度器;iptables在PREROUTING/FORWARD/OUTPUT链中匹配包并执行目标动作。两者均基于内核态执行,性能开销低,适合大规模并发测试。
实现流程
- 确认内核模块:加载
sch_netem、iptable_filter、xt_tcpudp等模块。 - 清空遗留规则:删除现有qdisc和iptables规则,保证测试基线干净。
- 依次注入故障:先施加延迟,再叠加丢包,最后通过iptables模拟断网。
- 启动测试用例:运行被测服务,发送请求,收集响应时间、错误码、重试次数等指标。
- 恢复与清理:删除所有模拟规则,恢复网络默认状态。
命令与配置
延迟模拟
# 在eth0出口添加固定延迟100ms ± 10ms
sudo tc qdisc add dev eth0 root netem delay 100ms 10ms distribution normal
# 查看已配置的qdisc
tc qdisc show dev eth0
# 删除eth0上的所有qdisc
sudo tc qdisc del dev eth0 root
丢包模拟
# 丢包率30%,且25%的丢包具有相关性
sudo tc qdisc add dev eth0 root netem loss 30% 25%
# 延迟+丢包组合(百分比、相关系数可调)
sudo tc qdisc add dev eth0 root netem delay 50ms 10ms loss 15% 30%
断网模拟(tc方式)
# 通过Netem corrupt损坏所有包,相当于断网
sudo tc qdisc add dev eth0 root netem corrupt 100%
断网模拟(iptables方式)
# 丢弃所有发往目标IP的包
sudo iptables -A OUTPUT -d 10.0.0.2 -j DROP
# 丢弃目标IP发来的包
sudo iptables -A INPUT -s 10.0.0.2 -j DROP
# 仅丢弃目标端口(如3306)
sudo iptables -A OUTPUT -d 10.0.0.2 -p tcp --dport 3306 -j DROP
# 恢复:清空filter表所有规则
sudo iptables -F
调试
调试阶段的核心目标是确认故障规则是否生效、影响范围是否正确。
| 目标 | 调试命令 | 预期输出特征 |
|---|---|---|
| 查看tc规则 | tc -s qdisc show dev eth0 |
显示sent bytes,dropped/lost计数增加 |
| 测量RTT | ping -c 10 <target> |
延迟符合设定值,丢包率匹配 |
| 确认iptables计数 | iptables -L -v -n |
目标链pkts增长,显示MATCH |
| 抓包验证 | tcpdump -i eth0 host <target> |
未收到响应包(断网时) |
建议在每条tc/iptables规则生效后立即用ping或curl快速验证,避免后续测试误判。
部署
将故障注入命令封装为函数脚本,支持一键启动/停止。
#!/bin/bash
# fault_inject.sh
DEV=eth0
TARGET_IP=10.0.0.2
DELAY_MS=200
LOSS_PERCENT=20
inject_latency() {
sudo tc qdisc add dev $DEV root netem delay ${DELAY_MS}ms
echo "注入延迟 ${DELAY_MS}ms"
}
inject_loss() {
sudo tc qdisc change dev $DEV root netem delay ${DELAY_MS}ms loss ${LOSS_PERCENT}%
echo "叠加丢包 ${LOSS_PERCENT}%"
}
inject_network_down() {
sudo iptables -A OUTPUT -d $TARGET_IP -j DROP
echo "断网:丢弃发往 $TARGET_IP 的包"
}
clean_all() {
sudo tc qdisc del dev $DEV root 2>/dev/null
sudo iptables -D OUTPUT -d $TARGET_IP -j DROP 2>/dev/null
sudo iptables -F
echo "已清理所有故障规则"
}
case "$1" in
latency) inject_latency ;;
loss) inject_loss ;;
down) inject_network_down ;;
clean) clean_all ;;
*) echo "用法: $0 {latency|loss|down|clean}" ;;
esac
常见错误
- RTNETLINK answers: Operation not supported:内核未加载
sch_netem,执行sudo modprobe sch_netem。 - iptables: No chain/target/match by that name:缺少内核模块,加载
iptable_filter、xt_tcpudp。 - 无法删除qdisc:网卡已被其他qdisc占用,或存在依赖子类,使用
tc qdisc del dev eth0 root需先解除所有子类。 - 断网后自身SSH连接断开:不要在SSH会话的网卡上直接执行DROP ALL,应指定目标IP范围,或者通过串口/管理网卡操作。
- 多网卡环境下规则错乱:确认当前测试流量走的是哪个网卡,使用
ip route get <target>查路由出口。
切勿在未确认目标IP的情况下直接
iptables -A OUTPUT -j DROP,这会导致当前Shell及SSH立即断连。务必指定具体目标地址或端口。性能与稳定性优化
- 限制影响范围:使用
tc filter配合u32或其他匹配器,只针对特定源IP/端口应用故障,避免干扰其他正常流量。 - 叠加粒度控制:延迟和丢包叠加时,Netem参数中相关系数(correlation)不宜过高,否则会出现极端抖动导致被测服务超时雪崩。
- 自动恢复机制:在CI脚本中使用
trap捕捉退出信号,确保测试结束后自动执行clean_all,防止故障规则残留。 - 容器化隔离:在Docker容器内执行tc/iptables时,需要特权容器(
--privileged)或挂载cgroup。推荐用网络命名空间(nsenter)做更干净的隔离。 - 性能观察:持续注入延迟或丢包时,监控系统CPU软中断(
/proc/softirqs)和内存占用,避免内核网络栈过载。
通过本章实战,你已经掌握tc和iptables的常见故障注入能力,可以灵活组合出延迟、丢包、断网、重复等多种离线场景,为后续集成测试案例构建打下坚实基础。