TC的使用实例说明
下面我们提供一个利用TC来实现对在一个Linux服务器上的两个虚拟主机实行不同的带宽策略的例子。在该例中,我们将讲述如何配置和测试TC。
编译内核
至于如何编译一个新的内核已经不属于本章节讨论的范围,我们假设你已经知道如何重新编译一个内核。
编译内核时将以下几个内核选项选中:"kernel/User netlink socket"和"Netlink device emulation"。这样TC就可以利用netlink来与内核传送信息。同时将所有的排队算法选上,位于包括
"Fair queueing"
"CBQ packet scheduler"
"CSZ packet scheduler"
"the simples PRIO pseudoscheduler"
"RED queue"
"SFQ queue"
"TBF queue"
"QoS support"
"rate estimator"
"packet classifier API"
"routing-tables-based classifier"
"U32 classifier"
"special RSVP classifier and special RSVP classifier for IPv6"。
选中这些选项以后,按正常的编译内核步骤编译内核,然后安装新内核,并用新内核重新启动系统。
编译TC软件包
可以在下面的地址下载到我们需要的软件,然后按照软件包里的说明编译它:
ftp://linux.wauug.org/pub/net/ip-routing/iproute2-current.tar.gz
通常我们要做的只是简单的输入make就可以了。
TC 的设定
图1. CBQ 树图
图一是我们将配置的一个系统的简单的树形图示范。两个叶子节点从父节点分配带宽,IP地址10.0.0.10(标识符1:1)和地址10.0.0.11(标识符1:2)是接口eth0上的IP别名,它们共同分享父节点(标识符1:0)的带宽。这个例子里面只涉及到了对一个接口上的流量控制,大家可以仿照这个例子构造自己感兴趣的控制多个接口设备的配置。
配置QoS特性的第一步就是将qdisc加入到一个接口上,例如本例子:
qdisc add dev eth0 root handle 1: ...
然后定义你需要区别的类别。不同的类别对应不同的流量控制类型。我们的例子中,使用如下的语句:
tc class add dev eth0 parent 1:0 classid X:Y ...
我们的例子中只使用了一层深的类别树。当然,我们可以构造多层深度的复杂的树,基本的原则是一样的:就是一个子节点(如图1所示)继承一个父节点的资源同时进一步根据类的定义去分配父节点的资源。例如,父类1:0拥有该设备的全部带宽,那么子节点1:1不可能获得超过10Mbits的带宽,当然本例子中是限制为1Mbps。
最后定义"IP分组--类别"的映射规则,用来告诉系统的分类器,经过路由器调度的某IP分组该对应什么类型。首先,将一个分类器与输出接口关联起来:
filter add dev eth0 parent 1:0 protocol ip ...
然后,定义"IP分组--类别"的映射规则。本例子中,将利用IP分组的源地址来作为分类的关键词。 下面的脚本完成了这个功能。关于脚本中TC等命令的参数,大家可以参考随机的文档,这里限于篇幅,不做介绍了。
#! /bin/sh
#path to tc and the ip utilities;
#change to reflect yours.
TC=./iproute2/tc/tc
IP=./iproute2/ip/ip
##################################################
#Addresses to be aliased
#change;or;add more to reflect yours
#
ALIAS1=10.0.0.10
ALIAS2=10.0.0.11
##################################################
# add ip aliasing support
#uncomment if you want to use the ip utility to
#add ip-aliasing for you
#
#$IP addr add $ALIAS1 dev eth0
#$IP addr add $ALIAS2 dev eth0
##################################################
# Attaching a device queue discipline to an
# interface a device queue discipline is
# equivalent almost to a device manager
#
#Attach CBQ to eth0
#Things you might need to change:
# bandwidth -- the bandwidth of the eth0 device
# note it must match the devices real bandwidth
# allot -- it is safe to leave it at the MTU of
# the device
# avpkt -- the average packet size that you
# suspect will be seen safe to leave at 1000
# for Ethernet with MTU of 1514 bytes
# mpu -- minimum packet size
#
$TC qdisc add dev eth0 root handle 1: cbq
bandwidth 10Mbit allot 1514 cell 8 avpkt 1000
mpu 64
##################################################
# Attaching class queue disciplines
# bounded -- it is bound to the rate allocated;
# can borrow even if there is a lot of idle
# bandwidth just sitting there isolated -- cannot
# share its bandwidth to other classes prio is the
# priority assigned 0 being the highest and 7 the
# lowest weight -- safer to leave at 1
# queue discipline setup. Classid 1:1 will have a
# rate of 1Mbps which is bounded.
#
$TC class add dev eth0 parent 1:0 classid 1:1 cbq
bandwidth 10Mbit rate 1Mbit avpkt 1000 prio 5
bounded isolated allot 1514 weight 1 maxburst 21
#rate 1Mbit avpkt 1000 prio 5 bounded allot 1514
#weight 1 maxburst 21
# Classid 1:2 will have a rate of 3Mbps which is
# bounded.
$TC class add dev eth0 parent 1:0 classid 1:2 cbq
bandwidth 10Mbit rate 3Mbit avpkt 1000 prio 5
bounded allot 1514 weight 1 maxburst 21
##################################################
# Define the filter to be attached to eth0
# Create with hash table of 256 slots with ID 1:
#
$TC filter add dev eth0 parent 1:0 protocol ip
prio 5 handle 1: u32 divisor 256
##################################################
# define the criteria for mapping incoming packets
# to classes. Add to the 5th slot of hash table a
# rule to;select;virtual address ALIAS1 direct it
# to class 1:1
#
$TC filter add dev eth0 parent 1:0 prio 5 u32
ht 1:6: match ip src $ALIAS1 flowid 1:1
# Add to 6th slot of hash table rule to;select;
# ALIAS2 direct it to class 1:2
$TC filter add dev eth0 parent 1:0 prio 5 u32
ht 1:6: match ip src $ALIAS2 flowid 1:2
## Lookup hash table, if it is not fragmented
## frame. Use protocol as hash key
#
$TC filter add dev eth0 parent 1:0 prio 5 handle
::1 u32 ht 800:: match ip nofrag
offset mask 0x0F00 shift 6
hashkey mask 0x00ff0000 at 8 link 1:
#
#some more examples of how to use u32
# Add to 4th slot of hash table rule to;select;
# tcp/telnet to 193.233.7.75 direct it to class
# 1:4 and prescribe to fall to best effort,
# if traffic violates TBF (32kbit,5K)
#$TC filter add dev eth1 parent 1:0 prio 5 u32
# ht 1:4: match ip dst 193.233.7.75
# match tcp dst 0x17 0xffff
# flowid 1:4
# police rate 32kbit buffer 5kb/8 mpu 64
# mtu 1514 index 1
## Add to 1st slot of hash table rule to;select;
## icmp to 193.233.7.75 direct it to class 1:3
## and prescribe to fall to best effort,
## if traffic violate TBF (10kbit,5K)
#$TC filter add dev eth1 parent 1:0 prio 5 u32
# ht 1:4: match ip dst 193.233.7.75
# match tcp dst 0x17 0xffff
# flowid 1:4
# police rate 32kbit buffer 5kb/8 mpu 64
# mtu 1514 index 1
## Add to 1st slot of hash table rule to;select;
## icmp to 193.233.7.75 direct it to class 1:3
## and prescribe to fall to best effort,
## if traffic violate TBF (10kbit,5K)
#$TC filter add dev eth1 parent 1:0 prio 5 u32
# ht 1:: sample ip protocol 1 0xff
# match ip dst 193.233.7.75 flowid 1:3
# police rate 10kbit buffer 5kb/8 mpu 64
# mtu 1514 index 2
##################################################
#Look at all that we created:
#
echo "---- qdisc parameters ----------"
$TC qdisc ls dev eth0
echo "---- Class parameters ----------"
$TC class ls dev eth0
echo "---- filter parameters ----------"
$TC filter ls dev eth0
测试
我们的例子当中,是有在同一个Linux服务器上有两个虚拟WWW
动态路由器的配置
基本原理介绍
先解释一下什么是动态路由。
从前面的描述中我们可以看到,路由器的基本功能就是为IP分组寻找到达目的地址的路径。我们前一节介绍的是人工手动静态配置路由规则,也就是人为的设定寻路方式。但是因特网是个庞大的系统,上面跑的网络结构负责,而且拓扑结构也在随时改变,这样在某些复杂的范围里我们的静态配置就不一定能获得最佳的寻路路径了。而且一旦网络结构发生改变,我们手动的静态配置也往往无法及时跟着改变。在这个背景下,产生了动态路由配置的概念,也就是动态路由器。
动态路由器上的路由表项是通过相互连接的路由器之间交换彼此信息,然后按照一定的算法优化出来的,而这些路由信息是在一定时间间隙里不断更新,以适应不断变化的网络,以随时获得最优的寻路效果。为了实现IP分组的高效寻路,IETF制定了多种寻路协议。其中用于自治系统(AS:Autonomous System)内部网关协议有开放式最短路径优先(OSPF:Open Shortest Path First)协议和寻路信息协议(RIP:Routing Information Protocol)。所谓自治系统是指在同一实体(如学校、企业或ISP)管理下的主机、路由器及其他网络设备的集合。还有用于自治域系统之间的外部网络路由协议BGP-4等。
运行这些路由协议的软件就是我们通常说的路由软件,Linux下常见的路由软件有gated和zebra,。前者既有GPL版本的发行,又有收费的版本;而后者则是日本某组织开发的完全GPL的高效的路由软件。Linux的发行里面一般都缺省就有gated这个软件,我们下面主要介绍它的配置和使用方法。
路由协议的介绍
我们这里介绍一下RIP协议。
RIP是Routing Information Protocol的缩写,直接翻译就是"路由信息协议"。
RIP计算路由时使用了"距离向量(distance vector)"算法,因此,它也被称作"距离向量寻路协议(distance vector routing protocol)。
RIP的特点是路由器间定时地交换网络的整体知识,并且只和相邻路由器交换这种知识。换句话说,路由器只和相邻路由器共享网络信息。路由器一旦从相邻路由器获取了新的知识,就将其追加到自己的数据库中,并将该信息传递给所有的相邻的路由器。相邻路由器做同样的操作,经过若干次传递,使自治系统内的所有路由器都能获得完整的路由信息。
RIP报文用UDP数据报来传送。为了区别于其他的UDP应用,规定RIPng的公认专用UDP端口号为521。主动寻路更新报文的源/目的的端口都是RIPng端口,应答的更新报文送往发起请求的端口。应当注意,IPv4中RIP使用的端口号是520,与RIPng的有所不同。
定时器爱RIP中有着比较重要的作用。在RIP中为支持寻路操作使用了三个不同的定时器。
第一个是启动定时进行RIP更新操作的定时器。此定时器通常设置成30秒。在RIP标准中对其进一步加以限制,它要求路由器对更新报文的发送间隔采用随机数,将RIP更新报文的间隔选取在25秒到35秒之间。其目的是为了避免网络上所有的路由器以相同的定时发送更新报文,大量的业务量压迫网络造成冲突。利用随机间隔可均衡业务量,从而减少路由器的冲突。
RIP在避免冲突方面还有一点需要注意,在触发更新中不论何时发送了报文,不对30秒定时器复位。如果复位,多个路由器的更新报文的发送间隔就会发生冲突。这是由于所有的路由器在发送触发更新后同时启动定时器造成的。如不对该定时器复位,即使与在数秒前刚广播的触发更新报文的内容完全一样,定时的更新报文也照发不误。
RIP使用的第二个定时器时期满(expiration)定时器。路由器只要收到通往特定信宿的路由,就对通往该信宿的期满定时器初始化。期满定时器虽然被设定为180秒,但在稳定的网络中总是每隔30秒被初始化。当网络不稳定时,此定时器的时间区间表示该路由无效。
RIP最后一个定时器时垃圾收集(garbage collection)定时器。路由器对无效路由打上尺度为无穷大的无效标记并将垃圾收集定时器置位。此时,定时器在120秒的区间内工作。在该期间内路由器将尺度费用置成无穷大的同时,继续公布该信宿。以这种方法公布路由,相邻路由表就能迅速从寻路表中删除该路由。
RIP协议也有它的缺陷:
网络直径较小
RIP将尺度(即费用)无穷大定义为16,这一定义对使用RIP的所有网络的规模作出了严格的限制。因尺度必须是整数,故网络的费用至少为1。在基于RIP的Internet中,所有的系统距其他任何系统不能超过15个网络。这一大小被称作网络直径。
这一限制对管理员分配费用的灵活性是一个很大的制约。管理员分配费用最直接的方法是对各个网络的费用都设成1。但是,在这种分配方式下,RIP就会选择费用最小的路径,而不管该路径上的信道容量的大小。因此会舍弃"较长"的高速路径而通过低效的"较短"路径传送数据。为了避免这种情况的发生,管理员可将大于1的费用分配给低效链路,人为地提高其费用。其结果是最大网络直径随之变小,进一步限制了RIP的网络规模。
对网络变化的反应较慢
RIP网络中的路由器从路由失效到将其识别出来要等待180秒,而在OSPF中典型值是1~2秒。
不支持组播
在RIP中没有公布组成员信息的方法,因此不支持组播寻路。为实现组播寻路需和其他协议并用。
gated的配置
gated支持RIP、OSPF、IS-IS等路由协议。我们这里着重介绍RIP协议的配置方法,其他协议的配置大家可以针对协议本身然后参考相关帮助文档做类似的配置就可以。
首先修改/etc/sysconfig/network文件,使得FORWARD_IPV4=yes。然后在/etc/目录下创建文件名为gated.conf的文件,里面就是需要填写的配置信息。RIP协议的配置语法如下:
rip yes | no | on | off [ {
broadcast ;
nobroadcast ;
nocheckzero ;
preference preference;
defaultmetric metric ;
query authentication [none | [[simple|md5] password]] ;
interface interface_list
[noripin] | [ripin]
[noripout] | [ripout]
[metricin metric]
[metricout metric]
[version 1]|[version 2 [multicast|broadcast]]
[[secondary] authentication [none | [[simple|md5] password]] ;
trustedgateways gateway_list ;
sourcegateways gateway_list ;
traceoptions trace_options ;
} ] ;
上面的配置语法用来启动或者禁止RIP协议的运行,并对RIP协议某些参数进行设置。各参数的含义如下:
broadcast
指明RIP分组将被广播。当广播静态路由或者由其他协议产生的RIP路由项时,这很有用。
nobroadcast
指明当然的接口上不广播RIP分组。
nocheckzero
指明RIP不处理RIP分组中的保留域。通常RIP将拒绝保留域为非零的分组。
preference preference
设置RIP路由的preference,其缺省值是100,这个值可以被其他的给定的策略重写。
metric metric
定义当使用RIP广告由其他路由协议获得的路由信息时使用的尺度(metric)。其缺省值为16(不可达)。
query authentication [none | [[simple|md5] password]] ;
设定身份认证密码。缺省是无需认证。
interface interface_list
针对某特定的接口进行参数设定。
可以有的参数如下:
noripin
指定该接口商接收的RIP分组无效。
ripin
这是缺省的参数。与noripin相反。
noripout
被指定的接口上将无RIP分组发出。缺省值是在所有的广播和非广播的接口商发送送RIP分组。
ripout
这是缺省值。与noripout的含义相反。
metricin metric
指定在新添加的路由表项加入内核路由表以前增加的尺度(metric)。缺省值是1。
metricout metric
指定通过特定的接口发出的RIP前,对尺度的增加值。缺省值是0。
version 1
指定发送第一个版本的RIP协议的分组。缺省值是这个。
version 2
在指定的接口商发送第二个版本的RIP协议分组。如果IP组播可以使用,则缺省发送完全第二版本的分组,如果不支持组播,则使用与第一版本兼容的第二版本的RIP分组。
multicast
指明在特定接口上的第二版本的RIP分组使用组播发送。
broadcast
指明在特定的接口上使用广播来发送与第一版本兼容的第二版本的RIP分组,即使该接口支持组播。
[secondary] authentication [none | [simple|md5] password]
定义身份认证的方式。只对第二版本的RIP协议有用。缺省是无身份认证。
trustedgateways gateway_list
定义RIP接收RIP更新分组的网关。gateway_list 是一个简单的主机名或者IP地址的列表。缺省情况下,在共享网络上的所有的路由器都被认为支持提供RIP更新信息。
sourcegateways gateway_list
定义RIP直接发送分组的路由器列表,而不通过组播或者广播。
traceoptions trace_options
设置RIP跟踪选项。详细设置略。
下面是些配置示例:
配置1:
#
#
# This configuration runs RIP in quiet mode, it only listens to
# packets, no matter how many interfaces are configured.
#
rip yes {
nobroadca
多网卡的设置
静态路由器的配置
Linux下最常用的指定路由规则的命令是route,当然也有些图形化的工具可以使用,我们下面一个一个介绍。
route命令的使用
route工具主要功能是管理Linux系统内核中的路由表。它最大的用途就是用来设定静态的路由表项,通常是在系统用ifconfig配置网络接口(例如网卡等)后,用它来设定主机或者一网段的IP地址应该通过什么接口发送等。
Route工具有复杂的调用参数。
调用格式如下:
route [-CFvnee]
route [-v] [-A family] add [-net|-host] target [netmask
Nm] [gw Gw] [metric N] [mss M] [window W] [irtt I]
[reject] [mod] [dyn] [reinstate] [[dev] If]
route [-v] [-A family] del [-net|-host] target [gw Gw]
[netmask Nm] [metric N] [[dev] If]
route [-V] [--version] [-h] [--help]
主要参数说明如下:
-v 使用冗余输出模式。
-A family
指定特定的地址族(例如"inet"、"inet6")。
-n 使用数字显示的地址(例如,202.38.75.75)而不是去解释域名。
-e 使用与netstat相同的输出格式。
-ee 参数会产生很长的输出,包括内核路由表的几乎所有信息。
-net 目标(target)是一个网段。
-host 目标(target)是一个单独的主机。
-F 显示内核FIB路由表。结果可能被-e 和-ee参数改变。
-C 显示内核中路由缓存信息。
del 删除一个路由表项。
add 增加一个路由表项。
target 配置的目的网段或者主机。可以是IP,或者是网络或主机名。
netmask Nm
用来指明要添加的路由表项的网络掩码。
gw Gw 任何通往目的(target )的IP分组都要通过这个网关。
metric M
设置路由表中该项的尺度域(metric field)为M。
mss M 设置TCP的最大分片长度(MSS)M bytes。
系统缺省值是536。
window W
设置TCP发送窗口的尺寸为W bytes。
irtt I 设置TCP的初始化回路时间(irtt)I毫秒(1-12000)。
缺省情况下按照RFC 1122 规定是300ms。
reject 安装一个阻塞型的路由,这样可能会有路由查找失败。
mod, dyn, reinstate
添加或者修改一个动态路由表项。主要用来测试和诊断。
dev If 强行使用某个特定的输出接口(If),而不用系统去寻找接口。
下面举几个配置的例子:
route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
添加一条路由表项,网段192.56.76.x 应该从接口"eth0"走。
route add default gw mango-gw
添加一条缺省路由(如果没有其他匹配的路由项,就使用这个路由规则)。
"mango-gw"是一个主机名, 而通往这个主机的路由规则应该事先已经设置好了。
route add ipx4 sl0
给主机"ipx4"添加一条路由规则,使用SLIP接口sl0。
Route命令的输出结果
输出的格式有以下几栏:
Destination
目标网段或者主机。
Gateway
网关地址,如果没有设置,则是"*"表示。
Genmask
网络掩码。
Flags 一些可能的标记如下:
U (路由是活动的)
H (目标是一个主机)
G (使用网关(gateway))
R (reinstate route 动态路由产生的表项)
D (dynamically installed by daemon;or;redirect)
M (modified from routing daemon;or;rederict)
! (reject route)
Metric 路由距离。
Ref 路由项引用次数。(linux内核中没有使用)
Use 查找路由项的次数。.
Iface 该路由表项对应的输出接口。
MSS 缺省的TCP最大分片尺寸。
Window 缺省的TCP窗口的尺寸。
irtt 缺省的TCP回路时间。
HH (cached only)
ARP入口的数目。
Arp (cached only)
该路由项对应的物理地址是否过期等信息。
下面是route -n的输出实例:
tarn:~$ /sbin/route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
202.38.64.3 202.38.75.62 255.255.255.255 UGH 0 0 0 eth0
202.38.75.75 0.0.0.0 255.255.255.255 UH 0 0 0 eth2
202.38.75.0 0.0.0.0 255.255.255.128 U 0 0 0 eth0
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2
192.168.75.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
159.226.0.0 202.38.75.62 255.255.0.0 UG 0 0 0 eth0
127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
0.0.0.0 202.38.75.62 0.0.0.0 UG 1 0 0 eth0
上面的输出中我们可以看出,该路由器配置的缺省网关是202.38.75.62,它上面有3个以太网接口(eth0、eth1和eth2)。其中第一条和第二条路由规则是针对一个主机的,其他的都是针对一个网段的,这可以重掩码看出