放在前面的结论:按照通常方式(本文中的脚本的那种方式),tc只能在出口限制带宽、时延、丢包率,不能在入口限制。
本文若有内容不严谨或不对,欢迎批评,欢迎指正
tc脚本内容
#!/bin/bash
echo "add tbf and netem to eth0..."
tc qdisc del dev eth0 root
tc qdisc add dev eth0 root handle 5:0 tbf rate $1mbit burst 100k limit 75k
if (($3=='0'))
then
tc qdisc add dev eth0 parent 5:0 handle 10:0 netem delay $2ms
else
tc qdisc add dev eth0 parent 5:0 handle 10:0 netem delay $2ms loss $3
fi
tc qdisc show dev eth0
该脚本用于快速设置tc至网卡eth0,建议保存为文件tc_X_Mbps_Y_ms_Z_%loss.sh
,使用时需加三个参数,即
./tc_X_Mbps_Y_ms_Z_%loss.sh {带宽(Mbps)} {时延(Mbps)} {丢包率(%)}
带宽
1.使用docker+ovs设置两个相连的主机,首先对主机1的网卡使用tbf+netem设置为5Mbps带宽,30ms时延 2.在两台主机上分别启动iperf服务端(收)和客户端(发),测试吞吐量,测试完成后交换服务端和客户端所在主机,再测试一遍,以测试tc对入口和出口流量的限制。
主机1收,主机2发。 主机2收,主机1发。
结论:以此脚本的方式使用tc,只能限制出口带宽,不能限制入口带宽。
丢包
将主机1的丢包率设置为100%,然后使用UDP发包程序发包,同样是两个主机分别往对端发以进行测试。
UDP发包程序的逻辑是有一个服务端(收)负责接收UDP数据包,收到一个包后程序结束,调用linux的date命令打印出当前时间。有一个客户端(发)负责发送数据包,发一个包后程序结束,调用linux的date命令打印出当前时间。具体程序见附录。
这里不使用iperf进行测试的原因是TCP建立链接的过程是双向发包的,导致分不清是发出去的包在出口被丢掉还是回来的包在入口被丢掉。
1.首先将主机1的网卡用tc设置为100%丢包。 2.主机1发,主机2收。(这里虽然只测试了3个包,但其实多少个包都是一样的)
主机2发,主机1收。
3.将主机1上的丢包率恢复为0%,然后主机1发,主机2收
结论:以此脚本的方式使用tc,只能在出口丢包,不能在入口丢包。
时延
同样为了避免双向发包问题,使用上述UDP程序进行实验。
1.为了便于观察结果,将主机1的时延设置为10s。
2.主机1发,主机2收
主机2发,主机1收
结论:以此脚本的方式使用tc,只能在出口加时延,不能在入口加时延。
相关资料收集
之所以前面强调“以此脚本的方式”、“通常方式”,是因为看完资料后仍然不是特别清楚tc的出口入口作用情况,故列举一些资料在这。
1.参考资料https://lartc.org/lartc.html#LARTC.QDISC,9.4节
- qdisc可以作用于入口或出口。
- tc在内核态长这样
- shaping作用在出口
2.参考资料https://linux.die.net/man/8/tc - 各个控制的可作用范围
3.参考资料https://tonydeng.github.io/sdn-handbook/linux/tc.html
附录:UDP发包收包程序
服务端(收)
# -*- coding: utf-8 -*-
import socket
import time
import commands
PORT = 8000
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("172.17.0.3", PORT) # 服务器的ip地址和端口号,这里ip地址懒得以参数的方式传入,实际做实验时是手动改的ip
server_socket.bind(address) # 为服务器绑定一个固定的地址,ip和端口
receive_data, client = server_socket.recvfrom(1024)
print("来自客户端%s,发送的%s\n" % (client, receive_data)) #打印接收的内容
print(commands.getstatusoutput('date'))
客户端(发)
# -*- coding: utf-8 -*-
import socket
import commands
#client 发送端
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
PORT = 8000
msg= b"hello"
server_address = ("172.17.0.3", PORT) # 接收方 服务器的ip地址和端口号,这里ip地址懒得以参数的方式传入,实际做实验时是手动改的ip
client_socket.sendto(msg, server_address) #将msg内容发送给指定接收方
print(commands.getstatusoutput('date'))