Linux的高级路由和流量控制HOWTO 1 2.docx
《Linux的高级路由和流量控制HOWTO 1 2.docx》由会员分享,可在线阅读,更多相关《Linux的高级路由和流量控制HOWTO 1 2.docx(13页珍藏版)》请在冰豆网上搜索。
![Linux的高级路由和流量控制HOWTO 1 2.docx](https://file1.bdocx.com/fileroot1/2023-6/30/7aacde17-41bd-4118-b58c-4289357df0b4/7aacde17-41bd-4118-b58c-4289357df0b41.gif)
Linux的高级路由和流量控制HOWTO12
Linux的高级路由和流量控制HOWTO12
Linux的高级路由和流量控制HOWTO(1.2)2010-07-1614:
28第4章规则--路由策略数据库
如果你有一个大规模的路由器,你可能不得不同时满足不同用户对于路由的不同需求。
路由策略数据库可以帮助你通过多路由表技术来实现。
如果你想使用这个特性,请确认你的内核配置中带有"IP:
advancedrouter"和"IP:
policyrouting"两项。
当内核需要做出路由选择时,它会找出应该参考哪一张路由表。
除了"ip"命令之外,以前的"route"命令也能修改main和local表。
缺省规则:
[ahu@homeahu]$iprulelist0:
fromalllookuplocal32766:
fromalllookupmain32767:
fromalllookupdefault
上面列出了规则的优先顺序。
我们看到,所有的规则都应用到了所有的包上("fromall")。
我们前面已经看到了"main"表,就是"iproutels"命令的输出,但是"local"和"default"是初次见到。
如果我们想做点有趣的事情,就可以生成一些指向不同路由表的规则,取代系统中的路由规则。
对于内核如何处理一个IP包匹配多个规则的精确意义,请参见Alexey关于ip-cref文档。
4.1.简单的源策略路由
让我们再来一个真实的例子。
我有两个CableModem,连接到了一个Linux的NAT("伪装")路由器上。
这里的室友们向我付费使用Internet。
假如我其中的一个室友因为只想访问hotmail而希望少付一些钱。
对我来说这没有问题,他们肯定只能使用那个比较次的CableModem。
那个比较快的cablemodem的IP地址是212.64.94.251,PPP链路,对端IP是212.64.94.1。
而那个比较慢的cablemodem的IP地址是212.64.78.148,对端是195.96.98.253。
local表:
[ahu@homeahu]$iproutelisttablelocalbroadcast127.255.255.255devloprotokernelscopelinksrc127.0.0.1local10.0.0.1deveth0protokernelscopehostsrc10.0.0.1broadcast10.0.0.0deveth0protokernelscopelinksrc10.0.0.1local212.64.94.251devppp0protokernelscopehostsrc212.64.94.251broadcast10.255.255.255deveth0protokernelscopelinksrc10.0.0.1broadcast127.0.0.0devloprotokernelscopelinksrc127.0.0.1local212.64.78.148devppp2protokernelscopehostsrc212.64.78.148local127.0.0.1devloprotokernelscopehostsrc127.0.0.1local127.0.0.0/8devloprotokernelscopehostsrc127.0.0.1
有很多明显的事实,其实可能还需要进一步说明。
好了,这样就行了。
"default"表为空。
让我们看看"main"路由表:
[ahu@homeahu]$iproutelisttablemain195.96.98.253devppp2protokernelscopelinksrc212.64.78.148212.64.94.1devppp0protokernelscopelinksrc212.64.94.25110.0.0.0/8deveth0protokernelscopelinksrc10.0.0.1127.0.0.0/8devloscopelinkdefaultvia212.64.94.1devppp0
我们现在为我们的朋友创建了一个叫做"John"的规则。
其实我们完全可以使用纯数字表示规则,但是不方便。
我们可以向/etc/iproute2/rt_tables文件中添加数字与名字的关联:
#echo200John/etc/iproute2/rt_tables
#ipruleaddfrom10.0.0.10tableJohn
#iprulels0:
fromalllookuplocal32765:
from10.0.0.10lookupJohn32766:
fromalllookupmain32767:
fromalllookupdefault
现在,剩下的事情就是为John的路由表创建路由项了。
别忘了刷新路由缓存:
#iprouteadddefaultvia195.96.98.253devppp2tableJohn
#iprouteflushcache
这样就做好了。
至于如何在ip-up阶段实现就留给读者自己去研究吧。
4.2.多重上连ISP的路由
下图是很常见的配置,同一个局域网(甚至是同一台计算机)通过两个ISP连接到互联网上。
_
+--+/
|||
+---+ISP1+---
__|||/
_/_+--+---++--+|
_/__|if1|/
/|||
|局域网---+Linux路由器||国际互联网
___/|||
____/|if2|
_/+--+---++--+|
|||
+---+ISP2+---
|||
+--+_
这种情况下通常会出现两个问题。
4.2.1.流量分割
首先是如何保证:
回应来自某一个ISP的数据包时,仍然使用相同的ISP。
让我们先定义一些符号。
令第一块网卡(上图的if1)的名字叫$IF1,而第二块网卡叫做$IF2。
然后设置$IF1的IP地址为$IP1,$IF2的IP地址为$IP2。
并且,令ISP1的网关地址为$P1,ISP2的网关地址为$P2。
最后,令$P1的网络地址为$P1_NET,令$P2的网络地址为$P2_NET。
额外创建两个路由表,T1和T2。
加入到/etc/iproute2/rt_tables中。
然后如下设置两个路由表中的路由:
iprouteadd$P1_NETdev$IF1src$IP1tableT1iprouteadddefaultvia$P1tableT1iprouteadd$P2_NETdev$IF2src$IP2tableT2iprouteadddefaultvia$P2tableT2
没什么大不了的,不过是建立了通向该网关的一条路由,并使之成为默认网关,分别负责一个单独的上行流,并且为这两个ISP都作这样的配置。
要指出的是,那条网络路由是必要条件,因为它能够让我们找到那个子网内的主机,也包括上述那台网关。
下一步,我们设置"main"路由表。
把包通过网卡直接路由到与网卡相连的局域网上不失为一个好办法。
要注意"src"参数,他们能够保证选择正确的出口IP地址。
iprouteadd$P1_NETdev$IF1src$IP1iprouteadd$P2_NETdev$IF2src$IP2
然后,设置你的缺省路由:
iprouteadddefaultvia$P1
接着,设置路由规则。
这实际上在选择用什么路由表进行路由。
你需要确认当你从一个给定接口路由出数据包时,是否已经有了相应的源地址:
你需要保证的就是如果你已经有了相应的源地址,就应该把数据包从相应的网卡路由出去:
ipruleaddfrom$IP1tableT1ipruleaddfrom$IP2tableT2
以上命令保证了所有的回应数据都会从他们来的那块网卡原路返回。
现在,完成了非常基本的配置。
这将对于所有运行在路由器上所有的进程起作用,实现IP伪装以后,对本地局域网也将起作用。
如果不进行伪装,那么你要么拥有两个ISP的地址空间,要么你想对两个ISP中的一个进行伪装。
无论哪种情况,你都要添加规则,基于发包的主机在局域网内的IP地址,选择从哪个ISP路由出去。
4.2.2.负载均衡
第二个问题是如何对于通过两个ISP流出的数据进行负载均衡。
如果你已经成功地实现了流量分割,这件事并不难。
与选择两个ISP中的一个作为缺省路由不同,这次是设置缺省路由为多路路由。
在缺省内核中,这会均衡两个ISP的路由。
象下面这样做(基于前面的流量分割实验):
iprouteadddefaultscopeglobalnexthopvia$P1dev$IF1weight1
nexthopvia$P2dev$IF2weight1
这样就可以均衡两个ISP的路由。
通过调整"weight"参数我们可以指定其中一个ISP的优先权高于另一个。
应该指出,由于均衡是基于路由进行的,而路由是经过缓冲的,所以这样的均衡并不是100%精确。
也就是说,对于一个经常访问的站点,总是会使用同一个ISP。
进而,如果你对此不满意,你可能需要参考以下JulianAnastasov的内核补丁:
Julian的路由补丁会弥补上述缺陷。
第5章GRE和其他隧道
Linux有3种隧道。
它们是:
IP-in-IP隧道、GRE隧道和非内核隧道(如PPTP)。
5.1.关于隧道的几点注释
隧道可以用于实现很多非常不一般而有趣的功能。
但如果你的配置有问题,却也会发生可怕的错误。
除非你确切地知道你在做什么,否则不要把缺省路由指向一个隧道设备。
而且,隧道会增加协议开销,因为它需要一个额外的IP包头。
一般应该是每个包增加20个字节,所以如果一个网络的MTU是1500字节的话,使用隧道技术后,实际的IP包长度最长只能有1480字节了。
这倒不是什么原则性的问题,但如果你想使用隧道技术构建一个比较大规模的网络的话,最好仔细研究一下关于IP包的分片和汇聚的知识。
哦,还有,挖一个隧道最好的方法当然是同时从两头挖。
5.2.IP-in-IP隧道
这种隧道在Linux上已经实现很长一段时间了。
需要两个内核模块:
ipip.o和new_tunnel.o。
比如说你有3个网络:
内部网A和B,中间网C(比如说:
Internet)。
A网络的情况:
网络地址10.0.1.0
子网掩码255.255.255.0
路由器10.0.1.1
路由器在C网络上的地址是172.16.17.18。
B网络的情况:
网络地址10.0.2.0
子网掩码255.255.255.0
路由器10.0.2.1
路由器在C网络上的IP地址是172.19.20.21。
已知C网络已经连通,我们假定它会将所有的数据包从A传到B,反之亦然。
而且你可以随便使用Internet。
这就是你要做的:
首先,确认模块是否加载:
insmodipip.oinsmodnew_tunnel.o
然后,在A网络的路由器上输入:
ifconfigtunl010.0.1.1pointopoint172.19.20.21routeadd-net10.0.2.0netmask255.255.255.0devtunl0
并且在B网络的路由器上输入:
ifconfigtunl010.0.2.1pointopoint172.16.17.18routeadd-net10.0.1.0netmask255.255.255.0devtunl0
如果你想中止隧道,输入:
ifconfigtunl0down
简单之极!
但是你不能通过IP-in-IP隧道转发广播或者IPv6数据包。
你只是连接了两个一般情况下无法直接通讯的IPv4网络而已。
至于兼容性,这部分代码已经有很长一段历史了,它的兼容性可以上溯到1.3版的内核。
据我所知,Linux的IP-in-IP隧道不能与其他操作系统或路由器互相通讯。
它很简单,也很有效。
需要它的时候尽管使用,否则就使用GRE。
5.3.GRE隧道
GRE是最初由CISCO开发出来的隧道协议,能够做一些IP-in-IP隧道做不到的事情。
比如,你可以使用GRE隧道传输多播数据包和IPv6数据包。
在Linux下,你需要ip_gre.o模块。
5.3.1.IPv4隧道
让我们先来做一做IPv4隧道:
比如说你有3个网络:
内部网A和B,中间网C(比如说:
Internet)。
A网络的情况:
网络地址10.0.1.0
子网掩码255.255.255.0
路由器10.0.1.1
路由器在C网络上的地址是172.16.17.18。
我们称之为neta。
B网络的情况:
网络地址10.0.2.0
子网掩码255.255.255.0
路由器10.0.2.1
路由器在C网络上的IP地址是172.19.20.21。
我们称之为netb。
已知C网络已经连通,我们假定它会将所有的数据包从A传到B,反之亦然。
至于原因,我们不考虑。
在A网络的路由器上,输入:
iptunneladdnetbmodegreremote172.19.20.21local172.16.17.18ttl255iplinksetnetbupipaddradd10.0.1.1devnetbiprouteadd10.0.2.0/24devnetb
让我们稍微讨论一下。
第1行,我们添加了一个隧道设备,并且称之为netb(为了能够表示出这个隧道通向哪里)。
并且表示要使用GRE协议(modegre),对端地址是172.19.20.21(另一端的路由器),我们的隧道数据包发源于172.16.17.18(以便当你的路由器在C网络中拥有多个地址的时候,你可以指定哪一个应用于隧道)并且包的TTL字段应设置为255(ttl255)。
第2行,启用该隧道。
第3行,我们给这个新生的网卡配置了一个IP:
10.0.1.1。
对于小网络来说足够了,但如果你网络中的隧道多得象无证运营的小煤窑一样,你可能就要考虑给你的隧道规划一个单独的IP地址范围(在本例中,你可以使用10.0.3.0)。
第4行,我们为B网络设置了一条路由。
注意子网掩码的另一种表示方法。
如果你不熟悉这种表示,我就来解释一下:
你把你的子网掩码写成二进制形式,数数里面由多少个1。
如果你连这个也不会做,不妨就简单地记住:
255.0.0.0就是/8,255.255.0.0就是/16,255.255.255.0就是/24。
让我们再看看B网络的路由器。
iptunneladdnetamodegreremote172.16.17.18local172.19.20.21ttl255iplinksetnetaupipaddradd10.0.2.1devnetaiprouteadd10.0.1.0/24devneta
如果你想从A路由器中停止隧道,输入:
iplinksetnetbdowniptunneldelnetb
当然,你可以把netb换成neta,在B路由器上操作。
5.3.2.IPv6隧道
关于IPv6地址,请参看第6章第1节。
这就开始吧。
我们假设你有如下的IPv6网络,你想把它连接到6bone或者一个朋友那里。
Network3ffe:
406:
5:
1:
5:
a:
2:
1/96
你的IPv4地址是172.16.17.18,6bone路由器的IPv4地址是172.22.23.24。
iptunneladdsixbonemodesitremote172.22.23.24local172.16.17.18ttl255iplinksetsixboneupipaddradd3ffe:
406:
5:
1:
5:
a:
2:
1/96devsixboneiprouteadd3ffe:
/15devsixbone
让我们来讨论一下。
我们创建了一个叫做sixbone的隧道设备。
我们设置它的模式是sit(也就是在IPv4隧道中使用IPv6)并且告诉它对端(remote)和本端(local)在哪里。
TTL设置为最大,255。
接着,我们激活了这个设备(up)。
然后,我们添加了我们自己的网络地址,并添加了一条通过隧道去往3ffe:
/15(现在全部属于6bone)的路由。
GRE隧道是现在最受欢迎的隧道技术。
它也广泛地应用于Linux世界之外并成为一个标准,是个好东西。
5.4.用户级隧道
在内核之外,还有很多实现隧道的方法,最闻名的当然要数PPP和PPTP,但实际上还有很多(有些是专有的,有些是安全的,有些甚至根本不用IP),但那远远超出了本HOWTO所涉及的范围。
第6章用Cisco和6bone实现IPv6隧道
MarcoDavidsmarco@sara.nl著
NOTEtomaintainer:
AsfarasIamconcerned,thisIPv6-IPv4tunnelingisnotperdefinitionGREtunneling.YoucouldtunnelIPv6overIPv4bymeansofGREtunneldevices(GREtunnelsANYtoIPv4),butthedeviceusedhere("sit")onlytunnelsIPv6overIPv4andisthereforesomethingdifferent.
6.1.IPv6隧道
这是Linux隧道能力的另一个应用。
这在IPv6的早期实现中非常流行。
下面动手试验的例子当然不是实现IPv6隧道的唯一方法。
然而,它却是在Linux与支持IPv6的CISCO路由器之间搭建隧道的常用方法,经验证明多数人都是照这样做的。
八成也适合于你.。
简单谈谈IPv6地址:
相对于IPv4地址而言,IPv6地址非常大,有128bit而不是32bit。
这让我们得到了我们需要的东西--非常非常多的IP地址。
确切地说,有340,282,266,920,938,463,463,374,607,431,768,211,465个。
同时,IPv6(或者叫Ipng,下一代IP)还能让Internet上的骨干路由器的路由表变得更小、设备的配置更简单、IP层的安全性更好以及更好地支持QoS。
例如:
2002:
836b:
9820:
0000:
0000:
0000:
836b:
9886
写下一个IPv6地址确实是件麻烦事。
所以我们可以使用如下规则来进行简化:
.数字打头的零不要写,就像IPv4一样。
.每16bit或者两个字节之间使用冒号分隔。
.当出现很多连续的零时可简写成":
"。
在一个地址中只能使用一次。
例如:
地址2002:
836b:
9820:
0000:
0000:
0000:
836b:
9886可以写成:
2002:
836b:
9820:
836b:
9886,看上去更简单些。
另一个例子:
地址3ffe:
0000:
0000:
0000:
0000:
0020:
34A1:
F32C可以写成3ffe:
20:
34A1:
F32C,要短得多。
IPv6将可能取代现有的IPv4。
因为它采用了相对更新的技术,所以现在还没有全球范围的IPv6网络。
为了能够平滑地过渡,引入了6bone计划。
IPv6网络中的站点通过现有的IPv4体系互联,把IPv6数据包封装在IPv4数据包中进行传输。
这就是为什么引入隧道机制的原因。
为了能够使用IPv6,我们需要一个能够支持它的内核。
现在有很多文档都很好地说明了这个问题。
不外乎以下几步:
.找到一个新版的Linux发行版,要有合适的glibc库。
.找到一份最新的内核源代码。
都准备好了以后,就可以继续编译一个带IPv6支持的内核了:
.cd/usr/src/linux
.makemenuconfig
.选择"NetworkingOptions"
.选择"TheIPv6protocol"、"IPv6:
enableEUI-64tokenformat","IPv6:
disableproviderbasedaddresses"
提示:
不要编译成内核模块,那样经常会出问题。
换句话说,就是把IPv6内置入内核。
然后你就可以象往常一样保存配置并编译内核了。
提示:
在编译之前,可以修改一下Makefile,把EXTRAVERSION=-x变成EXTRAVERSION=-x-IPv6
有很多文档都很好地说明了如何编译并安装一个内核,我们这篇文档不是讨论这个问题的。
如果你在这个过程中出现了问题,请参阅合适的资料。
你可以先看看/usr/src/linux/README。
当你完成之后,用新的内核重启系统,你可以输入"/sbin/ifconfig-a"看看有没有新的"sit0-device"设备。
SIT的意思是"简单Internet过渡"(SimpleInternetTransition)。
如果到这里没有问题,你就可以奖励自己了,你已经向着下一代IP网络迈进了一大步。
现在继续下一步。
你需要把你的主机,或甚至整个局域网连接到另外一个IPv6网络上。
这个网络很可能是"6bone",它就是为了这个特定的目的而专门设立的。
让我们假定你有如下IPv6网络:
3ffe:
604:
6:
8:
/64,并且希望连接到6bone,或者其他地方。
请注意,/64这个子网声明的意义与IPv4相同。
你的IPv4地址是145.100.24.181,6bone的路由器的IPv4地址是145.100.1.5。
#iptunneladdsixbonemodesitremote145.100.1.5[local145.100.24.181ttl255]
#iplinksetsixboneup
#ipaddradd3FFE:
604:
6:
7:
2/126devsixbone
#iprouteadd3ffe:
0/16devsixbone
让我们讨论一下。
第1行,我们创建了一个叫做sixbone的隧道。
设置为sit(让IPv4隧道承载IPv6数据包)模式,并设置对端与本端IP。
TTL设为最大--255。
下一步,我们激活(up)了这个设备。
然后添加我们自己的网络地址,并设置利用隧道通往3ffe:
/15(whichiscurrentlyallof6bone)的路由。
如果你运行这个的这台机器是你的IPv6网关,就得考虑运行下面的命令:
#echo1/proc/sys/net/ipv6/conf/all/forwarding
#/usr/local/sbin/radvd
下面的一行,radvd是一个类似于zebra的路由公告守护程序,用来支持IPv6的自动配置特性。
如果感兴趣的话就用你最喜欢的搜索引擎找一找。
你可以检查一下:
#/sbin/ip-finet6addr
如果你的Linux网关支持IPv6且运行了radvd,在局域网上启动后,你就可以享受IPv6的自动配置特性了:
#/s