按照移动特性,各种网络连接协议不同,导致通信的信号不同,速率也不同,影响app的加载时间、可用性、稳定性,因此对于应用构建弱网测试环境是必要的,弱网测试一般包括构建移动网络环境(主要是信号强弱),网络速率环境(网络延时,丢包,带宽限制,dns解析等各种复杂的网络环境),当然构建弱网环境的工具有很多,如:NistNet, netem等,本文主要介绍tc和iptables工具使用;
netem 是Linux 2.6及以上内核版本提供的一个网络模拟功能模块,该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带宽、传输延迟、丢包等等情况,其中tc是Linux 系统中的一个工具,全名为traffic control(流量控制),tc可以用来控制netem的工作模式,也就是说,如果想使用netem,需要至少两个条件,一个是内核中的netem功能被包含,另一个是要有tc。

1.tc和netmem

tc是数据包的调度者,对互联网而言,一切都是数据包,操控网络实际上是在操控数据包,操控它如何产生,路由,传输,分片等等,tc在数据包离开系统的时候进行控制,处于在IP层与网卡之间,负责将数据包传递到物理层的正是tc模块,实际上,tc维护一个先进先出的数据队列。
netem是模拟丢包,延时等功能,但是没有实现限速功能,如果要限速要使用tc的TBF。
注意:
而netem和TBF不能同时配置一条rule中,因此想要合并使用限速和丢包延时模拟,需要:
1.创建一条TBF rule模拟丢包和延时;
2.以上边的TBF rule为父节点,继续追加一条netem rule(也就是两条rule串起来);
(参考网上图的原理)tc的控制图:
(参考网上图的原理)TBF的工作原理图:

2.使用tc

常用方法:add命令是将一个节点里加入一个qdisc,remove命令是删除qdisc,change是修改规则,replace是替换等。

    tc qdisc [add|change|replace|link] dev DEV [parent qdisc-id |root] [handle qdisc-id ] qdisc [qdisc specific parameters]
    tc class [add|change|replace] dev DEV parent qdisc-id [classid class-id] qdisc [qdisc specific parameters]
    tc filter [add|change|replace] dev DEV [parent qdisc-id|root] protocol Protocol prio Priority filtertype [filtertype specific parameters] flowid flow-id
    tc [-s|-d] qdisc show [dev DEV]
    tc [-s|-d] class show dev DEV
    tc filter show dev DEV

(1)tc -s qdisc ls dev eth0 表示列出所有的eth0规则信息
(2)增加ping延迟命令
测试前:

    PING 10.125.32.19 (10.125.32.19) 56(84) bytes of data.
    64 bytes from 10.125.32.19: icmp_seq=1 ttl=50 time=3.71 ms
    64 bytes from 10.125.32.19: icmp_seq=2 ttl=50 time=3.65 ms

增加延时后测试:tc qdisc add dev eth1 root netem delay 200ms
查询规则(说明已经生效): tc -s qdisc ls dev eth1

    qdisc netem 8003: root refcnt 2 limit 1000 delay 200.0ms
    Sent 18828 bytes 98 pkt (dropped 0, overlimits 0 requeues 0) 
    backlog 106b 1p requeues 0 

测试结果:

    PING 10.125.32.19 (10.125.32.19) 56(84) bytes of data.
    64 bytes from 10.125.32.19: icmp_seq=1 ttl=50 time=203 ms
    64 bytes from 10.125.32.19: icmp_seq=2 ttl=50 time=203 ms
    64 bytes from 10.125.32.19: icmp_seq=3 ttl=50 time=203 ms
    64 bytes from 10.125.32.19: icmp_seq=4 ttl=50 time=203 ms

当然测试完以后要删除规则:

    tc qdisc del dev eth1 root 

其他命令: - 传输设置为延迟100ms±10ms
命令:tc qdisc add dev eth1 root netem delay 100ms 10ms
- 传输设置30%的包会延迟100ms±10ms发送
命令:tc qdisc add dev eth1 root netem delay 100ms 10ms 30%

(3)设置丢包
tc qdisc add dev eth1 root netem loss 50% (50%为设置的丢包率)
使用ping的测试结果:

PING 10.125.32.19 (10.125.32.19) 56(84) bytes of data.
64 bytes from 10.125.32.19: icmp_seq=3 ttl=50 time=3.65 ms
64 bytes from 10.125.32.19: icmp_seq=5 ttl=50 time=3.66 ms
64 bytes from 10.125.32.19: icmp_seq=6 ttl=50 time=3.72 ms
64 bytes from 10.125.32.19: icmp_seq=10 ttl=50 time=3.64 ms
64 bytes from 10.125.32.19: icmp_seq=11 ttl=50 time=3.65 ms
--- 10.125.32.19 ping statistics ---
11 packets transmitted, 5 received, 54% packet loss, time 10014ms
rtt min/avg/max/mdev = 3.642/3.667/3.722/0.028 ms

(4)设置包重复

tc qdisc add dev eth1 root netem duplicate 10% (10%为设置的包重复发送概率) 

(5)模拟数据包损坏

tc qdisc add dev eth1 root netem corrupt 2% (2%为设置的包损坏的概率) 

(7)模拟数据包乱序

tc qdisc change dev eth1 root netem delay 10ms 
    reorder 25% 50% (25%的数据包[50%]相关会被立即发送,其他的延迟10秒) 

3.使用iptables构建入包和出包控制

(模拟只有入包和对某些入包进行过滤)
构建只要入包参数:
iptables -A OUTPUT -p tcp –sport 8080 -j DROP
使用说明 – 将原出口port的8080数据全部断掉,这样就能构建8080接口只有出包,没有入包
查看规则 :iptables -nVL –line-numbers
删除规则 :iptables -D OUTPUT 3 删除OUTPUT链上的第三条规则,就是前面设置的规则
iptables可以配置概率性丢包 : iptables -A INPUT -m statistic –mode random –probability 0.03 -j DROP
更加详细的iptables使用可以查看另外一篇博客Linux iptables 配置

4.附录

在构建网络环境,查看包量工具的工具肯定必不可少,推荐命令和脚本:
使用iptables结合watch:watch iptables -t mangle -n -v -L
使用iptraf工具查看:iptraf
查看网卡流量脚本(每秒钟的数据):

# statics network send and recv data for every senconds
#!/bin/sh

ex_count=10
if [ $# -gt 0 ];then
        ex_count=$1
fi

awk -v _count=$ex_count 'BEGIN{
OFMT="%.3f";
devf="/proc/net/dev";
print strftime("===================Start Information===================", systime());
while(("cat "devf) | getline)
{
    if($0 ~ /:/ && ($10+0) > 0)
    {
        split($1,tarr,":");
        net[tarr[1]]=$10+tarr[2];
        print tarr[1],$10+tarr[2];
    }
}
close(devf);
while((system("sleep 1 ")) >=0 && _count > 0)
{
    print strftime("===================%Y%m%d %T===================", systime());
    while( getline < devf )
    {
        if($0 ~ /:/ && ($10+0) > 0)
        {
            split($1,tarr,":");
            if(tarr[1] in net)
            {
                print tarr[1],":",($10+tarr[2]-net[tarr[1]])*8/1024,"kb/s";
                net[tarr[1]]=$10+tarr[2];
            }
        }
    }
    close(devf);
    --_count;
}
}'

使用sar工具查看包量:sar -n DEV|EDEV|NFS|NFSD|SOCK|ALL 1 1 #每1秒钟取一次值
同时结合一张sar与linux网络图(参考网上)