摘要:本文主要介绍如何用Linux做一个功能完备的路由器,这里将主要介绍静态路由器和动态路由器的配置,但不包括IP伪装或者说公用一个IP地址访问外面网络等问题。其已经不属于本主题讨论的范围,请参考其他如防火墙的配置等的内容。
LRP(Linux Router Project) 已经成立了有好几年的时间了。这个计划的目的是利用Linux操作系统将老旧的个人电脑如486当成网络的路由器使用。利用Linux低成本建立的路由器 可能引起企业界广泛的注意。正如StoneFly Technology的系统整合工程师Bret Berger所说的,对无法负担两千(美元)思科 (Cisco)路由器的使用者来说,利用486加上几张以太网卡,当成路由器使用,是另一个选择。而对于一些小型企业、小型ISP ,甚至是政府机构,这 是一个很好的解决方案。事实上目前国内高校很多场合正是利用了这一解决方案。
由于LRP计划已经把Linux的源代码精简 化,LRP软件可以少于2MB ,同时也可以将LRP软件放入一张软盘内。也就是说在没有硬盘的情况下,LPR也可以利用软盘,载入电脑中使用。 Virtual Design Group的软件工程师Michael Kornegay则表示,他们已经将Linux路由器软件安装在一架老旧的x86 个人电脑上,再加上两张以太网卡,没有使用硬盘的设备,只是使用软盘启动,便当成阳春型的路由器与防火墙使用。
我们在这一章 里不是教大家如何制作小于2MB的LRP软件包,而是告诉大家如何利用Linux来获得一个性能良好、价格低廉的路由器。利用Linux构建路由器使用的 成本虽然便宜,可是在性能与效率上与真正的路由器相比,却还是有一段距离。对于玩家们或是一些要求不高的使用者来说,这是一个不错的选择。
什么是路由器?
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 {
nobroadcast ;
} ;
配置2:
# This configuration emulates routed. It runs RIP and only sends
# updates if there are more than one interfaces up and IP forwarding is
# enabled in the kernel.
#
# NOTE that RIP *will not* run if UDP checksums are disabled in
# the kernel.
#
rip yes ;
zebra介绍
这 是日本人写的以GNU版权方式发布的软件,开始于1996年,主要的功能是实现了 RIPv1,RIPv2,RIPng, OSPFv2, OSPFv3, BGP-4, and BGP-4+路由协议,目前是0.87版,目前支持 Linux和FreeBSD,将来会支持Solaris 7和GNU Hurd。
其中 RIPv1, RIPv2, OSPFv2是用于IPv4的自治域系统内部网络路由协议,最好的是OSPF,他支持VLSM(变长子网掩码)、收敛快,能 根据链路的负载等动态调整路由,是目前最好的所有厂商都支持的内部路由协议。跟他差不多(也许还要好)的是cisco专有的EIGRP.
BGP- 4是用于自治域系统之间的外部网络路由协议,也是目前Internet主干上目前使用的协议,非常的灵活。在国外用的非常普遍,如果一个网络有两个以上出 口(连接两个ISP)极大的可能会用他。但是在国内好象很少使用,这也跟国内的网络比较封闭有关。假如我们跟CSTNET和CETNET使用BGP-4的 话,只要这两个出口一个是通的,我们对外的连接不会中断超过1分钟。
RIPng OSPFv3, BGP-4+主要扩展了对ipv6的支持。
这个软件配置的很多方面跟cisco的IOS配置几乎完全相同,我们完全可以拿一台PC机来完成一些必须用昂贵的CISCO路由器才能完成的比较复杂的路由协议处理控制功能。
GNU Zebra可以到www.zebra.org去找。
路由器上的策略控制:IP带宽管理(QoS)
为什么要管理带宽?
因 特网的成功主要因素是IP(Internet Protocol)协议族的简单和稳健。现在几乎所有的人都在向IP靠拢,甚至传统的电讯公司也在将它们的 基于电路交换的语音网络向IP网络转。然而基于IP协议的因特网这时候就遇到了一个非常大的困难。它不相ATM协议,它是平等地对待任何业务,也就是说所 有的通过IP网络的数据都被平等地尽可能好的传送(称:尽力型服务)。如果我愿意多付1倍的钱,我也不能让我的主页下载的速度提高一倍。这时候就引入了 QoS概念,也就是服务质量保证。这种情况下,平等对待所有IP业务数据的方法就要被放弃,而试图区分不同的用户或业务,然后分配不同的带宽。这就是路由 器上的带宽的分配和管理。
这些年来,不同的技术发展很 快,IETF(Internet Engineering Task Force)已经发布了几种标准,包括:综合服务、区分服务、资源预留技术。这些标 准都在Linux下有了实现。但它们的使用是个综合的问题,需要网络其他路由器的配合(例如资源预留),所以当前在实践中使用不是很广,但随着因特网业务 的不断增加,它们会逐渐越来越多的被大家使用的。
Linux内核2.1.x及后续版本里面主要是引入了traffic control代码,来实现IP带宽的分配和管理。
TC的特点
TC 是Traffic Control的缩写,中文意思即为"流量控制"。TC有很大的伸缩性。作为一个提供虚拟主机服务的ISP来说,它可以利用Linux 的流量控制来给不同的客户不供的服务质量保证。传统的出售虚拟主机或者提供主页存放服务的ISP通常是提供不同的磁盘空间来作为不同档次的服务,例如一个 月100元可以获得100M的空间。如果使用Linux的流量控制(TC),我们就可以多提供一种有区分的不同的服务,例如你是一个提供虚拟主机服务的 ISP,你可以有如下多的不同的服务选择:
· 服务水平1: 花费¥100/月――浏览者获得250Kbps的浏览页面速度。
· 服务水平2: 花费¥150/月--250Kbps,但是如果带宽允许的话在午夜0点 到临晨6点之间可以达到1Mbps。
· 服务水平3: 花费¥200/月--250Kbps, 但是如果带宽允许的话可以最大达到1Mbps,不限制时间(与服务水平2区别之处)。
· 服务水平4:花费¥500/月--提供1Mbps的高速的带宽保证。适合视频和音频流的服务。
由上面的例子,我们可以利用TC很方便的指定各种有控制的服务规则。上面提到的每天不同时段提供不同的服务,我们可以很容易的用crontab来定时运行一些脚本去改变带宽分配规则来达到。这是一个比较好的例子,我们在后面将介绍一个实例。
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别名,将两个地址同时绑在一个接口上。我们可以用ftp来 测试。首先用ftp连接10.0.0.10服务器,我们可以获得大概1Mbps的速率;然后ftp另外的地址10.0.0.11,我们可以获得大概 3Mbps的速率。