为单个主机或子网限速
虽然在我们的man和其它地方对于这个问题描述的相当详细,但是这个经常被问到的问题存在简单的回答,不需要完全地理解流量控制。 这招就三行:
tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 10mbit
tc class add dev $DEV parent 1: classid 1:1 cbq rate 512kbit /
allot 1500 prio 5 bounded isolated
tc filter add dev $DEV parent 1: protocol ip prio 16 u32 /
match ip dst 195.96.96.97 flowid 1:1
第 一行在你的网卡中添加一个基于类的队列,并告诉内核如何计算,假定是一个10M的网卡。如果你做错了,不会有什么实际的损害。但是正确地设置会使结果更加 精确。 第二行用一些合理的参数创建了一个512kbit的类。关于细节,参见cbq的手册和第9章。 最后一行表明那些数据包应该交给整形的类。未匹配的数据包就不整形。如果想进行更精确的匹配(子网、端口等等),参见9.6.2。 如果你修改了什么,就得重新装入脚本,执行“tc qdisc del dev $DEV root”可以清除当前配置。 脚本还可以进一步地改进,最后加上一行“tc qdisc add dev $DEV parent 1:1 sfq perturb 10”。它的作用和细节参见9.2.3。
15.10. 一个完全NAT和QoS的范例
我是Pedro Larroy < piotr@omega.resa.es>
我 这里描述的环境是这样的:有很多用户,通过一个Linux路由器连接到Internet上,路由器上有一个公网IP地址,通过NAT让大家上网。我作为网 管,使用这个QoS设置来让我们宿舍的198个用户访问Internet。这里的用户们非常多地使用对等应用,所以合理的带宽管理是必须的。我希望这个例 子能够帮助那些对lartc感兴趣的读者们。 我一开始一步步地作了实际的实现,最后我会解释如何让这个过程在启动期间自动进行。这个网络的私有LAN通过Linux路由器连接到Internet,路 由器上有一个公网IP。把
它扩展成拥有多个公网IP非常简单,改动一些iptables规则即可。为了顺利配置,我们需要:
Linux 2.4.18或更高版本的内核
如果你使用2.4.18,需要打上HTB补丁。
iproute
也是要确认“tc”是否支持HTB。
iptables
15.10.1. 开始优化那不多的带宽
首 先,我们设置一些用来分类的队列规定。我们创建一个由6个类组成的HTB队列规定,各类按升序排列。这样,我们就有了一些总能够分配到制定带宽的类,但是 他们还可以利用其它类没有用到的一部分带宽。更高优先权(更小的优先权值)的类优先获得剩余的带宽。我们的连接是一个2Mbps下行/300kbps上行 的ADSL线路。我使用240kbps作为峰值速率,因为这个值比延迟开始增长时的带宽值稍高一点,延迟的原因是在本端与对端主机之间任意地点的缓冲区造 成的。参数应该通过试验计时得到,当观测到附近的主机之间有明显的延迟时就要调高或调低。 现在调整CEIL为你上行速率的75%,我写eth0的地方应该是你拥有公网IP的网卡。在root shell下执行下列命令行,开始我们的试验:
CEIL=240
tc qdisc add dev eth0 root handle 1: htb default 15
tc class add dev eth0 parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80kbit ceil 80kbit prio 0
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3
tc class add dev eth0 parent 1:1 classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3
tc qdisc add dev eth0 parent 1:12 handle 120: sfq perturb 10
tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10
tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10
tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10
我们刚才创建了一个一级的HTB树。看上去应该是:
+---------+
| root 1: |
+---------+
|
+---------------------------------------+
| class 1:1 |
+---------------------------------------+
| | | | | |
+----+ +----+ +----+ +----+ +----+ +----+
|1:10| |1:11| |1:12| |1:13| |1:14| |1:15|
+----+ +----+ +----+ +----+ +----+ +----+
classid 1:10 htb rate 80kbit ceil 80kbit prio 0
这是一个较高优先权的类。这个类中的数据包拥有最低的延迟并最先取得空闲带宽,所以应该设置这个类的峰值速率。我们将把这些要求低延迟的服务归属到该类中:ssh、telnet、dns、quake3、irc和带有SYN标记的数据包。
classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1
这是我们第一个用于放置大批量传输的类。在我的例子中,用来处理从我的本地WEB服务器发出和浏览网页的数据包,分别是源端口80、目标端口80的包。
classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
我把带有最大吞吐TOS位的数据包和其余从路由器本地进程发往Internet的数据包放在这个类中。所以下面的类会包括经过路由穿过机器的数据包。
classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2
这些类包含了其它经过NAT,需要用高优先权进行大批量传输的机器。
classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3
这里是邮件(SMTP、pop3?)相关和TOS要求最小成本的数据流。
classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3
最后是路由器后面经过NAT进行大批量传输的机器。所有的变态网虫、下载狂人等等都分在这里,以保证他们不会妨碍正常服务。
15.10.2. 对数据包分类
我 们创建好了队列规定,但是还没有进行必要的分类,所以现在等于把所有发出的数据包都送给了1:15 (因为我们设的是:tc qdisc add dev eth0 root handle 1: htb default 15)。现在需要告诉机器那些数据包走哪条路,这是最重要的部分。 现在我们设置过滤器以便用iptables对数据包进行分类。我确实更愿意用iptables来做这件事,因为它更灵活,而且你还可以为每个规则设置计数 器。而且可以通过RETURN方法避免遍历所有的规则。我们执行下面的命令:
tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev eth0 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
tc filter add dev eth0 parent 1:0 protocol ip prio 3 handle 3 fw classid 1:12
tc filter add dev eth0 parent 1:0 protocol ip prio 4 handle 4 fw classid 1:13
tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 5 fw classid 1:14
tc filter add dev eth0 parent 1:0 protocol ip prio 6 handle 6 fw classid 1:15
这样我们就告诉了内核,数据包会有一个特定的FWMARK标记值(hanlde x fw),表明它应该送给哪类( classid x:x)。后面你会明白如何给数据包打标记。 你必须先搞明白数据包穿越Linux的IP协议层:
+------------+ +---------+ +-------------+
Packet -| PREROUTING |---routing-----| FORWARD |-------+-------| POSTROUTING |- Packe
input +------------+ decision +---------+ +-------------+ out
| |
+-------+ +--------+
| INPUT |---- Local process- -----| OUTPUT |
+-------+ +--------+
我 假定你已经完整地清除了tables而且缺省策略是ACCEPT(-P ACCEPT)。如果你还没有搞明白iptables也没关系,缺省设置也可以用。我们的私有子网是一个B类——172.17.0.0/16,我们的公网 IP是212.170.21.172。 下面我们指示内核进行NAT,以便私网内部的人能够与外部通讯:
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 172.17.0.0/255.255.0.0 -o eth0 -j SNAT --to-source
212.170.21.172
然后检查数据包是否通过了1:15:
tc -s class show dev eth0
你可以开始给数据包打标记了,往PREROUTING链中添加一个mangle规则:
iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -p icmp -j RETURN
现在你从私网内部ping外部的某个站点,就会看到数据包的计数在增长。察看1:10的计数:
tc -s class show dev eth0
我们设置了一个-j RETURN以防止数据包遍历所有的规则。也就是说icmp数据包不必再匹配下面的那些规则了。把这招记住。现在我们可以开始添加更多规则了,我们来彻底地设置TOS的处理:
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j MARK --set-mark 0
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j MARK --set-mark 0x
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j MARK --set-m
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j RETURN
然后提高ssh数据包的优先权:
iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j RETURN
提高tcp初始连接(也就是带有SYN的数据包)的优先权是非常明智的:
iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK
0x1
iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j RET
以此类推。当我们向PREROUTING中添加完mangle规则后,用这条规则结束
PREROUTING表:
iptables -t mangle -A PREROUTING -j MARK --set-mark 0x6
也 就是说前面没有打过标记的数据包将交给1:15处理。实际上最后一步是不必要的,因为1:15是缺省类,但我仍然打上标记是为了保持整个设置的协调一致, 而且这样还能看到规则的包计数。 应该在OUTPUT链中再重复一遍上面的设置,也就是说把命令中的 –A PREROUTING改成-A OUTPUT ( s/PREROUTING/OUTPUT/ )。那么本地(Linux路由器)产生的数据包也可以参与分类了。我用-j MARK --set-mark 0x3来结束OUTPUT链,让本地产生的数据包有更高的优先权。
15.10.3. 改进设置
现在我们完成 了所有的工作。花点时间看看图表,了解一下你的带宽究竟在被如何消耗,与你的期望是否一致。经过很多小时的监测,最终让Internet连接达到了很好的 效果。否则可能发生经常性地超时或者tcp的新建连接无法得到任何带宽。 如果你发现有些类几乎总是满满的,不如在那个类下面再附加上另一个队列规定,以保证带宽的公平使用:
tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10
tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10
tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10
15.10.4. 让上面的设置开机时自动执行
肯 定有无数种方法。我的方法是,写一个脚本/etc/init.d/packetfilter,让他能够接受[start | stop | stop-tables | start-tables | reload-tables]等参数,来配置对列规定和装载需要的内核模块,就像一个守护程序一样。同样的脚本从 /etc/network/iptables-rules中得到iptables规则。我争取稍稍美化一下,放到我的网页上去。
虽然在我们的man和其它地方对于这个问题描述的相当详细,但是这个经常被问到的问题存在简单的回答,不需要完全地理解流量控制。 这招就三行:
tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 10mbit
tc class add dev $DEV parent 1: classid 1:1 cbq rate 512kbit /
allot 1500 prio 5 bounded isolated
tc filter add dev $DEV parent 1: protocol ip prio 16 u32 /
match ip dst 195.96.96.97 flowid 1:1
第 一行在你的网卡中添加一个基于类的队列,并告诉内核如何计算,假定是一个10M的网卡。如果你做错了,不会有什么实际的损害。但是正确地设置会使结果更加 精确。 第二行用一些合理的参数创建了一个512kbit的类。关于细节,参见cbq的手册和第9章。 最后一行表明那些数据包应该交给整形的类。未匹配的数据包就不整形。如果想进行更精确的匹配(子网、端口等等),参见9.6.2。 如果你修改了什么,就得重新装入脚本,执行“tc qdisc del dev $DEV root”可以清除当前配置。 脚本还可以进一步地改进,最后加上一行“tc qdisc add dev $DEV parent 1:1 sfq perturb 10”。它的作用和细节参见9.2.3。
15.10. 一个完全NAT和QoS的范例
我是Pedro Larroy < piotr@omega.resa.es>
我 这里描述的环境是这样的:有很多用户,通过一个Linux路由器连接到Internet上,路由器上有一个公网IP地址,通过NAT让大家上网。我作为网 管,使用这个QoS设置来让我们宿舍的198个用户访问Internet。这里的用户们非常多地使用对等应用,所以合理的带宽管理是必须的。我希望这个例 子能够帮助那些对lartc感兴趣的读者们。 我一开始一步步地作了实际的实现,最后我会解释如何让这个过程在启动期间自动进行。这个网络的私有LAN通过Linux路由器连接到Internet,路 由器上有一个公网IP。把
它扩展成拥有多个公网IP非常简单,改动一些iptables规则即可。为了顺利配置,我们需要:
Linux 2.4.18或更高版本的内核
如果你使用2.4.18,需要打上HTB补丁。
iproute
也是要确认“tc”是否支持HTB。
iptables
15.10.1. 开始优化那不多的带宽
首 先,我们设置一些用来分类的队列规定。我们创建一个由6个类组成的HTB队列规定,各类按升序排列。这样,我们就有了一些总能够分配到制定带宽的类,但是 他们还可以利用其它类没有用到的一部分带宽。更高优先权(更小的优先权值)的类优先获得剩余的带宽。我们的连接是一个2Mbps下行/300kbps上行 的ADSL线路。我使用240kbps作为峰值速率,因为这个值比延迟开始增长时的带宽值稍高一点,延迟的原因是在本端与对端主机之间任意地点的缓冲区造 成的。参数应该通过试验计时得到,当观测到附近的主机之间有明显的延迟时就要调高或调低。 现在调整CEIL为你上行速率的75%,我写eth0的地方应该是你拥有公网IP的网卡。在root shell下执行下列命令行,开始我们的试验:
CEIL=240
tc qdisc add dev eth0 root handle 1: htb default 15
tc class add dev eth0 parent 1: classid 1:1 htb rate ${CEIL}kbit ceil ${CEIL}kbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80kbit ceil 80kbit prio 0
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2
tc class add dev eth0 parent 1:1 classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3
tc class add dev eth0 parent 1:1 classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3
tc qdisc add dev eth0 parent 1:12 handle 120: sfq perturb 10
tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10
tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10
tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10
我们刚才创建了一个一级的HTB树。看上去应该是:
+---------+
| root 1: |
+---------+
|
+---------------------------------------+
| class 1:1 |
+---------------------------------------+
| | | | | |
+----+ +----+ +----+ +----+ +----+ +----+
|1:10| |1:11| |1:12| |1:13| |1:14| |1:15|
+----+ +----+ +----+ +----+ +----+ +----+
classid 1:10 htb rate 80kbit ceil 80kbit prio 0
这是一个较高优先权的类。这个类中的数据包拥有最低的延迟并最先取得空闲带宽,所以应该设置这个类的峰值速率。我们将把这些要求低延迟的服务归属到该类中:ssh、telnet、dns、quake3、irc和带有SYN标记的数据包。
classid 1:11 htb rate 80kbit ceil ${CEIL}kbit prio 1
这是我们第一个用于放置大批量传输的类。在我的例子中,用来处理从我的本地WEB服务器发出和浏览网页的数据包,分别是源端口80、目标端口80的包。
classid 1:12 htb rate 20kbit ceil ${CEIL}kbit prio 2
我把带有最大吞吐TOS位的数据包和其余从路由器本地进程发往Internet的数据包放在这个类中。所以下面的类会包括经过路由穿过机器的数据包。
classid 1:13 htb rate 20kbit ceil ${CEIL}kbit prio 2
这些类包含了其它经过NAT,需要用高优先权进行大批量传输的机器。
classid 1:14 htb rate 10kbit ceil ${CEIL}kbit prio 3
这里是邮件(SMTP、pop3?)相关和TOS要求最小成本的数据流。
classid 1:15 htb rate 30kbit ceil ${CEIL}kbit prio 3
最后是路由器后面经过NAT进行大批量传输的机器。所有的变态网虫、下载狂人等等都分在这里,以保证他们不会妨碍正常服务。
15.10.2. 对数据包分类
我 们创建好了队列规定,但是还没有进行必要的分类,所以现在等于把所有发出的数据包都送给了1:15 (因为我们设的是:tc qdisc add dev eth0 root handle 1: htb default 15)。现在需要告诉机器那些数据包走哪条路,这是最重要的部分。 现在我们设置过滤器以便用iptables对数据包进行分类。我确实更愿意用iptables来做这件事,因为它更灵活,而且你还可以为每个规则设置计数 器。而且可以通过RETURN方法避免遍历所有的规则。我们执行下面的命令:
tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 1 fw classid 1:10
tc filter add dev eth0 parent 1:0 protocol ip prio 2 handle 2 fw classid 1:11
tc filter add dev eth0 parent 1:0 protocol ip prio 3 handle 3 fw classid 1:12
tc filter add dev eth0 parent 1:0 protocol ip prio 4 handle 4 fw classid 1:13
tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 5 fw classid 1:14
tc filter add dev eth0 parent 1:0 protocol ip prio 6 handle 6 fw classid 1:15
这样我们就告诉了内核,数据包会有一个特定的FWMARK标记值(hanlde x fw),表明它应该送给哪类( classid x:x)。后面你会明白如何给数据包打标记。 你必须先搞明白数据包穿越Linux的IP协议层:
+------------+ +---------+ +-------------+
Packet -| PREROUTING |---routing-----| FORWARD |-------+-------| POSTROUTING |- Packe
input +------------+ decision +---------+ +-------------+ out
| |
+-------+ +--------+
| INPUT |---- Local process- -----| OUTPUT |
+-------+ +--------+
我 假定你已经完整地清除了tables而且缺省策略是ACCEPT(-P ACCEPT)。如果你还没有搞明白iptables也没关系,缺省设置也可以用。我们的私有子网是一个B类——172.17.0.0/16,我们的公网 IP是212.170.21.172。 下面我们指示内核进行NAT,以便私网内部的人能够与外部通讯:
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 172.17.0.0/255.255.0.0 -o eth0 -j SNAT --to-source
212.170.21.172
然后检查数据包是否通过了1:15:
tc -s class show dev eth0
你可以开始给数据包打标记了,往PREROUTING链中添加一个mangle规则:
iptables -t mangle -A PREROUTING -p icmp -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -p icmp -j RETURN
现在你从私网内部ping外部的某个站点,就会看到数据包的计数在增长。察看1:10的计数:
tc -s class show dev eth0
我们设置了一个-j RETURN以防止数据包遍历所有的规则。也就是说icmp数据包不必再匹配下面的那些规则了。把这招记住。现在我们可以开始添加更多规则了,我们来彻底地设置TOS的处理:
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j MARK --set-mark 0
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Delay -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j MARK --set-mark 0x
iptables -t mangle -A PREROUTING -m tos --tos Minimize-Cost -j RETURN
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j MARK --set-m
iptables -t mangle -A PREROUTING -m tos --tos Maximize-Throughput -j RETURN
然后提高ssh数据包的优先权:
iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -p tcp -m tcp --sport 22 -j RETURN
提高tcp初始连接(也就是带有SYN的数据包)的优先权是非常明智的:
iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK
0x1
iptables -t mangle -I PREROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j RET
以此类推。当我们向PREROUTING中添加完mangle规则后,用这条规则结束
PREROUTING表:
iptables -t mangle -A PREROUTING -j MARK --set-mark 0x6
也 就是说前面没有打过标记的数据包将交给1:15处理。实际上最后一步是不必要的,因为1:15是缺省类,但我仍然打上标记是为了保持整个设置的协调一致, 而且这样还能看到规则的包计数。 应该在OUTPUT链中再重复一遍上面的设置,也就是说把命令中的 –A PREROUTING改成-A OUTPUT ( s/PREROUTING/OUTPUT/ )。那么本地(Linux路由器)产生的数据包也可以参与分类了。我用-j MARK --set-mark 0x3来结束OUTPUT链,让本地产生的数据包有更高的优先权。
15.10.3. 改进设置
现在我们完成 了所有的工作。花点时间看看图表,了解一下你的带宽究竟在被如何消耗,与你的期望是否一致。经过很多小时的监测,最终让Internet连接达到了很好的 效果。否则可能发生经常性地超时或者tcp的新建连接无法得到任何带宽。 如果你发现有些类几乎总是满满的,不如在那个类下面再附加上另一个队列规定,以保证带宽的公平使用:
tc qdisc add dev eth0 parent 1:13 handle 130: sfq perturb 10
tc qdisc add dev eth0 parent 1:14 handle 140: sfq perturb 10
tc qdisc add dev eth0 parent 1:15 handle 150: sfq perturb 10
15.10.4. 让上面的设置开机时自动执行
肯 定有无数种方法。我的方法是,写一个脚本/etc/init.d/packetfilter,让他能够接受[start | stop | stop-tables | start-tables | reload-tables]等参数,来配置对列规定和装载需要的内核模块,就像一个守护程序一样。同样的脚本从 /etc/network/iptables-rules中得到iptables规则。我争取稍稍美化一下,放到我的网页上去。
1148

被折叠的 条评论
为什么被折叠?



