网络模拟工具实战:tc命令与iptables规则

原理

集成测试中,真实网络环境不可控,而通过Linux内核自带的流量控制(tc)和包过滤(iptables)工具,可以在单机或容器内精确模拟各种网络故障。tc作用于网络栈的出口和入口队列规则(qdisc),通过Netem模块实现延迟、丢包、重复、乱序等损毁;iptables则在Netfilter钩子点丢包(DROP)、拒绝(REJECT)或修改(TOS/MSS等),模拟断网、端口不可达或带宽限速。两种工具组合可以覆盖绝大部分离线场景。

tc利用qdisc(队列规则)在网卡出/入方向挂载Netem调度器;iptables在PREROUTING/FORWARD/OUTPUT链中匹配包并执行目标动作。两者均基于内核态执行,性能开销低,适合大规模并发测试。
网络故障模拟流程图:tc与iptables协作 网络故障模拟逻辑架构 测试进程(Client) tc 出口qdisc (Netem) iptables (DROP/REJECT) 物理/虚拟网卡 目标服务(Server) 故障注入点 延迟 | 丢包 | 断网 | 重复 观察并验证故障效果

实现流程

  1. 确认内核模块:加载 sch_netem、iptable_filter、xt_tcpudp 等模块。
  2. 清空遗留规则:删除现有qdisc和iptables规则,保证测试基线干净。
  3. 依次注入故障:先施加延迟,再叠加丢包,最后通过iptables模拟断网。
  4. 启动测试用例:运行被测服务,发送请求,收集响应时间、错误码、重试次数等指标。
  5. 恢复与清理:删除所有模拟规则,恢复网络默认状态。

命令与配置

延迟模拟

# 在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立即断连。务必指定具体目标地址或端口。

性能与稳定性优化

  1. 限制影响范围:使用 tc filter 配合u32或其他匹配器,只针对特定源IP/端口应用故障,避免干扰其他正常流量。
  2. 叠加粒度控制:延迟和丢包叠加时,Netem参数中相关系数(correlation)不宜过高,否则会出现极端抖动导致被测服务超时雪崩。
  3. 自动恢复机制:在CI脚本中使用 trap 捕捉退出信号,确保测试结束后自动执行clean_all,防止故障规则残留。
  4. 容器化隔离:在Docker容器内执行tc/iptables时,需要特权容器(--privileged)或挂载cgroup。推荐用网络命名空间(nsenter)做更干净的隔离。
  5. 性能观察:持续注入延迟或丢包时,监控系统CPU软中断(/proc/softirqs)和内存占用,避免内核网络栈过载。

通过本章实战,你已经掌握tc和iptables的常见故障注入能力,可以灵活组合出延迟、丢包、断网、重复等多种离线场景,为后续集成测试案例构建打下坚实基础。