Linux的高级路由和流量控制HOWTO中文版1BertHubertNetherlabsBVbert.
hubert@netherlabs.
nlGregoryMaxwell(章节作者)remco%virtu.
nlRemcovanMook(章节作者)remco@virtu.
nlMartijnvanOosterhout(章节作者)kleptog@cupid.
suninternet.
comPaulBSchroeder(章节作者)paulsch@us.
ibm.
comJasperSpaans(章节作者)jasper@spaans.
ds9a.
nlPedroLarroy(章节作者)piotr%omega.
resa.
ed一个非常上手的关于iproute2、流量整形和一点netfilter的指南.
2译者序可以说,翻译这篇文档的过程就是我重新学习Linux的过程.
与原文的作者感受相似,当我根据这篇文档大致了解了Linux在IP方面的功能后,绝对是"itreallyblewmeaway!
".
才发现我以前一直是把Linux当成UNIX来用,Linux本身很多精彩的功能都被我忽略了.
看来Linux在路由方面的设计的确非常独到.
但愿这篇文章的内容能够对您应用Linux有所帮助.
本文档的原作实际上还尚未完成,估计要等到Linux的2.
6版内核发布之后才能最终定稿.
但是我已经等不及了,非常希望尽快与各位共享这篇文档.
如果这篇文档的原作完成,我会尽力追踪翻译.
这里是本HOWTO的正规出处.
由于本人的英语和语文都是业余水平,有两三处晦涩或者与技术无关的内容没有翻译,希望英语高手予以指点.
如有词不达意甚至理解错误之处,非常渴望您能通过Email告知!
谢谢!
2/15/20035:28PM译毕JohnBull3目录第1章贡献1第2章简介22.
1.
除外责任与许可22.
2.
预备知识22.
3.
LINUX能为你做什么32.
4.
内务声明32.
5.
访问、CVS和提交更新42.
6.
邮件列表42.
7.
本文档的布局4第3章介绍IPROUTE263.
1为什么使用IPROUTE263.
2IPROUTE2概览63.
3先决条件63.
4浏览你的当前配置73.
4.
1.
让ip显示我们的链路73.
4.
2.
让ip显示我们的IP地址73.
4.
3.
让ip显示路由83.
5.
ARP9第4章规则——路由策略数据库114.
1.
简单的源策略路由114.
2.
多重上连ISP的路由124.
2.
1.
流量分割134.
2.
2.
负载均衡14第5章GRE和其他隧道155.
1.
关于隧道的几点注释155.
2.
IP-IN-IP隧道155.
3.
GRE隧道1645.
3.
1.
IPv4隧道165.
3.
2.
IPv6隧道185.
4.
用户级隧道18第6章用CISCO和6BONE实现IPV6隧道196.
1.
IPV6隧道19第7章IPSEC:INTERNET上安全的IP227.
1.
从手动密钥管理开始227.
2.
自动密钥管理257.
2.
1.
理论267.
2.
2.
举例267.
2.
3.
使用X.
509证书进行自动密钥管理297.
3.
IPSEC隧道327.
4.
其它IPSEC软件337.
5.
IPSEC与其它系统的互操作337.
5.
1.
Windows33第8章多播路由34第9章带宽管理的队列规定369.
1.
解释队列和队列规定369.
2.
简单的无类队列规定379.
2.
1.
pfifo_fast379.
2.
2.
令牌桶过滤器(TBF)399.
2.
3.
随机公平队列(SFQ)419.
3.
关于什么时候用哪种队列的建议429.
4.
术语439.
5.
分类的队列规定459.
5.
1.
分类的队列规定及其类中的数据流向459.
5.
2.
队列规定家族:根、句柄、兄弟和父辈459.
5.
3.
PRIO队列规定469.
5.
4.
著名的CBQ队列规定489.
5.
5.
HTB(HierarchicalTokenBucket,分层的令牌桶)5459.
6.
使用过滤器对数据包进行分类559.
6.
1.
过滤器的一些简单范例569.
6.
2.
常用到的过滤命令一览579.
7.
IMQ(INTERMEDIATEQUEUEINGDEVICE,中介队列设备)589.
7.
1.
配置范例58第10章多网卡的负载均衡6010.
1.
告诫6110.
2.
其它可能性61第11章NETFILTER和IPROUTE——给数据包作标记62第12章对包进行分类的高级过滤器6412.
1.
U32分类器6512.
1.
1.
U32选择器6512.
1.
2.
普通选择器6612.
1.
3.
特殊选择器6712.
2.
路由分类器6712.
3.
管制分类器6812.
3.
1.
管制的方式6812.
3.
2.
越限动作6912.
3.
3.
范例7012.
4.
当过滤器很多时如何使用散列表70第13章内核网络参数7213.
1.
反向路径过滤7213.
2.
深层设置7313.
2.
1.
ipv4一般设置7313.
2.
2.
网卡的分别设置7813.
2.
3.
邻居策略7913.
2.
4.
路由设置80第14章不经常使用的高级队列规定8214.
1.
BFIFO/PFIFO8214.
1.
1.
参数与使用82614.
2.
CLARK-SHENKER-ZHANG算法(CSZ)8214.
3.
DSMARK8314.
3.
1.
介绍8314.
3.
2.
Dsmark与什么相关8314.
3.
3.
DifferentiatedServices指导8414.
3.
4.
使用Dsmark8414.
3.
5.
SCH_DSMARK如何工作8414.
3.
6.
TC_INDEX过滤器8514.
4.
入口队列规定8714.
4.
1.
参数与使用8714.
5.
RED(RANDOMEARLYDETECTION,随机提前检测)8714.
6.
GRED(GENERICRANDOMEARLYDETECTION,一般的随机提前检测)8814.
7.
VC/ATM模拟8914.
8.
WRR(WEIGHTEDROUNDROBIN,加权轮转)89第15章方便菜谱9015.
1.
用不同的SLA运行多个网站.
9015.
2.
防护SYN洪水攻击9015.
3.
为防止DDOS而对ICMP限速9115.
4.
为交互流量设置优先权9215.
5.
使用NETFILTER、IPROUTE2和SQUID实现WEB透明代理9315.
5.
1.
实现之后的数据流图9615.
6.
与PMTU发现有关的"基于路由的MTU设置"9615.
6.
1.
解决方案9715.
7.
与PMTU发现有关的MSS箝位(给ADSL,CABLE,PPPOE和PPTP用户)9815.
8.
终极的流量控制:低延迟、高速上/下载9815.
8.
1.
为什么缺省设置不让人满意9915.
8.
2.
实际的脚本(CBQ)10015.
8.
3.
实际的脚本(HTB)10215.
9.
为单个主机或子网限速10315.
10.
一个完全NAT和QOS的范例104715.
10.
1.
开始优化那不多的带宽10415.
10.
2.
对数据包分类10615.
10.
3.
改进设置10715.
10.
4.
让上面的设置开机时自动执行108第16章构建网桥以及用ARP代理构建伪网桥10916.
1.
桥接与IPTABLES的关系10916.
2.
桥接与流量整形10916.
3.
用ARP代理实现伪网桥10916.
3.
1.
ARP和ARP代理11016.
3.
2.
实现110第17章动态路由——OSPF和BGP11217.
1.
用ZEBRA设置OSPF11217.
1.
1.
必要条件11317.
1.
2.
配置Zebra11317.
1.
3.
运行Zebra115第18章其它可能性117第19章进一步学习119第20章鸣谢1208第1章贡献本文档的成形得益于很多人的贡献,我希望能够回报他们.
列出其中几个:RustyRussellAlexeyN.
Kuznetsov来自Google的一些好心人CasemaInternet的工作人员1第2章简介欢迎,亲爱的读者.
希望这篇文档能对你更好地理解Linxs2.
2/2.
4的路由有所帮助和启发.
不被大多数使用者所知道的是,你所使用工具,其实能够完成相当规模工作.
比如route和ifconfig,实际上暗中调用了非常强大的iproute2的底层基本功能.
我希望这个HOWTO能够象RustyRussell的作品那样通俗易懂.
你可以随时给HOWTO工作组发电子邮件来找到我们.
但是如果您的问题并不直接与这个HOWTO文档相关,请首先考虑发给邮件列表(参考相关章节).
我们可不是免费的帮助平台,但我们经常会在邮件列表上回答问题.
在钻研这个HOWTO之前,如果您想做的只是一点简单的流量整形,不妨直接去看看其它可能性这一章里面的CBQ.
init.
2.
1.
除外责任与许可这个文档依着对公众有利用价值的目的而发布,但不提供任何担保,即使是在经销或者使用在特定场合时的潜在担保.
简单地说,如果您的STM-64骨干网瘫痪,并向您尊敬的客户们散布黄色图片,对不起,那绝对不关我的事.
Copyright(c)2002所有:berthubert、GregoryMaxwell、MartijnvanOosterhout、RemcovanMook、PaulB.
Schroeder等等.
这份材料可以在遵从OpenPublicationLicense,v1.
0(或更新版)各项条款的前提下发布.
OpenPublicationLicense的最新版可以在http://www.
opencontent.
org/openpub/得到.
请随意复制并发布(出售或者赠送)本文档,格式不限.
只是请求将纠正和/或注解转发给文档的维护者.
还希望如果你出版本HOWTO的硬拷贝,请给作者们发一份以备复习之用.
2.
2.
预备知识就像标题所暗示的,这是一个"高级"HOWTO.
虽然它不是终极的航天科技,但还是要求一定的基础知识.
这里是一些可能对你有帮助的参考文献:2RustyRussell的networking-concepts-HOWTO非常精彩的介绍,解释了什么是网络以及一个网络如何与其它网络互联.
LinuxNetworking-HOWTO(以前叫做Net-3HOWTO)好东西,虽然非常冗长.
它讲授的内容就是你连接到Internet所需的的配置内容.
应该在/usr/doc/HOWTO/NET3-4-HOWTO.
txt中,也可以在线阅读.
2.
3.
Linux能为你做什么一个小列表:管制某台计算机的带宽管制通向某台计算机的带宽帮助你公平地共享带宽保护你的网络不受DoS攻击保护Internet不受到你的客户的攻击把多台服务器虚拟成一台,进行负载均衡或者提高可用性限制对你的计算机的访问限制你的用户访问某些主机基于用户账号(没错!
)、MAC地址、源IP地址、端口、服务类型、时间或者内容等条件进行路由.
现在,很多人都没有用到这些高级功能.
这有很多原因.
比如提供的文档过于冗长而且不容易上手,而且流量控制甚至根本就没有归档.
2.
4.
内务声明关于这个文档有些事情要指出.
当我写完这个文档的绝大部分的时候,我真的不希望它永远就是那个样子.
我是一个坚信开放源代码的人,所以我希望你能够给我发回反馈、更新、补丁等等.
所以你应该尽可以告知我你的手稿或者指出一些哪怕是无关紧要的错误,不必犹豫.
如果我的英语有些晦涩,请原谅那不是我的母语,尽可以给我建议.
如果你认为自己更有资格维护某个章节,或者认为自己可以写作并维护一个新的章节,请您一定不要客气.
这个HOWTO的SGML可以通过CVS得到,我估计肯定有很多人还在为它出力.
作为请求援助,你会在文档中发现很多"求助"的字样.
我们永远欢迎您的补丁!
无论您在哪里发现"求助",都应该明白您正在踏入一个未知的领域.
这并不是说在别的地方就没有错误,但您应该倍加小心.
如果您确认了某些事情,请您一3定通知我们,以便我们能够把"求助"的标记去掉.
关于这个HOWTO,Iwilltakesomelibertiesalongtheroad.
Forexample,Ipostulatea10MbitInternetconnection,whileIknowfullwellthatthosearenotverycommon.
2.
5.
访问、CVS和提交更新本HOWTO的规范位置在这里.
我们现在向全球开放了匿名CVS访问.
从各个角度来说这都是一件好事.
你可以轻松地升级到本HOWTO的最新版本,而且提交补丁也不再成为问题.
另外的好处是,这可以让作者在源码上独立地继续工作.
$exportCVSROOT=:pserver:anon@outpost.
ds9a.
nl:/var/cvsroot$cvsloginCVSpassword:[enter'cvs'(without's)]$cvsco2.
4routingcvsserver:Updating2.
4routingU2.
4routing/lartc.
db如果您做了修改并希望投稿,运行:cvs-z3diff–uBb然后把输出用电子邮件发给,我们就可以很轻松地把它集成进去了.
谢谢!
请确认你修改的是.
db文件,其它文件都是通过它生成的.
提供了一个Makefile帮助您生成postscript、dvi、pdf、html和纯文本格式的文件.
你可能需要安装docbook、docboot-utils、ghostscript和tetex等等支持软件才能生成各种格式的文本.
注意,不要更改2.
4routing.
sgml!
那里面有旧版本的HOWTO.
正确的文件是lartc.
db.
2.
6.
邮件列表作者已经开始收到关于这个HOWTO越来越多的邮件了.
为了把大家的兴趣条理化,已经决定启动一个邮件列表,让大家在那里互相探讨有关高级路由和流量控制的话题.
你可以在这里进行订阅.
需要指出的是,作者们对于列表中没有问及的问题不可能及时回答.
我们愿意让列表的归档成为一个知识库.
如果你有问题,请搜索归档,然后在post到邮件列表里.
2.
7.
本文档的布局我们几乎马上就要做一些有趣的实验,也就意味着最开始部分的基本概念解释并不完整或者不完善,请您不必管它,后面会一点点说清楚.
4路由和包过滤是完全不同的概念.
关于过滤的问题,Rusty的文档说得很清楚,你可以在这里找到:Rusty出色的不可靠指南我们则将致力于netfilter与iproute2相结合后能做什么.
5第3章介绍iproute23.
1为什么使用iproute2现在,绝大多数Linux发行版和绝大多数UNIX都使用古老的arp,ifconfig和route命令.
虽然这些工具能够工作,但它们在Linux2.
2和更高版本的内核上显得有一些落伍.
比如,现在GRE隧道已经成为了路由的一个主要概念,但却不能通过上述工具来配置.
使用了iproute2,隧道的配置与其他部分完全集成了.
2.
2和更高版本的Linux内核包含了一个经过彻底重新设计的网络子系统.
这些新的代码让Linux在操作系统的竞争中取得了功能和性能上的优势.
实际上,Linux新的路由、过滤和分类代码,从功能和性能上都不弱于现有的那些专业的路由器、防火墙和流量整形产品.
随着新的网络概念的提出,人们在现有操作系统的现有体系上修修补补来实现他们.
这种固执的行为导致了网络代码中充斥着怪异的行为,这有点像人类的语言.
过去,Linux模仿了SunOS的许多处理方式,并不理想.
这个新的体系则有可能比以往任何一个版本的Linux都更善于清晰地进行功能表达.
3.
2iproute2概览Linux有一个成熟的带宽供给系统,称为TrafficControl(流量控制).
这个系统支持各种方式进行分类、排序、共享和限制出入流量.
我们将从iproute2各种可能性的一个简要概览开始.
3.
3先决条件你应该确认已经安装了用户级配置工具.
这个包的名字在RedHat和Debian中都叫作"iproute",也可以在这个地方找到:ftp://ftp.
inr.
ac.
ru/ip-routing/iproute2-2.
2.
4-now-ss.
tar.
gz你也可以试试在这里找找最新版本.
iproute的某些部分需要你打开一些特定的内核选项.
应该指出的是,RedHat6.
2及其以前的所有发行版中所带的缺省内核都不带有流量控制所需要的绝大多数功能.
6而RedHat7.
2在缺省情况下能满足所有要求.
另外,确认一下你的内核支持netlink,Iproute2需要它.
3.
4浏览你的当前配置这听上去确实让人惊喜:iproute2已经配置好了!
当前的ifconfig和route命令已经正在使用新的系统调用,但通常使用了缺省参数(真无聊).
新的工具ip成为中心,我们会让它来显示我们的网卡配置.
3.
4.
1.
让ip显示我们的链路[ahu@homeahu]$iplinklist1:lo:mtu3924qdiscnoqueuelink/loopback00:00:00:00:00:00brd00:00:00:00:00:002:dummy:mtu1500qdiscnooplink/ether00:00:00:00:00:00brdff:ff:ff:ff:ff:ff3:eth0:mtu1400qdiscpfifo_fastqlen100link/ether48:54:e8:2a:47:16brdff:ff:ff:ff:ff:ff4:eth1:mtu1500qdiscpfifo_fastqlen100link/ether00:e0:4c:39:24:78brdff:ff:ff:ff:ff:ff3764:ppp0:mtu1492qdiscpfifo_fastqlen10link/ppp你的结果可能有所区别,但上述显示了我家里NAT路由器的情况.
我将只解释输出中并非全部直接相关的部分.
因为并不是所有部分都与我们的话题有关,所以我只会解释输出的一部分.
我们首先看到了loopback接口.
Whileyourcomputermayfunctionsomewhatwithoutone,I'dadviseagainstit.
MTU(最大传输单元)尺寸为3924字节,并且不应该参与队列.
这是因为loopback接口完全是内核想象出来的、并不存在的接口.
现在我们跳过这个无关的接口,它应该并不实际存在于你的机器上.
然后就是两个物理网络接口,一个接在我的cablemodem上,另一个接到我家里的以太网端上.
再下面,我们看见了一个ppp0接口.
应该指出,我们没有看到IP地址.
iproute切断了"链路"和"IP地址"两个概念的直接联系.
当使用IP别名的时候,IP地址的概念显得更加不相关了.
尽管如此,还是显示出了标识以太网卡硬件的MAC地址.
3.
4.
2.
让ip显示我们的IP地址[ahu@homeahu]$ipaddressshow1:lo:mtu3924qdiscnoqueuelink/loopback00:00:00:00:00:00brd00:00:00:00:00:00inet127.
0.
0.
1/8brd127.
255.
255.
255scopehostlo2:dummy:mtu1500qdiscnoop7link/ether00:00:00:00:00:00brdff:ff:ff:ff:ff:ff3:eth0:mtu1400qdiscpfifo_fastqlen100link/ether48:54:e8:2a:47:16brdff:ff:ff:ff:ff:ffinet10.
0.
0.
1/8brd10.
255.
255.
255scopeglobaleth04:eth1:mtu1500qdiscpfifo_fastqlen100link/ether00:e0:4c:39:24:78brdff:ff:ff:ff:ff:ff3764:ppp0:mtu1492qdiscpfifo_fastqlen10link/pppinet212.
64.
94.
251peer212.
64.
94.
1/32scopeglobalppp0这里包含了更多信息.
显示了我们所有的地址,以及这些地址属于哪些网卡.
"inet"表示Internet(IPv4).
还有很多其它的地址类型,但现在还没有涉及到.
让我们先就近看看eth0.
上面说它与IP地址10.
0.
0.
1/8相关联.
这是什么意思呢"/8"表示IP地址表示网络地址的位数.
因为一共是32个bit,所以我们的这个网络有了24bit的主机空间.
10.
0.
0.
1的开始8bit是10.
0.
0.
0,也就是我们的网络地址,我们的子网掩码是255.
0.
0.
0.
其它的bit直接连接在这个网卡上,所以10.
250.
3.
13可以直接通过eth0联络到,就象10.
0.
0.
1一样.
对于ppp0,仍是相同的概念,虽然数字看上去有所不同.
它的地址是212.
64.
94.
251,不带子网掩码.
这意味着这是一个点到点的连接,而且除了212.
64.
94.
251之外的地址是对端的.
当然,还有很多信息.
它还告诉我们这个链路的另一端只有一个地址:212.
64.
94.
1.
/32意思是说没有表示网络的bit.
掌握这些概念是绝对重要的.
如果有问题,不妨先参考以下这个HOWTO文件开头曾经提到的那些文档.
你应该注意到了"qdisc",它是基于对列规范的一个概念.
它在后面会变得很重要.
3.
4.
3.
让ip显示路由好的,现在我们已经知道如何找到10.
x.
y.
z了,然后我们就可以到达212.
64.
94.
1.
但这还不够,我们还得说明如何找到全世界.
可以通过我们的ppp连接找到Internet,212.
64.
94.
1愿意把我们的数据包发给全世界,并把回应的数据包传回给我们.
[ahu@homeahu]$iprouteshow212.
64.
94.
1devppp0protokernelscopelinksrc212.
64.
94.
25110.
0.
0.
0/8deveth0protokernelscopelinksrc10.
0.
0.
1127.
0.
0.
0/8devloscopelinkdefaultvia212.
64.
94.
1devppp0字面的意思相当清楚.
前4行的输出明确地说明了ipaddressshow的意思,最后一行说明了世界的其它部分可以通过我们的缺省网关212.
64.
94.
1找到.
我们通过"via"这个词断定这是一个网关,我们要把数据包交给它.
这就是我们要留心的问题下面列出以前route命令的输出作为参考:[ahu@homeahu]$route-n8KernelIProutingtableDestinationGatewayGenmaskFlagsMetricRefUseIface212.
64.
94.
10.
0.
0.
0255.
255.
255.
255UH000ppp010.
0.
0.
00.
0.
0.
0255.
0.
0.
0U000eth0127.
0.
0.
00.
0.
0.
0255.
0.
0.
0U000lo0.
0.
0.
0212.
64.
94.
10.
0.
0.
0UG000ppp03.
5.
ARPARP是由RFC826所描述的"地址解析协议".
ARP是网络上的计算机在居域网中用来解析另一台机器的硬件地址/位置的时候使用的.
互联网上的机器一般都是通过机器名解析成IP地址来互相找到的.
这就能够解决foo.
com网络能够与bar.
net网络通讯.
但是,仅仅依靠IP地址,却无法得到一台计算机在一个网络中的物理位置.
这时候就需要ARP.
让我们举一个非常简单的例子.
假定我有一个网络,里面有几台机器.
其中的两台在我的子网上,一台叫foo,IP地址是10.
0.
0.
1,另一台叫bar,IP地址是10.
0.
0.
2.
现在,foo想ping一下bar看看是不是正常,但是呢,foo只知道bar的IP地址,却并不知道bar的硬件(MAC)地址.
所以foo在pingbar之前就会先发出ARP询问.
这个ARP询问就像在喊:"Bar(10.
0.
0.
2)!
你在哪里(你的MAC地址是多少)!
"结果这个广播域中的每台机器都能听到foo的喊话,但是只有bar(10.
0.
0.
2)会回应.
Bar会直接给foo发送一个ARP回应,告诉它"Foo(10.
0.
0.
1),我的Mac地址是00:60:94:E9:08:12".
经过这种简单的交谈,机器就能够在局域网中定位它要通话的对象.
Foo会一直使用这个结果,直到它的ARP缓冲忘掉这个结果(在Unix系统上通常是15分钟之后).
现在我们来看一看具体的工作过程.
你可以这样察看你的ARP表(缓冲):[root@espa041/home/src/iputils]#ipneighshow9.
3.
76.
42deveth0lladdr00:60:08:3f:e9:f9nudreachable9.
3.
76.
1deveth0lladdr00:06:29:21:73:c8nudreachable你可以看到,我的机器espa041(9.
3.
76.
41)知道如何找到espa042(9.
3.
76.
42)和espagate(9.
3.
76.
1).
现在让我们往缓冲中添加另一台机器.
[root@espa041/home/paulsch/.
gnome-desktop]#ping-c1espa043PINGespa043.
austin.
ibm.
com(9.
3.
76.
43)from9.
3.
76.
41:56(84)bytesofdata.
64bytesfrom9.
3.
76.
43:icmp_seq=0ttl=255time=0.
9ms---espa043.
austin.
ibm.
compingstatistics---1packetstransmitted,1packetsreceived,0%packetlossround-tripmin/avg/max=0.
9/0.
9/0.
9ms[root@espa041/home/src/iputils]#ipneighshow9.
3.
76.
43deveth0lladdr00:06:29:21:80:20nudreachable9.
3.
76.
42deveth0lladdr00:60:08:3f:e9:f9nudreachable9.
3.
76.
1deveth0lladdr00:06:29:21:73:c8nudreachable由于espa041试图联络espa043,espa043的硬件地址已经添加到ARP缓冲里了.
所以直到espa043的记录失效以前(也就是两个机器间长时间没有通讯),espa041知道如何找到espa043,也就不必频繁地进行ARP询问了.
9现在让我们来删除espa043的ARP缓冲:[root@espa041/home/src/iputils]#ipneighdelete9.
3.
76.
43deveth0[root@espa041/home/src/iputils]#ipneighshow9.
3.
76.
43deveth0nudfailed9.
3.
76.
42deveth0lladdr00:60:08:3f:e9:f9nudreachable9.
3.
76.
1deveth0lladdr00:06:29:21:73:c8nudstale现在espa041已经忘记了espa043的MAC地址,如果下次它要与espa043通讯,需要再次发送ARP询问.
你在espagate(9.
3.
76.
1)上也会发现以上输出已经变成了"stale"状态.
这意味着MAC地址仍然是在册,但是接下来第一次通讯的时候需要确认一下.
10第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]$iproutelisttablelocal11broadcast127.
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+-------12if1|/|局域网-----+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$IP2tableT213以上命令保证了所有的回应数据都会从他们来的那块网卡原路返回.
现在,完成了非常基本的配置.
这将对于所有运行在路由器上所有的进程起作用,实现IP伪装以后,对本地局域网也将起作用.
如果不进行伪装,那么你要么拥有两个ISP的地址空间,要么你想对两个ISP中的一个进行伪装.
无论哪种情况,你都要添加规则,基于发包的主机在局域网内的IP地址,选择从哪个ISP路由出去.
4.
2.
2.
负载均衡第二个问题是如何对于通过两个ISP流出的数据进行负载均衡.
如果你已经成功地实现了流量分割,这件事并不难.
与选择两个ISP中的一个作为缺省路由不同,这次是设置缺省路由为多路路由.
在缺省内核中,这会均衡两个ISP的路由.
象下面这样做(基于前面的流量分割实验):iprouteadddefaultscopeglobalnexthopvia$P1dev$IF1weight1\nexthopvia$P2dev$IF2weight1这样就可以均衡两个ISP的路由.
通过调整"weight"参数我们可以指定其中一个ISP的优先权高于另一个.
应该指出,由于均衡是基于路由进行的,而路由是经过缓冲的,所以这样的均衡并不是100%精确.
也就是说,对于一个经常访问的站点,总是会使用同一个ISP.
进而,如果你对此不满意,你可能需要参考以下JulianAnastasov的内核补丁:http://www.
linuxvirtualserver.
org/~julian/#routesJulian的路由补丁会弥补上述缺陷.
14第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.
115路由器在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.
116路由器在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路由器上操作.
175.
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所涉及的范围.
18第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,要短得多.
19IPv6将可能取代现有的IPv4.
因为它采用了相对更新的技术,所以现在还没有全球范围的IPv6网络.
为了能够平滑地过渡,引入了6bone计划.
IPv6网络中的站点通过现有的IPv4体系互联,把IPv6数据包封装在IPv4数据包中进行传输.
这就是为什么引入隧道机制的原因.
为了能够使用IPv6,我们需要一个能够支持它的内核.
现在有很多文档都很好地说明了这个问题.
不外乎以下几步:找到一个新版的Linux发行版,要有合适的glibc库.
找到一份最新的内核源代码.
都准备好了以后,就可以继续编译一个带IPv6支持的内核了:cd/usr/src/linuxmakemenuconfig选择"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/16devsixbone20让我们讨论一下.
第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的自动配置特性了:#/sbin/ip-finet6addr1:lo:mtu3924qdiscnoqueueinet6::1/128scopehost3:eth0:mtu1500qdiscpfifo_fastqlen100inet63ffe:604:6:8:5054:4cff:fe01:e3d6/64scopeglobaldynamicvalid_lftforeverpreferred_lft604646secinet6fe80::5054:4cff:fe01:e3d6/10scopelink你可以继续进行了,为IPv6配置你的bind.
与A记录等价的,支持IPv6的记录类型是"AAAA".
与in-addr.
arpa等价的是"ip6.
int".
这方面可以找到很多信息.
支持IPv6的应用系统曾在增加,包括ssh、telnet、inetd、Mozilla浏览器、ApacheWEB浏览器…….
但那些都不是这个路由文档所应该涉及的.
作为Cisco系统,应该这样配置:!
interfaceTunnel1descriptionIPv6tunnelnoipaddressnoipdirected-broadcastipv6address3FFE:604:6:7::1/126tunnelsourceSerial0tunneldestination145.
100.
24.
181tunnelmodeipv6ip!
ipv6route3FFE:604:6:8::/64Tunnel1但如果你没有Cisco作为disposal,试试Internet上的众多IPv6隧道提供者之一.
他们愿意在他们的Cisco设备上为你额外创建一个隧道.
大部分是友好的WEB界面.
用你常用的搜索引擎搜索一下"ipv6tunnelbroker".
21第7章IPSEC:Internet上安全的IP现在,在Linux上有两种IPSEC可用.
对于2.
2和2.
4,第一个比较正规的实现有FreeS/WAN.
他们有一个官方站点和一个经常维护的非官方站点.
出于多种原因,FreeS/WAN历来没有被合并到内核的主线上.
最常提到的原因就是政治问题——违反了美国的密码产品扩散条例.
所以它不会被集成到Linux内核中.
另外,很多合作伙伴表明了他们对代码质量的忧虑.
关于如何设置FreeS/WAN,有很多文档可以参考.
Linux2.
5.
47版内核里有一个内置的IPSEC实现,是由AlexeyKuznetsov和DaveMiller在USAGIIPv6小组的启发下写成的.
经过这次合并,JamesMorris的CrypoAPI也成了内核的一部分——它能够真正地加密.
本HOWTO文档仅收录2.
5以上版本内核的IPSEC.
Linux2.
4内核的用户推荐使用FreeS/WAN.
但是要注意,它的配置方法与内置IPSEC不同.
2.
5.
49版内核的IPSEC不再需要任何补丁.
我在这里收集了Alexey或DaveMiller发布的一些补丁.
对于2.
5.
48版的内核,在报告BUG之前请确认已经打上了那些补丁!
(迄今还没有2.
5.
49这方面的补丁).
一些简单的用户级工具可以在这里(编译好的可执行文件和手册)找到.
编译这些用户级工具需要修改Makefiles指向你的2.
5.
x内核.
这种情况可望很快解决.
编译你的内核的时候,要确认已经打开CryptoAPI中的"PF_KEY"、"AH"、"ESP"以及其他所有选项!
netfilter中的TCP_MSS方法现在不能用,请关掉.
本章的作者对于IPSEC完全是外行(nitwit)!
如果你发现了错误,请email通知berthubert.
首先,我们展示一下如何在两个主机之间手动设置安全通讯.
其间的大部分工作都可以自动完成,但是为了了解细节我们仍然用手动完成.
如果你仅仅对自动密钥管理感兴趣,请跳过下面一节.
但是要知道,了解手动密要管理是很有用的.
7.
1.
从手动密钥管理开始22IPSEC是一个复杂的主题.
有很多在线信息可以查阅,这个HOWTO将集中讲解如何设置、运行并解释一些基本原理.
很多iptables配置会丢弃IPSEC数据包!
要想让IPSEC通过,运行iptables-Axxx-p50-jACCEPTiptables-Axxx-p51-jACCEPTIPSEC提供了一个安全的IP协议版本.
所谓的"安全"意味着两件事情:加密与验证.
如果认为安全仅仅就是加密哪就太天真了,很容易看出来那是不够的——你的通讯过程或许是加密的,但是你如何保证与你通讯的对端就是你期望中的人呢IPSEC使用ESP('EncapsulatedSecurityPayload',安全载荷封装)来支持加密,使用AH(AuthenticationHeader,头部验证)来支持对端验证.
你可以同时使用二者,也可以使用二者之一.
ESP和AH都依靠SA(securityassociations,安全联盟)来工作.
一个SA由一个源、一个目的和一个说明组成.
一个验证SA看上去应该是:add10.
0.
0.
1110.
0.
0.
216ah15700-Ahmac-md5"1234567890123456";意思是:"从10.
0.
0.
11到10.
0.
0.
216的数据包需要AH,使用HMAC-MD5签名,密码是1234567890123456".
这个说明的SPI(SecurityParameterIndex,安全参数索引)号码是'15700',细节后面再谈.
有意思的是一个会话的两端都使用相同的SA——它们是全等的,而不是镜像对称的.
要注意的是,SA并没有自动翻转规则——这个SA仅仅描述了从10.
0.
0.
11到10.
0.
0.
216的认证.
如果需要双向安全,需要2条SA.
一个简单地ESPSA:add10.
0.
0.
1110.
0.
0.
216esp15701-E3des-cbc"123456789012123456789012";意思是:"从10.
0.
0.
11到10.
0.
0.
216的数据包需要ESP,使用3des-cbc加密算法,密码是123456789012123456789012".
SPI号码是'15701'.
到此,我们看到SA描述了所有的说明,但它并没有描述应该什么时候用到这些策略.
实际上,可以有很多完全相同的SA,除了它们之间的SPI不同.
顺便提一句,SPI的意思是SecurityParameterIndex(安全参数索引).
为了进行加密操作,我们需要声明一个策略.
这个策略可以包含诸如"如果可能的话使用ipsec"或者"除非我们有ipsec否则丢弃数据包"这样的东西.
最简单的典型SP(SecurityPolicy,安全策略)应该是这样:spdadd10.
0.
0.
21610.
0.
0.
11any-Poutipsec\esp/transport//require\ah/transport//require;如果在10.
0.
0.
216上输入这个,就意味着凡是去往10.
0.
0.
11的数据包必须经过加密并附带AH头验证.
要注意,它并没有指出使用哪一个SA,那是留给内核来23决定的.
换句话说,SP描述了我们需要什么;而SA描述了它应该如何实现.
发出的数据包打上SA的API标记,以便本端内核用来加密和鉴定、对端内核用来解密和验证.
接下来是一个非常简单的配置,用来从10.
0.
0.
216到10.
0.
0.
11使用加密和验证进行发包.
注意.
相反方向的发包仍然是明码传输的,没有进行相应的配置.
在10.
0.
0.
216主机上:#!
/sbin/setkey-fadd10.
0.
0.
21610.
0.
0.
11ah24500-Ahmac-md5"1234567890123456";add10.
0.
0.
21610.
0.
0.
11esp24501-E3des-cbc"123456789012123456789012";spdadd10.
0.
0.
21610.
0.
0.
11any-Poutipsecesp/transport//requireah/transport//require;在10.
0.
0.
11机器上,SA是一样的,没有SP:#!
/sbin/setkey-fadd10.
0.
0.
21610.
0.
0.
11ah24500-Ahmac-md5"1234567890123456";add10.
0.
0.
21610.
0.
0.
11esp24501-E3des-cbc"123456789012123456789012";经过上面的设置(如果你在/sbin下安装了"setkey"命令,以上脚本是可以执行的),从10.
0.
0.
216上面"ping10.
0.
0.
11"的过程用tcpdump应该截获如下内容:22:37:5210.
0.
0.
216>10.
0.
0.
11:AH(spi=0x00005fb4,seq=0xa):ESP(spi=0x00005fb5,seq=0xa)(DF)22:37:5210.
0.
0.
11>10.
0.
0.
216:icmp:echoreply注意,可看出从10.
0.
0.
11返回的包的确是明码传输.
Ping的进一步细节用tcpdump当然看不到,但是它还是显示了用来告诉10.
0.
0.
11如何进行解密和验证的参数——AH和ESP的SPI值.
还有几件事情必须提及.
上面给出的配置在很多IPSEC的配置范例中都有引用,但确实是很危险的.
问题就在于上述配置包含了10.
0.
0.
216应该如何处理发往10.
0.
0.
11的包,和10.
0.
0.
1如何解释那些包,但是却没有指出10.
0.
0.
11应当丢弃来自10.
0.
0.
216的未进行加密及验证的包!
任何人都可以插入完全未加密的欺骗包,而10.
0.
0.
11会不假思索地接受.
为了弥补上述漏洞我们必须在10.
0.
0.
11上增加一个针对进入数据包的SP,如下:#!
/sbin/setkey-fspdadd10.
0.
0.
21610.
0.
0.
11any-PINipsecesp/transport//requireah/transport//require;这就指明了10.
0.
0.
11收到来自10.
0.
0.
216的包的时候需要正确的ESP和AH处理.
现在,我们完成这个配置,我们当然也希望回去的数据包也进行加密和头验证.
10.
0.
0.
216上完整的配置应该是:24#!
/sbin/setkey-fflush;spdflush;#AHadd10.
0.
0.
1110.
0.
0.
216ah15700-Ahmac-md5"1234567890123456";add10.
0.
0.
21610.
0.
0.
11ah24500-Ahmac-md5"1234567890123456";#ESPadd10.
0.
0.
1110.
0.
0.
216esp15701-E3des-cbc"123456789012123456789012";add10.
0.
0.
21610.
0.
0.
11esp24501-E3des-cbc"123456789012123456789012";spdadd10.
0.
0.
21610.
0.
0.
11any-Poutipsecesp/transport//requireah/transport//require;spdadd10.
0.
0.
1110.
0.
0.
216any-Pinipsecesp/transport//requireah/transport//require;10.
0.
0.
11上:#!
/sbin/setkey-fflush;spdflush;#AHadd10.
0.
0.
1110.
0.
0.
216ah15700-Ahmac-md5"1234567890123456";add10.
0.
0.
21610.
0.
0.
11ah24500-Ahmac-md5"1234567890123456";#ESPadd10.
0.
0.
1110.
0.
0.
216esp15701-E3des-cbc"123456789012123456789012";add10.
0.
0.
21610.
0.
0.
11esp24501-E3des-cbc"123456789012123456789012";spdadd10.
0.
0.
1110.
0.
0.
216any-Poutipsecesp/transport//requireah/transport//require;spdadd10.
0.
0.
21610.
0.
0.
11any-Pinipsecesp/transport//requireah/transport//require;注意,本例中通信双方的加密密钥是一样的.
这在实际中是不会出现的.
为了检测一下我们刚才的配置,运行一下:setkey–D就会显示出SA,或者用setkey–DP显示出SP.
7.
2.
自动密钥管理25在上一节中,使用了简单共享的密码进行加密.
换句话说,为了保密,我们必须通过一个安全的通道把加密配置传给对方.
如果我们使用telnet配置对端的机器,任何一个第三方都可能知道我们的共享密码,那么设置就是不安全的.
而且,因为密码是共享的,所以它就不成为真正意义的密码.
就算对端不能用它做什么,但我们需要为每一个要进行IPSEC通讯的对方都设置互补相同的密码.
这需要生成大量的密钥,如果有10个成员需要通讯,就至少需要50个不同的密码.
除了对称密钥的问题之外,还有解决密钥轮转的问题.
如果第三方搜集到足够的数据包,就有可能反向计算出密钥.
这可以通过每隔一段时间换用一个新密钥来解决,但是这必须自动完成.
另一个问题是,如上所述的手动密钥管理,我们必须精确地指定算法和密钥长度,与对端的协调也是个大问题.
我们渴望能够用一种比较宽松的方式来描述密钥策略,比如说:"我们可以用3DES或者Blowfish算法,密钥长度至少是多少多少位".
为了满足这些要求,IPSEC提供了IKE(InternetKeyExchange,Internet密钥交换)来自动随机生成密钥,并使用协商好的非对称加密算法进行密钥交换.
Linux2.
5的IPSEC实现利用KAME的"racoon"IKE守护程序来进行.
截止到11月9日,在Alexey的iptools发布包中的racoon是可以编译的,但是需要从两个文件中删除#include.
你也可以下载我提供的编译好的版本.
IKE需要使用UDP的500端口,确认你的iptables不会挡住数据包.
7.
2.
1.
理论象前面所解释的自动密钥管理会为我们做很多事情.
特别地,它会自动地动态生成SA.
不象大家所以为的那样,它并不会为我们设置SP.
所以,如果想使用IKE,需要设置好SP,但不要设置任何SA.
内核如果发现有一个IPSEC的SP,但不存在任何相应的SA,就会通知IKE守护程序,让它去协商.
重申,一个SP决定了我们需要什么;而SA决定了我们如何得到它.
使用自动密钥管理就可以让我们只关心需要什么就够了.
7.
2.
2.
举例Kame的racoon有非常多的选项,其中绝大部分都已经配置好了缺省值,所以我们不用修改它们.
象上面描述的,管理员需要定义一个SP而不配置SA,留给IKE守护程序去协商.
在这个例子中,仍然是10.
0.
0.
11和10.
0.
0.
216之间需要配置安全通讯,但这次要26借助于racoon.
为了简单起见,这个配置使用预先共享的密钥(又是可怕的共享密钥).
X.
509证书的问题单独讨论,参见后面的7.
2.
3.
我们尽量保持缺省配置,两台机器是一样的:pathpre_shared_key"/usr/local/etc/racoon/psk.
txt";remoteanonymous{exchange_modeaggressive,main;doiipsec_doi;situationidentity_only;my_identifieraddress;lifetimetime2min;#sec,min,hourinitial_contacton;proposal_checkobey;#obey,strictorclaimproposal{encryption_algorithm3des;hash_algorithmsha1;authentication_methodpre_shared_key;dh_group2;}}sainfoanonymous{pfs_group1;lifetimetime2min;encryption_algorithm3des;authentication_algorithmhmac_sha1;compression_algorithmdeflate;}有很多设置,我认为仍然有很多可以去掉而更接近缺省配置.
很少有值得注意的事情.
我们已经配置了两个匿名配置支持所有的对端机器,让将来的配置简单些.
这里没有必要为每台机器各写一个段落,除非真的必要.
此外,我们还设置了我们基于我们的IP地址来识别我们自己('my_identifieraddress'),并声明我们可以进行3DES、sha1,并且我们将使用预先共享的密钥,写在psk.
txt中.
在psk.
txt中,我们设置两个条目,两台机器上都不一样.
在10.
0.
0.
11上:10.
0.
0.
216password2在10.
0.
0.
216上:10.
0.
0.
11password2确认这些文件必须为root所有、属性是0600,否则racoon将不信任其内容.
两个机器上的这个文件是镜像对称的.
现在,我们就剩下设置SP了,这比较简单.
在10.
0.
0.
216上:27#!
/sbin/setkey-fflush;spdflush;spdadd10.
0.
0.
21610.
0.
0.
11any-Poutipsecesp/transport//require;spdadd10.
0.
0.
1110.
0.
0.
216any-Pinipsecesp/transport//require;在10.
0.
0.
11上:#!
/sbin/setkey-fflush;spdflush;spdadd10.
0.
0.
1110.
0.
0.
216any-Poutipsecesp/transport//require;spdadd10.
0.
0.
21610.
0.
0.
11any-Pinipsecesp/transport//require;请注意这些SP的镜像对称规律.
我们可以启动racoon了!
一旦启动,当我们试图从10.
0.
0.
11到10.
0.
0.
216进行telnet的时候,racoon就会开始协商:12:18:44:INFO:isakmp.
c:1689:isakmp_post_acquire():IPsec-SArequestfor10.
0.
0.
11queuedduetonophase1found.
12:18:44:INFO:isakmp.
c:794:isakmp_ph1begin_i():initiatenewphase1negotiation:10.
0.
0.
216[500]10.
0.
0.
11[500]12:18:44:INFO:isakmp.
c:799:isakmp_ph1begin_i():beginAggressivemode.
12:18:44:INFO:vendorid.
c:128:check_vendorid():receivedVendorID:KAME/racoon12:18:44:NOTIFY:oakley.
c:2037:oakley_skeyid():couldn'tfindtheproperpskey,trytogetonebythepeer'saddress.
12:18:44:INFO:isakmp.
c:2417:log_ph1established():ISAKMP-SAestablished10.
0.
0.
216[500]-10.
0.
0.
11[500]spi:044d25dede78a4d1:ff01e5b4804f068012:18:45:INFO:isakmp.
c:938:isakmp_ph2begin_i():initiatenewphase2negotiation:10.
0.
0.
216[0]10.
0.
0.
11[0]12:18:45:INFO:pfkey.
c:1106:pk_recvupdate():IPsec-SAestablished:ESP/Transport10.
0.
0.
11->10.
0.
0.
216spi=44556347(0x2a7e03b)12:18:45:INFO:pfkey.
c:1318:pk_recvadd():IPsec-SAestablished:ESP/Transport10.
0.
0.
216->10.
0.
0.
11spi=15863890(0xf21052)如果我们现在运行setkey–D列出SA,就会发现已经存在了:10.
0.
0.
21610.
0.
0.
11espmode=transportspi=224162611(0x0d5c7333)reqid=0(0x00000000)E:3des-cbc5d421c1bd33b2a9f4e9055e3857db9fc211d9c95ebaead04A:hmac-sha1c5537d66f3c5d869bd736ae208d2213327f7aa99seq=0x00000000replay=4flags=0x00000000state=maturecreated:Nov1112:28:452002current:Nov1112:29:162002diff:31(s)hard:600(s)soft:480(s)last:Nov1112:29:122002hard:0(s)soft:0(s)current:304(bytes)hard:0(bytes)soft:0(bytes)28allocated:3hard:0soft:0sadb_seq=1pid=17112refcnt=010.
0.
0.
1110.
0.
0.
216espmode=transportspi=165123736(0x09d79698)reqid=0(0x00000000)E:3des-cbcd7af8466acd4f14c872c5443ec45a719d4b3fde18d239d6aA:hmac-sha141ccc3884568ac4919e4e024628e240c141ffe2fseq=0x00000000replay=4flags=0x00000000state=maturecreated:Nov1112:28:452002current:Nov1112:29:162002diff:31(s)hard:600(s)soft:480(s)last:hard:0(s)soft:0(s)current:231(bytes)hard:0(bytes)soft:0(bytes)allocated:2hard:0soft:0sadb_seq=0pid=17112refcnt=0我们的SP是如下配置的:10.
0.
0.
11[any]10.
0.
0.
216[any]tcpinipsecesp/transport//requirecreated:Nov1112:28:282002lastused:Nov1112:29:122002lifetime:0(s)validtime:0(s)spid=3616seq=5pid=17134refcnt=310.
0.
0.
216[any]10.
0.
0.
11[any]tcpoutipsecesp/transport//requirecreated:Nov1112:28:282002lastused:Nov1112:28:442002lifetime:0(s)validtime:0(s)spid=3609seq=4pid=17134refcnt=37.
2.
2.
1.
问题和常见的疏忽如果不工作,检查一下所有的配置文件是不是为root所有,而且只有root才能读取.
如想前台启动racoon,就加上"-F"参数.
如想强制它读取某一个配置文件来取代缺省配置文件,使用参数"-f".
如想看到超级详细的细节,往racoon.
conf中加入"logdebug;"一行.
7.
2.
3.
使用X.
509证书进行自动密钥管理如前所述,之所以共享密码很困难,是因为它们一旦共享,就不再成为真正意义的密码.
幸运的是,我们仍可以用非对称加密技术来解决这个问题.
如果IPSEC的每个参与者都生成一对公钥和私钥,就可以让双方公开它们的公29钥并设置策略,从而建立安全连接.
虽然需要一些计算,但生成密钥还是相对比较简单的.
以下都是基于openssl工具实现的.
7.
2.
3.
1.
为你的主机生成一个X.
509证书OpenSSL搭好了很多基础结构,以便我们能够使用经过或者没有经过CA签署的密钥.
现在,我们就围绕这些基础结构,并练习一下使用著名的SnakeOil安全,而不是使用CA.
首先,我们为主机laptop发起一个"证书请求":$opensslreq-new-nodes-newkeyrsa:1024-sha1-keyformPEM-keyout\laptop.
private-outformPEM-outrequest.
pem这是可能问我们的问题:CountryName(2lettercode)[AU]:NLStateorProvinceName(fullname)[Some-State]:.
LocalityName(eg,city)[]:DelftOrganizationName(eg,company)[InternetWidgitsPtyLtd]:LinuxAdvancedRouting&TrafficControlOrganizationalUnitName(eg,section)[]:laptopCommonName(eg,YOURname)[]:berthubertEmailAddress[]:ahu@ds9a.
nlPleaseenterthefollowing'extra'attributestobesentwithyourcertificaterequestAchallengepassword[]:Anoptionalcompanyname[]:请你根据自己的实际情况完整填写.
你可以把你的主机名写进去,也可以不写,取决于你的安全需求.
这个例子中,我们写了.
我们现在自己签署这个请求:$opensslx509-req-inrequest.
pem-signkeylaptop.
private-out\laptop.
publicSignatureoksubject=/C=NL/L=Delft/O=LinuxAdvancedRouting&Traffic\Control/OU=laptop/CN=berthubert/Email=ahu@ds9a.
nlGettingPrivatekey现在,"request.
pem"这个文件已经没用了,可以删除.
在你需要证书的每台机器上都重复上述过程.
你现在就可以放心地发布你的"*.
public"文件了,但是一定要保证"*.
private"是保密的!
7.
2.
3.
2.
设置并启动我们一旦拥有了一对公钥和私钥,就可以告诉racoon去使用它们了.
现在我们回到上面的配置中的两台机器,10.
0.
0.
11(upstairs)和10.
0.
0.
216(laptop).
在10.
0.
0.
11上的racoon.
conf中,我们添加:30pathcertificate"/usr/local/etc/racoon/certs";remote10.
0.
0.
216{exchange_modeaggressive,main;my_identifierasn1dn;peers_identifierasn1dn;certificate_typex509"upstairs.
public""upstairs.
private";peers_certfile"laptop.
public";proposal{encryption_algorithm3des;hash_algorithmsha1;authentication_methodrsasig;dh_group2;}}它们告诉racoon:证书可以在/usr/local/etc/racoon/certs/那里找到.
而且还包含了专门为10.
0.
0.
216而写的配置项.
包含"asn1dn"的行告诉racoon,本端和对端的标识都从公钥中提取.
也就是上面输出的"subject=/C=NL/L=Delft/O=LinuxAdvancedRouting&TrafficControl/OU=laptop/CN=berthubert/Email=ahu@ds9a.
nl".
"certificate_type"那一行配置了本地的公钥和私钥.
"peers_certfile"这行告诉racoon读取名叫"laptop.
public"的文件取得对端的公钥.
"proposal"这一段与你以前看到的基本一致,除了"authentication_method"的值变成了"rsasig",意思是使用RSA公钥/私钥对.
在10.
0.
0.
216上面的配置文件与上面的是完全镜像关系,没有其它改变:pathcertificate"/usr/local/etc/racoon/certs";remote10.
0.
0.
11{exchange_modeaggressive,main;my_identifierasn1dn;peers_identifierasn1dn;certificate_typex509"laptop.
public""laptop.
private";peers_certfile"upstairs.
public";proposal{encryption_algorithm3des;hash_algorithmsha1;authentication_methodrsasig;dh_group2;}}现在,我们已经把两台机器的配置文件改好了,然后就应该把证书文件拷贝到正确的位置.
"upstairs"这台机器需要往/usr/local/etc/racoon/certs中放入upstairs.
private、upstairs.
public和laptop.
public.
请确认这个目录属于root,且属性为0600,否则racoon会拒绝使用!
"laptop"这台机器需要往/usr/local/etc/racoon/certs中放入laptop.
private、laptop.
public和upstairs.
public.
也就是说,每台机器都需要本端的公钥和私钥,31以及对端的公钥.
确认一下已经写好了SP(执行在7.
2.
2中提到的spdadd).
然后启动racoon,就应该可以工作了.
7.
2.
3.
3.
如何安全地建立隧道为了与对端建立安全的通讯,我们必须交换公钥.
公钥没必要保密,重要的是要保证它不被替换.
换句话说,要确保没有"中间人".
为了简化这个工作,OpenSSL提供了"digest"命令:$openssldgstupstairs.
publicMD5(upstairs.
public)=78a3bddafb4d681c1ca8ed4d23da4ff1现在我们要做的就是检验一下对方是否能够得到相同的MD5散列值.
这可以通过真实地接触来完成,也可以通过电话,但是一定不要与公钥放在同一封电子邮件里发送!
另一个办法是通过一个可信的第三方(CA)来实现.
这个CA会为你的密钥进行签名,而不是象上面那样由我们自己签名.
7.
3.
IPSEC隧道迄今为止,我们只是认识了IPSEC的"transport"(透明)模式,也就是通讯的两端都能够直接理解IPSEC.
这不能代表所有的情况,有时候我们只需要路由器理解IPSEC,路由器后面的机器利用它们进行通讯.
这就是所谓的"tunnelmode"(隧道模式).
设置这个极其简单.
如果想通过10.
0.
0.
216与10.
0.
0.
11建立的隧道来传输从10.
0.
0.
0/24到130.
161.
0.
0/16的数据包,按下面配置就可以:#!
/sbin/setkey-fflush;spdflush;add10.
0.
0.
21610.
0.
0.
11esp34501-mtunnel-E3des-cbc"123456789012123456789012";spdadd10.
0.
0.
0/24130.
161.
0.
0/16any-Poutipsecesp/tunnel/10.
0.
0.
216-10.
0.
0.
11/require;注意.
"-mtunnel"是关键!
这里首先配置了一个隧道两端(10.
0.
0.
216与10.
0.
0.
11)使用ESP的SA.
然后配置了实际的隧道.
它指示内核,对于从10.
0.
0.
0/24到130.
161.
0.
0/16的数据包需要加密.
而且这些数据包被发往10.
0.
0.
11.
10.
0.
0.
11也需要相同的配置:#!
/sbin/setkey-fflush;32spdflush;add10.
0.
0.
21610.
0.
0.
11esp34501-mtunnel-E3des-cbc"123456789012123456789012";spdadd10.
0.
0.
0/24130.
161.
0.
0/16any-Pinipsecesp/tunnel/10.
0.
0.
216-10.
0.
0.
11/require;注意,与上面基本一样,除了把"-Pout"换成了"-Pin".
就象先前的例子一样,我们只配置了单向的传输.
完整地实现双向传输就留给读者自己研究实现吧.
这种配置的另一个更直观的名字叫做"ESP代理(proxyESP)".
IPSEC隧道需要内核能够进行IP转发!
7.
4.
其它IPSEC软件ThomasWalpuski报告说它已经写了一个补丁,可以让OpenBSD的isakpmd与Linux2.
5的IPSEC协同工作.
可以在这个网页找到.
他指出isakpmd在Linux上仅仅需要libkeynote支持.
根据他的说法,在Linux2.
5.
59上面工作得很好.
isakpmd与前面提到的racoon由很大的不同,但是很多人喜欢用它,可以在这里找到.
这里有OpenBSDCVS.
Thomas还为那些不习惯用CVS或者patch的人们制作了一个tarball.
7.
5.
IPSEC与其它系统的互操作求助:Writethis7.
5.
1.
Windows33第8章多播路由求助:EditorVacancy!
Multicast-HOWTO已经很古老了(相对而言),而且不够准确甚至会因此而误导读者.
在你开始进行多播路由之前,你需要重新配置你的Linux内核来支持你想实现的多播路由类型.
这一步需要你来决定使用何种类型的多播路由.
基本上有这么四种:DVMRP(RIP单播协议的多播版本),MOSPF(同理,只不过是OSPF),PIM-SM("ProtocolIndependentMulticasting-SparseMode",协议无关多播-稀疏模式,它假定任意多播组的用户都是spreadout的,而不是clumped的)和PIM-DM(同理,只不过是"密集模式",它假定同一个多播组的用户适当地clumps在一起).
在Linux内核中,你会注意到并没有这些选项.
这是因为这些协议本身是由路由程序负责处理的,比如Zebra、mrouted或者pimd.
然而你仍然应该对于你要使用那种方案有一个明确的主意,以便选择正确的内核选项.
无论哪种多播路由,你一定都要启用"multicasting"和"multicastrouting"选项.
对于DVMRP和MOSPF这就够了.
如果你想使用PIM,你必须还要启用PIMv1或者PIMv2选项,具体用哪个取决于你的网络究竟使用PIM协议的哪一个版本.
当你把那些都想清楚、编译了新内核之后,重启的时候应该能够看到IP协议的列表中包括了IGMP.
这是用来管理多播组的协议.
虽然第3版业已存在并归档,但截止到写这篇文档时为止,Linux只支持IGMP的第1版和第2版.
但这并不会太多地影响我们,因为IGMPv3还太新,并没有看到多少能够用到v3特有功能的应用.
因为,用IGMP处理组,仅会使用到连最简单的IGMP版本中都会包含的基本功能.
绝大部分应该是IGMPv2,虽然仍能接触到IGMPv1.
到此为止,一切都好.
我们已经启用了多播.
现在,我们得告诉内核,做点实在的事情了——启动路由.
也就是说象路由表中添加多播子网:iprouteadd224.
0.
0.
0/4deveth0(当然,我们假定你要通过eth0进行多播.
你要根据你的情况选择设备.
)现在,启动Linux的包转发.
.
.
echo1>/proc/sys/net/ipv4/ip_forward在这里,你可能想知道是否起了作用.
所以我们ping一下缺省组224.
0.
0.
1,看看有没有人在.
在你的LAN上所有配置并启用了多播的机器都应该予以回应,其他机器则不会.
但你会注意到,没有任何一台机器回应的时候声明自己是224.
0.
0.
1,多么令人惊奇!
因为这是一个组地址(对于接收者来说是"广播"),所以组中的所有成员都用它们的地址来回应,而不是用组地址来回应.
ping-c2224.
0.
0.
134到此,你已经可以实现真正的多播路由了.
好的,假定你需要在两个网络间进行路由.
(ToBeContinued!
)35第9章带宽管理的队列规定现在,当我搞清楚这一切之后,我真的大吃一惊.
Linux2.
2/2.
4完全能象那些最高端的专用带宽管理系统一样来管理带宽.
甚至比帧中继和ATM还要优秀.
为避免概念混乱,tc采用如下规定来描述带宽:mbps=1024kbps=1024*1024bps=>byte/smbit=1024kbit=>kilobit/s.
mb=1024kb=1024*1024b=>bytembit=1024kbit=>kilobit.
内定:数字以bps和b方式储存.
但当tc输出速率时,使用如下表示:1Mbit=1024Kbit=1024*1024bps=>byte/s9.
1.
解释队列和队列规定利用队列,我们决定了数据被发送的方式.
必须认识到,我们只能对发送数据进行整形.
根据Internet的工作方式,我们无法直接控制别人向我们发送什么数据.
有点象我们家里的信报箱,你不可能控制全世界,联系每一个人,修改别人对你发送邮件的数量.
然而,Internet主要依靠TCP/IP,它的一些特性很有用.
因为TCP/IP没办法知道两个主机之间的网络容量,所以它会试图越来越快地发送数据(所谓的"慢起技术"),当因为网络容量不够而开始丢失数据时,再放慢速度.
实际情况要比这种方法更聪明,我们以后再讨论.
这就象当你尚未读完一半邮件时,希望别人停止给你寄信.
与现实世界不同,在Internet上可以做到这一点.
(译注:这个例子并不恰当,TCP/IP的这种机制并不是在网络层实现的,而是要靠传输层的TCP协议)如果你有一个路由器,并且希望能够防止某些主机下载速度太快,你需要在你路由器的内网卡——也就是向你的网内主机发送数据包的网卡——上进行流量整形.
你还要保证你正在控制的是瓶颈环节.
如果你有一个100M以太网卡,而你的路由器的链路速度是256k,你必须保证你发送的数据量没有超过路由器的处理能力.
否则,就是路由器在控制链路和对带宽进行整形,而不是你.
可以说,我们需要拥有的队列必须是一系列链路中最慢的环节.
幸运的是这很容易.
369.
2.
简单的无类队列规定如前所述,利用队列,我们决定了数据被发送的方式.
无类队列规定就是那样,能够接受数据和重新编排、延迟或丢弃数据包.
这可以用作对于整个网卡的流量进行整形,而不细分各种情况.
在我们进一步学习分类的队列规定之前,理解这部分是必不可少的!
最广泛应用的规定是pfifo_fast队列规定,因为它是缺省配置.
这也解释了为什么其它那些复杂的功能为何如此健壮,因为那些都与缺省配置相似,只不过是其他类型的队列而已.
每种队列都有它们各自的优势和弱点.
9.
2.
1.
pfifo_fast这个队列的特点就象它的名字——先进先出(FIFO),也就是说没有任何数据包被特殊对待.
至少不是非常特殊.
这个队列有3个所谓的"频道".
FIFO规则应用于每一个频道.
并且:如果在0频道有数据包等待发送,1频道的包就不会被处理,1频道和2频道之间的关系也是如此.
内核遵照数据包的TOS标记,把带有"最小延迟"标记的包放进0频道.
不要把这个无类的简单队列规定与分类的PRIO相混淆!
虽然它们的行为有些类似,但对于无类的pfifo_fast而言,你不能使用tc命令向其中添加其它的队列规定.
9.
2.
1.
1.
参数与使用pfifo_fast队列规定作为硬性的缺省设置,你不能对它进行配置.
它缺省是这样配置的:priomap:内核规定,根据数据包的优先权情况,对应相应的频道.
这个对应是根据数据包的TOS字节进行的.
TOS看上去是这样的:01234567|||||优先权|TOS|MBZ|||||TOS字段的4个bit是如下定义的:二进制十进制意义10008最小延迟(md)01004最大throughput(mt)00102最大可靠性(mr)3700011最小成本(mmc)00000正常服务因为在这4bit的后面还有一个bit,所以TOS字段的实际值是上述值的2倍.
(Tcpdump-v-v可以让你看到整个TOS字段的情况,而不仅仅是这4个bit)也就是你在下表的第一列看到的值:TOSBits意义Linux优先权频道0x00正常服务0最好效果10x21最小成本(mmc)1填充20x42最大可靠性(mr)0最好效果10x63mmc+mr0最好效果10x84最大吞吐量(mt)2大量传输20xa5mmc+mt2大量传输20xc6mr+mt2大量传输20xe7mmc+mr+mt2大量传输20x108最小延迟(md)6交互00x129mmc+md6交互00x1410mr+md6交互00x1611mmc+mr+md6交互00x1812mt+md4交互+大量传输10x1a13mmc+mt+md4交互+大量传输10x1c14mr+mt+md4交互+大量传输10x1e15mmc+mr+mt+md4交互+大量传输1很多的数字.
第二列写着与4个TOS位相关的数值,接着是它们的意义.
比如,15表示一个数据包要求最小成本、最大可靠性、最大吞吐量和最小延迟.
我想称之为"人代会车队".
[译者按:原作为'荷兰数据包']第四列写出了Linux内核对于TOS位的理解,并表明了它们对应哪种优先权.
最后一列表明缺省的权限图.
在命令行里,缺省的权限图应该是:1,2,2,2,1,2,0,0,1,1,1,1,1,1,1,1也就是说,比如优先权4将被映射到1频道.
权限图允许你列出更高的优先权值(>7),它们不对应TOS映射,但是有其它的意图.
下表来自RFC1349,告诉你应用程序可能如何设置它们的TOS:TELNET1000(minimizedelay)控制1000(minimizedelay)FTP数据0100(maximizethroughput)TFTP1000(minimizedelay)命令阶段1000(minimizedelay)SMTP数据阶段0100(maximizethroughput)UDP查询1000(minimizedelay)TCP查询0000DomainNameService区域传输0100(maximizethroughput)NNTP0001(minimizemonetarycost)报错0000请求0000(mostly)ICMP响应(mostly)txqueuelen38队列的长度来自网卡的配置,你可以用ifconfig和ip命令修改.
如设置队列长度为10,执行:ifconfigeth0txqueuelen10你不能用tc命令设置这个!
9.
2.
2.
令牌桶过滤器(TBF)令牌桶过滤器(TBF)是一个简单的队列规定:只允许以不超过事先设定的速率到来的数据包通过,但可能允许短暂突发流量朝过设定值.
TBF很精确,对于网络和处理器的影响都很小.
所以如果您想对一个网卡限速,它应该成为您的第一选择!
TBF的实现在于一个缓冲器(桶),不断地被一些叫做"令牌"的虚拟数据以特定速率填充着.
(tokenrate).
桶最重要的参数就是它的大小,也就是它能够存储令牌的数量.
每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除.
这个算法关联到两个流上——令牌流和数据流,于是我们得到3种情景:数据流以等于令牌流的速率到达TBF.
这种情况下,每个到来的数据包都能对应一个令牌,然后无延迟地通过队列.
数据流以小于令牌流的速度到达TBF.
通过队列的数据包只消耗了一部分令牌,剩下的令牌会在桶里积累下来,直到桶被装满.
剩下的令牌可以在需要以高于令牌流速率发送数据流的时候消耗掉,这种情况下会发生突发传输.
数据流以大于令牌流的速率到达TBF.
这意味着桶里的令牌很快就会被耗尽.
导致TBF中断一段时间,称为"越限".
如果数据包持续到来,将发生丢包.
最后一种情景非常重要,因为它可以用来对数据通过过滤器的速率进行整形.
令牌的积累可以导致越限的数据进行短时间的突发传输而不必丢包,但是持续越限的话会导致传输延迟直至丢包.
请注意,实际的实现是针对数据的字节数进行的,而不是针对数据包进行的.
9.
2.
2.
1.
参数与使用即使如此,你还是可能需要进行修改,TBF提供了一些可调控的参数.
第一个参数永远可用:limit/latencylimit确定最多有多少数据(字节数)在队列中等待可用令牌.
你也可以通过设置latency参数来指定这个参数,latency参数确定了一个包在TBF中等待传输的最长等待时间.
后者计算决定桶的大小、速率和峰值速率.
39burst/buffer/maxburst桶的大小,以字节计.
这个参数指定了最多可以有多少个令牌能够即刻被使用.
通常,管理的带宽越大,需要的缓冲器就越大.
在Intel体系上,10兆bit/s的速率需要至少10k字节的缓冲区才能达到期望的速率.
如果你的缓冲区太小,就会导致到达的令牌没有地方放(桶满了),这会导致潜在的丢包.
mpu一个零长度的包并不是不耗费带宽.
比如以太网,数据帧不会小于64字节.
Mpu(MinimumPacketUnit,最小分组单位)决定了令牌的最低消耗.
rate速度操纵杆.
参见上面的limits!
如果桶里存在令牌而且允许没有令牌,相当于不限制速率(缺省情况).
Ifthebucketcontainstokensandisallowedtoempty,bydefaultitdoessoatinfinitespeed.
如果不希望这样,可以调整入下参数:peakrate如果有可用的令牌,数据包一旦到来就会立刻被发送出去,就象光速一样.
那可能并不是你希望的,特别是你有一个比较大的桶的时候.
峰值速率可以用来指定令牌以多块的速度被删除.
用书面语言来说,就是:释放一个数据包,但后等待足够的时间后再释放下一个.
我们通过计算等待时间来控制峰值速率然而,由于UNIX定时器的分辨率是10毫秒,如果平均包长10kbit,我们的峰值速率被限制在了1Mbps.
mtu/minburst但是如果你的常规速率比较高,1Mbps的峰值速率对我们就没有什么价值.
要实现更高的峰值速率,可以在一个时钟周期内发送多个数据包.
最有效的办法就是:再创建一个令牌桶!
这第二个令牌桶缺省情况下为一个单个的数据包,并非一个真正的桶.
要计算峰值速率,用mtu乘以100就行了.
(应该说是乘以HZ数,Intel体系上是100,Alpha体系上是1024)9.
2.
2.
2.
配置范例这是一个非常简单而实用的例子:#tcqdiscadddevppp0roottbfrate220kbitlatency50msburst1540为什么它很实用呢如果你有一个队列较长的网络设备,比如DSLmodem或者cablemodem什么的,并通过一个快速设备(如以太网卡)与之相连,你会发现上40载数据绝对会破坏交互性.
这是因为上载数据会充满modem的队列,而这个队列为了改善上载数据的吞吐量而设置的特别大.
但这并不是你需要的,你可能为了提高交互性而需要一个不太大的队列.
也就是说你希望在发送数据的时候干点别的事情.
上面的一行命令并非直接影响了modem中的队列,而是通过控制Linux中的队列而放慢了发送数据的速度.
把220kbit修改为你实际的上载速度再减去几个百分点.
如果你的modem确实很快,就把"burst"值提高一点.
9.
2.
3.
随机公平队列(SFQ)SFQ(StochasticFairnessQueueing,随机公平队列)是公平队列算法家族中的一个简单实现.
它的精确性不如其它的方法,但是它在实现高度公平的同时,需要的计算量却很少.
SFQ的关键词是"会话"(或称作"流"),主要针对一个TCP会话或者UDP流.
流量被分成相当多数量的FIFO队列中,每个队列对应一个会话.
数据按照简单轮转的方式发送,每个会话都按顺序得到发送机会.
这种方式非常公平,保证了每一个会话都不会没其它会话所淹没.
SFQ之所以被称为"随机",是因为它并不是真的为每一个会话创建一个队列,而是使用一个散列算法,把所有的会话映射到有限的几个队列中去.
因为使用了散列,所以可能多个会话分配在同一个队列里,从而需要共享发包的机会,也就是共享带宽.
为了不让这种效应太明显,SFQ会频繁地改变散列算法,以便把这种效应控制在几秒钟之内.
有很重要的一点需要声明:只有当你的出口网卡确实已经挤满了的时候,SFQ才会起作用!
否则在你的Linux机器中根本就不会有队列,SFQ也就不会起作用.
稍后我们会描述如何把SFQ与其它的队列规定结合在一起,以保证两种情况下都比较好的结果.
特别地,在你使用DSLmodem或者cablemodem的以太网卡上设置SFQ而不进行任何进一步地流量整形是无谋的!
9.
2.
3.
1.
参数与使用SFQ基本上不需要手工调整:perturb多少秒后重新配置一次散列算法.
如果取消设置,散列算法将永远不会重新配置(不建议这样做).
10秒应该是一个合适的值.
quantum41一个流至少要传输多少字节后才切换到下一个队列.
却省设置为一个最大包的长度(MTU的大小).
不要设置这个数值低于MTU!
9.
2.
3.
2.
配置范例如果你有一个网卡,它的链路速度与实际可用速率一致——比如一个电话MODEM——如下配置可以提高公平性:#tcqdiscadddevppp0rootsfqperturb10#tc-s-dqdisclsqdiscsfq800c:devppp0quantum1514blimit128pflows128/1024perturb10secSent4812bytes62pkts(dropped0,overlimits0)"800c:"这个号码是系统自动分配的一个句柄号,"limit"意思是这个队列中可以有128个数据包排队等待.
一共可以有1024个散列目标可以用于速率审计,而其中128个可以同时激活.
(nomorepacketsfitinthequeue!
)每隔10秒种散列算法更换一次.
9.
3.
关于什么时候用哪种队列的建议总之,我们有几种简单的队列,分别使用排序、限速和丢包等手段来进行流量整形.
下列提示可以帮你决定使用哪一种队列.
涉及到了第14章所描述的的一些队列规定:如果想单纯地降低出口速率,使用令牌桶过滤器.
调整桶的配置后可用于控制很高的带宽.
如果你的链路已经塞满了,而你想保证不会有某一个会话独占出口带宽,使用随机公平队列.
如果你有一个很大的骨干带宽,并且了解了相关技术后,可以考虑前向随机丢包(参见"高级"那一章).
如果希望对入口流量进行"整形"(不是转发流量),可使用入口流量策略,注意,这不是真正的"整形".
如果你正在转发数据包,在数据流出的网卡上应用TBF.
除非你希望让数据包从多个网卡流出,也就是说入口网卡起决定性作用的时候,还是使用入口策略.
如果你并不希望进行流量整形,只是想看看你的网卡是否有比较高的负载而需要使用队列,使用pfifo队列(不是pfifo_fast).
它缺乏内部频道但是可以统计backlog.
最后,你可以进行所谓的"社交整形".
你不能通过技术手段解决一切问题.
用户的经验技巧永远是不友善的.
正确而友好的措辞可能帮助你的正确地分配带宽!
429.
4.
术语为了正确地理解更多的复杂配置,有必要先解释一些概念.
由于这个主题的历史不长和其本身的复杂性,人们经常在说同一件事的时候使用各种词汇.
以下来自draft-ietf-diffserv-model-06.
txt,Diffserv路由器的建议管理模型.
可以在以下地址找到:http://www.
ietf.
org/internet-drafts/draft-ietf-diffserv-model-06.
txt.
关于这些词语的严格定义请参考这个文档.
队列规定管理设备输入(ingress)或输出(egress)的一个算法.
无类的队列规定一个内部不包含可配置子类的队列规定.
分类的队列规定一个分类的队列规定内可一包含更多的类.
其中每个类又进一步地包含一个队列规定,这个队列规定可以是分类的,也可以是无类的.
根据这个定义,严格地说pfifo_fast算是分类的,因为它实际上包含3个频道(实际上可以认为是子类).
然而从用户的角度来看它是无类的,因为其内部的子类无法用tc工具进行配置.
类一个分类的队列规定可以拥有很多类,类内包含队列规定.
分类器每个分类的队列规定都需要决定什么样的包使用什么类进行发送.
分类器就是做这个用的.
过滤器分类是通过过滤器完成的.
一个过滤器包含若干的匹配条件,如果符合匹配条件,就按此过滤器分类.
调度在分类器的帮助下,一个队列规定可以裁定某些数据包可以排在其他数据包之前发送.
这种处理叫做"调度",比如此前提到的pfifo_fast就是这样的.
调度也可以叫做"重排序",但这样容易混乱.
整形在一个数据包发送之前进行适当的延迟,以免超过事先规定好的最大速率,这种处理叫做"整形".
整形在egress处进行.
习惯上,通过丢包来降速也经常被称为整形.
43策略通过延迟或是丢弃数据包来保证流量不超过事先规定的带宽.
在Linux,里,策略总是规定丢弃数据包而不是延迟.
即,不存在ingress队列.
Work-Conserving对于一个work-conserving队列规定,如果得到一个数据包,它总是立刻对它进行分发.
换句话说,只要网卡(egress队列规定)允许,它就不会延迟数据包的发送.
non-Work-Conserving有些队列——比如令牌桶过滤器——可能需要暂时停止发包以实现限制带宽.
也就是说它们有时候即使有数据包需要处理,也可能拒绝发送.
现在我们简单了解了一些术语,让我们看看他们的位置:用户级程序^||Y|IP协议栈|||||||Y|||Y||^||转发-||/Y||||||^Y/-队列规定1-\|||Egress/--队列规定2--\|--->->Ingress分类器-------队列规定3-|队列规定\__队列规定4__/||\-队列规定N_/|||感谢JamalHadiSalim制作的ASCII字符图像.
整个大方框表示内核.
最左面的箭头表示从网络上进入机器的数据包.
它们进入Ingress队列规定,并有可能被某些过滤器丢弃.
即所谓策略.
这些是很早就发生的(在进入内核更深的部分之前).
这样早地丢弃数据有利于节省CPU时间.
数据包顺利通过的话,如果它是发往本地进程的,就会进入IP协议栈处理并提交给该进程.
如果它需要转发而不是进入本地进程,就会发往egress.
本地进程也可以发送数据,交给Egress分类器.
然后经过审查,并放入若干队列规定中的一个进行排队.
这个过程叫做"入队".
在不进行任何配置的情况下,只有一个egress队列规定——pfifo_fast——总是接收数据包.
数据包进入队列后,就等待内核处理并通过某网卡发送.
这个过程叫做"出队".
44这张图仅仅表示了机器上只有一块网卡的情况,图中的箭头不能代表所有情况.
每块网卡都有它自己的ingress和egress.
9.
5.
分类的队列规定如果你有多种数据流需要进行区别对待,分类的队列规定就非常有用了.
众多分类的队列规定中的一种——CBQ(ClassBasedQueueing,基于类的队列)——经常被提起,以至于造成大家认为CBQ就是鉴别队列是否分类的标准,这是不对的.
CBQ不过是家族中最大的孩子而已,同时也是最复杂的.
它并不能为你做所有的事情.
对于某些人而言这有些不可思议,因为他们受"sendmail效应"影响较深,总是认为只要是复杂的并且没有文档的技术肯定是最好的.
9.
5.
1.
分类的队列规定及其类中的数据流向一旦数据包进入一个分类的队列规定,它就得被送到某一个类中——也就是需要分类.
对数据包进行分类的工具是过滤器.
一定要记住:"分类器"是从队列规定内部调用的,而不是从别处.
过滤器会返回一个决定,队列规定就根据这个决定把数据包送入相应的类进行排队.
每个子类都可以再次使用它们的过滤器进行进一步的分类.
直到不需要进一步分类时,数据包才进入该类包含的队列规定排队.
除了能够包含其它队列规定之外,绝大多数分类的队列规定黑能够流量整形.
这对于需要同时进行调度(如使用SFQ)和流量控制的场合非常有用.
如果你用一个高速网卡(比如以太网卡)连接一个低速设备(比如cablemodem或者ADSLmodem)时,也可以应用.
如果你仅仅使用SFQ,那什么用也没有.
因为数据包进、出路由器时没有任何延迟.
虽然你的输出网卡远远快于实际连接速率,但路由器中却没有队列可以调度.
9.
5.
2.
队列规定家族:根、句柄、兄弟和父辈每块网卡都有一个出口"根队列规定",缺省情况下是前面提到的pfifo_fast队列规定.
每个队列规定都指定一个句柄,以便以后的配置语句能够引用这个队列规定.
除了出口队列规定之外,每块网卡还有一个入口,以便policies进入的数据流.
队列规定的句柄有两个部分:一个主号码和一个次号码.
习惯上把根队列规定称为"1:",等价于"1:0".
队列规定的次号码永远是0.
类的主号码必须与它们父辈的主号码一致.
9.
5.
2.
1.
如何用过滤器进行分类下图给出一个典型的分层关系:45根1:|_1:1_/|\/|\/|\10:11:12:/\/\10:110:212:112:2不要误解这张图!
你千万不要想象内核处在树的顶点而下面部分是网络.
数据包是在根队列规定处入队和出队的,而内核只同根打交道.
一个数据包可能是按照下面这个链状流程进行分类的:1:->1:1->12:->12:2数据包现在应该处于12:2下属的某个队列规定中的某个队列中.
在这个例子中,树的每个节点都附带着一个过滤器,用来选择下一步进入哪个分支.
这样比较直观.
然而,这样也是允许的:1:->12:2也就是说,根所附带的一个过滤器要求把数据包直接交给12:2.
9.
5.
2.
2.
数据包如何出队并交给硬件当内核决定把一个数据包发给网卡的时候,根队列规定1:会得到一个出队请求,然后把它传给1:1,然后依次传给10:、11:和12:,whicheachquerytheirsiblings,然后试图从它们中进行dequeue()操作.
也就是说,内和需要遍历整颗树,因为只有12:2中才有这个数据包.
换句话说,类及其兄弟仅仅与其"父队列规定"进行交谈,而不会与网卡进行交谈.
只有根队列规定才能由内核进行出队操作!
更进一步,任何类的出队操作都不会比它们的父类更快.
这恰恰是你所需要的:我们可以把SFQ作为一个子类,放到一个可以进行流量整形的父类中,从而能够同时得到SFQ的调度功能和其父类的流量整形功能.
9.
5.
3.
PRIO队列规定PRIO队列规定并不进行整形,它仅仅根据你配置的过滤器把流量进一步细分.
你可以认为PRIO队列规定是pfifo_fast的一种衍生物,区别在每个频道都是一个单独的类,而非简单的FIFO.
当数据包进入PRIO队列规定后,将根据你给定的过滤器设置选择一个类.
缺省情况下有三个类,这些类仅包含纯FIFO队列规定而没有更多的内部结构.
你可以把它们替换成你需要的任何队列规定.
每当有一个数据包需要出队时,首先处理:1类.
只有当标号更小的类中没有需要处理的包时,才会标号大的类.
46当你希望不仅仅依靠包的TOS,而是想使用tc所提供的更强大的功能来进行数据包的优先权划分时,可以使用这个队列规定.
它也可以包含更多的队列规定,而pfifo_fast却只能包含简单的fifo队列规定.
因为它不进行整形,所以使用时与SFQ有相同的考虑:要么确保这个网卡的带宽确实已经占满,要么把它包含在一个能够整形的分类的队列规定的内部.
后者几乎涵盖了所有cablemodems和DSL设备.
严格地说,PRIO队列规定是一种Work-Conserving调度.
9.
5.
3.
1.
PRIO的参数与使用tc识别下列参数:bands创建频道的数目.
每个频道实际上就是一个类.
如果你修改了这个数值,你必须同时修改:priomap如果你不给tc提供任何过滤器,PRIO队列规定将参考TC_PRIO的优先级来决定如何给数据包入队.
它的行为就像前面提到过的pfifo_fast队列规定,关于细节参考前面章节.
频道是类,缺省情况下命名为主标号:1到主标号:3.
如果你的PRIO队列规定是12:,把数据包过滤到12:1将得到最高优先级.
注意:0频道的次标号是1!
1频道的次标号是2,以此类推.
9.
5.
3.
2.
配置范例我们想创建这个树:root1:prio/|\1:11:21:3|||10:20:30:sfqtbfsfqband012大批量数据使用30:,交互数据使用20:或10:.
命令如下:#tcqdiscadddeveth0roothandle1:prio##这个命令立即创建了类:1:1,1:2,1:3#tcqdiscadddeveth0parent1:1handle10:sfq#tcqdiscadddeveth0parent1:2handle20:tbfrate20kbitbuffer1600limit3000#tcqdiscadddeveth0parent1:3handle30:sfq现在,我们看看结果如何:47#tc-sqdisclsdeveth0qdiscsfq30:quantum1514bSent0bytes0pkts(dropped0,overlimits0)qdisctbf20:rate20Kbitburst1599blat667.
6msSent0bytes0pkts(dropped0,overlimits0)qdiscsfq10:quantum1514bSent132bytes2pkts(dropped0,overlimits0)qdiscprio1:bands3priomap1222120011111111Sent174bytes3pkts(dropped0,overlimits0)如你所见,0频道已经有了一些流量,运行这个命令之后发送了一个包!
现在我们来点大批量数据传输(使用能够正确设置TOS标记的工具):#scptcahu@10.
0.
0.
11:.
/ahu@10.
0.
0.
11'spassword:tc100%353KB00:00#tc-sqdisclsdeveth0qdiscsfq30:quantum1514bSent384228bytes274pkts(dropped0,overlimits0)qdisctbf20:rate20Kbitburst1599blat667.
6msSent2640bytes20pkts(dropped0,overlimits0)qdiscsfq10:quantum1514bSent2230bytes31pkts(dropped0,overlimits0)qdiscprio1:bands3priomap1222120011111111Sent389140bytes326pkts(dropped0,overlimits0)如你所见,所有的流量都是经过30:处理的,优先权最低.
现在我们验证一下交互数据传输经过更高优先级的频道,我们生成一些交互数据传输:#tc-sqdisclsdeveth0qdiscsfq30:quantum1514bSent384228bytes274pkts(dropped0,overlimits0)qdisctbf20:rate20Kbitburst1599blat667.
6msSent2640bytes20pkts(dropped0,overlimits0)qdiscsfq10:quantum1514bSent14926bytes193pkts(dropped0,overlimits0)qdiscprio1:bands3priomap1222120011111111Sent401836bytes488pkts(dropped0,overlimits0)正常——所有额外的流量都是经10:这个更高优先级的队列规定处理的.
与先前的整个scp不同,没有数据经过最低优先级的队列规定.
9.
5.
4.
著名的CBQ队列规定如前所述,CBQ是最复杂、最琐碎、最难以理解、最刁钻的队列规定.
这并不是因为其作者的恶毒或者不称职,而是因为CBQ算法本身的不精确,而且与Linux的内在机制不协调造成的.
48除了可以分类之外,CBQ也是一个整形器,但是从表面上看来工作得并不好.
它应该是这样的:如果你试图把一个10Mbps的连接整形成1Mbps的速率,就应该让链路90%的时间处于闲置状态,必要的话我们就强制,以保证90%的闲置时间.
但闲置时间的测量非常困难,所以CBQ就采用了它一个近似值——来自硬件层的两个传输请求之间的毫秒数——来代替它.
这个参数可以近似地表征这个链路的繁忙程度.
这样做相当慎重,而且不一定能够得到正确的结论.
比如,由于驱动程序方面或者其它原因造成一块网卡的实际传输速率不能够达到它的标称速率,该怎么办由于总线设计的原因,PCMCIA网卡永远也不会达到100Mbps.
那么我们该怎么计算闲置时间呢如果我们引入非物理网卡——像PPPoE、PPTP——情况会变得更糟糕.
因为相当一部分有效带宽耗费在了链路维护上.
那些实现了测量的人们都发现CBQ总不是非常精确甚至完全失去了其本来意义.
但是,在很多场合下它还是能够很好地工作.
根据下面的文档,你应该能够较好地配置CBQ来解决答多数问题.
9.
5.
4.
1.
CBQ整形的细节如前所述,CBQ的工作机制是确认链路的闲置时间足够长,以达到降低链路实际带宽的目的.
为此,它要计算两个数据包的平均发送间隔.
操作期间,有效闲置时间的测量使用EWMA(exponentialweightedmovingaverage,指数加权移动均值)算法,也就是说最近处理的数据包的权值比以前的数据包按指数增加.
UNIX的平均负载也是这样算出来的.
计算出来的平均时间值减去EWMA测量值,得出的结果叫做"avgidle".
最佳的链路负载情况下,这个值应当是0:数据包严格按照计算出来的时间间隔到来.
在一个过载的链路上,avgidle值应当是负的.
如果这个负值太严重,CBQ就会暂时禁止发包,称为"overlimit"(越限).
相反地,一个闲置的链路应该有很大的avgidle值,这样闲置几个小时后,会造成链路允许非常大的带宽通过.
为了避免这种局面,我们用maxidle来限制avgidle的值不能太大.
理论上讲,如果发生越限,CBQ就会禁止发包一段时间(长度就是事先计算出来的传输数据包之间的时间间隔),然后通过一个数据包后再次禁止发包.
但是最好参照一下下面的minburst参数.
下面是配置整形时需要指定的一些参数:avpkt平均包大小,以字节计.
计算maxidle时需要,maxidle从maxburst得出.
49bandwidth网卡的物理带宽,用来计算闲置时间.
cell一个数据包被发送出去的时间可以是基于包长度而阶梯增长的.
一个800字节的包和一个806字节的包可以认为耗费相同的时间.
也就是说它设置时间粒度.
通常设置为8,必须是2的整数次幂.
maxburst这个参数的值决定了计算maxidle所使用的数据包的个数.
在avgidle跌落到0之前,这么多的数据包可以突发传输出去.
这个值越高,越能够容纳突发传输.
你无法直接设置maxidle的值,必须通过这个参数来控制.
minburst如前所述,发生越限时CBQ会禁止发包.
实现这个的理想方案是根据事先计算出的闲置时间进行延迟之后,发一个数据包.
然而,UNIX的内核一般来说都有一个固定的调度周期(一般不大于10ms),所以最好是这样:禁止发包的时间稍长一些,然后突发性地传输minburst个数据包,而不是一个一个地传输.
等待的时间叫做offtime.
从大的时间尺度上说,minburst值越大,整形越精确.
但是,从毫秒级的时间尺度上说,就会有越多的突发传输.
minidle如果avgidle值降到0,也就是发生了越限,就需要等待,直到avgidle的值足够大才发送数据包.
为避免因关闭链路太久而引起的以外突发传输,在avgidle的值太低的时候会被强制设置为minidle的值.
参数minidle的值是以负微秒记的.
所以10代表avgidle被限制在-10us上.
mpu最小包尺寸——因为即使是0长度的数据包,在以太网上也要生成封装成64字节的帧,而需要一定时间去传输.
为了精确计算闲置时间,CBQ需要知道这个值.
rate期望中的传输速率.
也就是"油门"!
在CBQ的内部由很多的微调参数.
比如,那些已知队列中没有数据的类就不参加计算、越限的类将被惩罚性地降低优先级等等.
都非常巧妙和复杂.
9.
5.
4.
2.
CBQ在分类方面的行为除了使用上述idletime近似值进行整形之外,CBQ还可以象PRIO队列那样,把50各种类赋予不同的优先级,优先权数值小的类会比优先权值大的类被优先处理.
每当网卡请求把数据包发送到网络上时,都会开始一个WRR(weightedroundrobin,加权轮转)过程,从优先权值小的类开始.
那些队列中有数据的类就会被分组并被请求出队.
在一个类收到允许若干字节数据出队的请求之后,再尝试下一个相同优先权值的类.
下面是控制WRR过程的一些参数:allot当从外部请求一个CBQ发包的时候,它就会按照"priority"参数指定的顺序轮流尝试其内部的每一个类的队列规定.
当轮到一个类发数据时,它只能发送一定量的数据.
"allot"参数就是这个量的基值.
更多细节请参照"weight"参数.
prioCBQ可以象PRIO设备那样工作.
其中"prio"值较低的类只要有数据就必须先服务,其他类要延后处理.
weight"weight"参数控制WRR过程.
每个类都轮流取得发包的机会.
如果其中一个类要求的带宽显著地高于其他的类,就应该让它每次比其他的类发送更多的数据.
CBQ会把一个类下面所有的weight值加起来后归一化,所以数值可以任意定,只要保持比例合适就可以.
人们常把"速率/10"作为参数的值来使用,实际工作得很好.
归一化值后的值乘以"allot"参数后,决定了每次传输多少数据.
请注意,在一个CBQ内部所有的类都必须使用一致的主号码!
9.
5.
4.
3.
决定链路的共享和借用的CBQ参数除了纯粹地对某种数据流进行限速之外,CBQ还可以指定哪些类可以向其它哪些类借用或者出借一部分带宽.
Isolated/sharing凡是使用"isolated"选项配置的类,就不会向其兄弟类出借带宽.
如果你的链路上同时存在着竞争对手或者不友好的其它人,你就可以使用这个选项.
选项"sharing"是"isolated"的反义选项.
bounded/borrow一个类也可以用"bounded"选项配置,意味着它不会向其兄弟类借用带宽.
选项"borrow"是"bounded"的反义选项.
51一个典型的情况就是你的一个链路上有多个客户都设置成了"isolated"和"bounded",那就是说他们都被限制在其要求的速率之下,且互相之间不会借用带宽.
在这样的一个类的内部的子类之间是可以互相借用带宽的.
9.
5.
4.
4.
配置范例这个配置把WEB服务器的流量控制为5Mbps、SMTP流量控制在3Mbps上.
而且二者一共不得超过6Mbps,互相之间允许借用带宽.
我们的网卡是100Mbps的.
#tcqdiscadddeveth0roothandle1:0cbqbandwidth100Mbit\avpkt1000cell8#tcclassadddeveth0parent1:0classid1:1cbqbandwidth100Mbit\rate6Mbitweight0.
6Mbitprio8allot1514cell8maxburst20\avpkt1000bounded这部分按惯例设置了根为1:0,并且绑定了类1:1.
也就是说整个带宽不能超过6Mbps.
如前所述,CBQ需要调整很多的参数.
其实所有的参数上面都解释过了.
相应的HTB配置则要简明得多.
#tcclassadddeveth0parent1:1classid1:3cbqbandwidth100Mbit\rate5Mbitweight0.
5Mbitprio5allot1514cell8maxburst20\avpkt1000#tcclassadddeveth0parent1:1classid1:4cbqbandwidth100Mbit\rate3Mbitweight0.
3Mbitprio5allot1514cell8maxburst20\avpkt1000我们建立了2个类.
注意我们如何根据带宽来调整weight参数的.
两个类都没有配置成"bounded",但它们都连接到了类1:1上,而1:1设置了"bounded".
所以两个类的总带宽不会超过6Mbps.
别忘了,同一个CBQ下面的子类的主号码都必须与CBQ自己的号码相一致!
#tcqdiscadddeveth0parent1:3handle30:sfq#tcqdiscadddeveth0parent1:4handle40:sfq缺省情况下,两个类都有一个FIFO队列规定.
但是我们把它换成SFQ队列,以保证每个数据流都公平对待.
#tcfilteradddeveth0parent1:0protocolipprio1u32matchip\sport800xffffflowid1:3#tcfilteradddeveth0parent1:0protocolipprio1u32matchip\sport250xffffflowid1:4这些命令规定了根上的过滤器,保证数据流被送到正确的队列规定中去.
注意:我们先使用了"tcclassadd"在一个队列规定中创建了类,然后使用"tcqdiscadd"在类中创建队列规定.
你可能想知道,那些没有被那两条规则分类的数据流怎样处理了呢从这个例子来说,它们被1:0直接处理,没有限制.
如果SMTP+web的总带宽需求大于6Mbps,那么这6M带宽将按照两个类的52weight参数的比例情况进行分割:WEB服务器得到5/8的带宽,SMTP得到3/8的带宽.
从这个例子来说,你也可以这么认为:WEB数据流总是会得到5/8*6Mbps=3.
75Mbps的带宽.
9.
5.
4.
5.
其它CBQ参数:split和defmap如前所述,一个分类的队列规定需要调用过滤器来决定一个数据包应该发往哪个类去排队.
除了调用过滤器,CBQ还提供了其他方式,defmap和split.
很难掌握,但好在无关大局.
但是现在是解释defmap和split的最佳时机,我会尽力解释.
因为你经常是仅仅需要根据TOS来进行分类,所以提供了一种特殊的语法.
当CBQ需要决定了数据包要在哪里入队时,要检查这个节点是否为"split节点".
如果是,子队列规定中的一个应该指出它接收所有带有某种优先权值的数据包,权值可以来自TOS字段或者应用程序设置的套接字选项.
数据包的优先权位与defmap字段的值进行"或"运算来决定是否存在这样的匹配.
换句话说,这是一个可以快捷创建仅仅匹配某种优先权值数据包的过滤器的方法.
如果defmap等于0xff,就会匹配所有包,0则是不匹配.
这个简单的配置可以帮助理解:#tcqdiscadddeveth1roothandle1:cbqbandwidth10Mbitallot1514\cell8avpkt1000mpu64#tcclassadddeveth1parent1:0classid1:1cbqbandwidth10Mbit\rate10Mbitallot1514cell8weight1Mbitprio8maxburst20\avpkt1000一个标准的CBQ前导.
Defmap参照TC_PRIO位(我从来不直接使用数字!
):TC_PRIO.
.
Num对应TOSBESTEFFORT0最高可靠性FILLER1最低成本BULK2最大吞吐量(0x8)INTERACTIVE_BULK4INTERACTIVE6最小延迟(0x10)CONTROL7TC_PRIO.
.
的数值对应它右面的bit.
关于TOS位如何换算成优先权值的细节可以参照pfifo_fast有关章节.
然后是交互和大吞吐量的类:#tcclassadddeveth1parent1:1classid1:2cbqbandwidth10Mbit\rate1Mbitallot1514cell8weight100Kbitprio3maxburst20\avpkt1000split1:0defmapc0#tcclassadddeveth1parent1:1classid1:3cbqbandwidth10Mbit\rate8Mbitallot1514cell8weight800Kbitprio7maxburst20\avpkt1000split1:0defmap3f53"split队列规定"是1:0,也就是做出选择的地方.
c0是二进制的11000000,3F是00111111,所以它们共同匹配所有的数据包.
第一个类匹配第7和第6位,也就是负责"交互"和"控制"的数据包.
第二个类匹配其余的数据包.
节点1:0现在应该有了这样一个表格:prioritysendto01:311:321:331:341:351:361:271:2为了更有趣,你还可以传递一个"change掩码",确切地指出你想改变哪个优先权值.
你只有在使用了"tcclasschange"的时候才需要.
比如,往1:2中添加besteffort数据流,应该执行:#tcclasschangedeveth1classid1:2cbqdefmap01/01现在,1:0上的优先权分布应该是:prioritysendto01:211:321:331:341:351:361:271:2求助:尚未测试过"tcclasschange",资料上这么写的.
9.
5.
5.
HTB(HierarchicalTokenBucket,分层的令牌桶)MartinDevera()正确地意识到CBQ太复杂,而且并没有按照多数常见情况进行优化.
他的Hierarchical能够很好地满足这样一种情况:你有一个固定速率的链路,希望分割给多种不同的用途使用.
为每种用途做出带宽承诺并实现定量的带宽借用.
HTB就象CBQ一样工作,但是并不靠计算闲置时间来整形.
它是一个分类的令牌桶过滤器.
它只有很少的参数,并且在它的网站能够找到很好的文档.
随着你的HTB配置越来越复杂,你的配置工作也会变得复杂.
但是使用CBQ的话,即使在很简单的情况下配置也会非常复杂!
HTB3(关于它的版本情况,请参阅它的网站)已经成了官方内核的一部分(2.
4.
20-pre1、2.
5.
31及其后).
然而,你可能仍然要为你的tc命令打上HTB3支持补丁,否则你的tc命令不理解HTB3.
如果你已经有了一个新版内核或者已经打了补丁,请尽量考虑使用HTB.
549.
5.
5.
1.
配置范例环境与要求与上述CBQ的例子一样.
#tcqdiscadddeveth0roothandle1:htbdefault30#tcclassadddeveth0parent1:classid1:1htbrate6mbitburst15k#tcclassadddeveth0parent1:1classid1:10htbrate5mbitburst15k#tcclassadddeveth0parent1:1classid1:20htbrate3mbitceil6mbitburst15k#tcclassadddeveth0parent1:1classid1:30htbrate1kbitceil6mbitburst15k作者建议2在那些类的下方放置SFQ:#tcqdiscadddeveth0parent1:10handle10:sfqperturb10#tcqdiscadddeveth0parent1:20handle20:sfqperturb10#tcqdiscadddeveth0parent1:30handle30:sfqperturb10添加过滤器,直接把流量导向相应的类:#U32="tcfilteradddeveth0protocolipparent1:0prio1u32"#$U32matchipdport800xffffflowid1:10#$U32matchipsport250xffffflowid1:20这就完了——没有没见过的或者没解释过的数字,没有不明意义的参数.
HTB完成得相当不错——如果10:和20:都得到了保证的速率,剩下的就是分割了,它们借用的比率是5:3,正如你其网的那样.
未被分类的流量被送到了30:,仅有一点点带宽,但是却可以任意借用剩下的带宽.
因为我们内部使用了SFQ,而可以公平发包.
9.
6.
使用过滤器对数据包进行分类为了决定用哪个类处理数据包,必须调用所谓的"分类器链"进行选择.
这个链中包含了这个分类队列规定所需的所有过滤器.
重复前面那棵树:根1:|_1:1_/|\/|\/|\10:11:12:/\/\10:110:212:112:2当一个数据包入队的时候,每一个分支处都会咨询过滤器链如何进行下一步.
典型的配置是在1:1处有一个过滤器把数据包交给12:,然后12:处的过滤器在把包交给12:2.
你可以把后一个过滤器同时放在1:1处,可因为…havingmorespecifictestslowerinthechain.
…而得到效率的提高.
55另外,你不能用过滤器把数据包向"上"送.
而且,使用HTB的时候应该把所有的规则放到根上!
再次强调:数据包只能向"下"进行入队操作!
只有处队的时候才会上到网卡所在的位置来.
他们不会落到树的最底层后送到网卡!
9.
6.
1.
过滤器的一些简单范例就象在"分类器"那章所解释的,借助一些复杂的语法你可以详细地匹配任何事情.
下面我们就开始,从简单地匹配一些比较明显的特征开始.
比方说,我们有一个PRIO队列规定,叫做"10:",包含3个类,我们希望把去往22口的数据流发送到最优先的频道中去.
应该这样设置过滤器:#tcfilteradddeveth0protocolipparent10:prio1u32\matchipdport220xffffflowid10:1#tcfilteradddeveth0protocolipparent10:prio1u32\matchipsport800xffffflowid10:1#tcfilteradddeveth0protocolipparent10:prio2flowid10:2什么意思呢是说:向eth0上的10:节点添加一个u32过滤规则,它的优先权是1:凡是去往22口(精确匹配)的IP数据包,发送到频道10:1.
向eth0上的10:节点添加一个u32过滤规则,它的优先权是1:凡是来自80口(精确匹配)的IP数据包,发送到频道10:1.
向eth0上的10:节点添加一个过滤规则,它的优先权是2:凡是上面未匹配的IP数据包,发送到频道10:2.
别忘了添加"deveth0"(你的网卡或许叫别的名字),因为每个网卡的句柄都有完全相同的命名空间.
想通过IP地址进行筛选的话,这么敲:#tcfilteradddeveth0parent10:0protocolipprio1u32\matchipdst4.
3.
2.
1/32flowid10:1#tcfilteradddeveth0parent10:0protocolipprio1u32\matchipsrc1.
2.
3.
4/32flowid10:1#tcfilteradddeveth0protocolipparent10:prio2\flowid10:2这个例子把去往4.
3.
2.
1和来自1.
2.
3.
4的数据包送到了最高优先的队列,其它的则送到次高权限的队列.
你可以连续使用match,想匹配来自1.
2.
3.
4的80口的数据包的话,就这么敲:56#tcfilteradddeveth0parent10:0protocolipprio1u32matchipsrc4.
3.
2.
1/32matchipsport800xffffflowid10:19.
6.
2.
常用到的过滤命令一览这里列出的绝大多数命令都根据这个命令改编而来:#tcfilteradddeveth0parent1:0protocolipprio1u32……这些是所谓的"u32"匹配,可以匹配数据包的任意部分.
根据源/目的地址源地址段'matchipsrc1.
2.
3.
0/24'目的地址段'matchipdst4.
3.
2.
0/24'单个IP地址使用"/32"作为掩码即可.
根据源/目的端口,所有IP协议源'matchipsport800xffff'目的'matchipdport800xffff'根据IP协议(tcp,udp,icmp,gre,ipsec)使用/etc/protocols所指定的数字.
比如:icmp是1:'matchipprotocol10xff'.
根据fwmark你可以使用ipchains/iptables给数据包做上标记,并且这个标记会在穿过网卡的路由过程中保留下来.
如果你希望对来自eth0并从eth1发出的数据包做整形,这就很有用了.
语法是这样的:tcfilteradddeveth1protocolipparent1:0prio1handle6fwflowid1:1注意,这不是一个u32匹配!
你可以象这样给数据包打标记:#iptables-APREROUTING-tmangle-ieth0-jMARK--set-mark6数字6是可以任意指定的.
如果你不想去学习所有的tc语法,就可以与iptables结合,仅仅学习按fwmark匹配就行了.
按TOS字段选择交互和最小延迟的数据流:#tcfilteradddevppp0parent1:0protocolipprio10u32\57matchiptos0x100xffflowid1:4想匹配大量传输的话,使用"0x080xff".
关于更多的过滤命令,请参照"高级过滤"那一章.
9.
7.
IMQ(Intermediatequeueingdevice,中介队列设备)中介队列设备不是一个队列规定,但它的使用与队列规定是紧密相连的.
就Linux而言,队列规定是附带在网卡上的,所有在这个网卡上排队的数据都排进这个队列规定.
根据这个概念,出现了两个局限:1.
只能进行出口整形(虽然也存在入口队列规定,但在上面实现分类的队列规定的可能性非常小).
2.
一个队列规定只能处理一块网卡的流量,无法设置全局的限速.
IMQ就是用来解决上述两个局限的.
简单地说,你可以往一个队列规定中放任何东西.
被打了特定标记的数据包在netfilter的NF_IP_PRE_ROUTING和NF_IP_POST_ROUTING两个钩子函数处被拦截,并被送到一个队列规定中,该队列规定附加到一个IMQ设备上.
对数据包打标记要用到iptables的一种处理方法.
这样你就可以对刚刚进入网卡的数据包打上标记进行入口整形,或者把网卡们当成一个个的类来看待而进行全局整形设置.
你还可以做很多事情,比如:把http流量放到一个队列规定中去、把新的连接请求放到一个队列规定中去、……9.
7.
1.
配置范例我们首先想到的是进行入口整形,以便让你自己得到高保证的带宽.
就象配置其它网卡一样:tcqdiscadddevimq0roothandle1:htbdefault20tcclassadddevimq0parent1:classid1:1htbrate2mbitburst15ktcclassadddevimq0parent1:1classid1:10htbrate1mbittcclassadddevimq0parent1:1classid1:20htbrate1mbittcqdiscadddevimq0parent1:10handle10:pfifotcqdiscadddevimq0parent1:20handle20:sfqtcfilteradddevimq0parent10:0protocolipprio1u32match\ipdst10.
0.
0.
230/32flowid1:10在这个例子中,使用了u32进行分类.
其它的分类器应该也能实现.
然后,被打上标记的包被送到imq0排队.
iptables-tmangle-APREROUTING-ieth0-jIMQ--todev058iplinksetimq0upiptables的IMQ处理方法只能用在PREROUTING和POSTROUTING链的mangle表中.
语法是:IMQ[--todevn]n:imq设备的编号注:ip6tables也提供了这种处理方法.
请注意,如果数据流是事后才匹配到IMQ处理方法上的,数据就不会入队.
数据流进入imq的确切位置取决于这个数据流究竟是流进的还是流出的.
下面是netfilter(也就是iptables)在内核中预先定义优先级:enumnf_ip_hook_priorities{NF_IP_PRI_FIRST=INT_MIN,NF_IP_PRI_CONNTRACK=-200,NF_IP_PRI_MANGLE=-150,NF_IP_PRI_NAT_DST=-100,NF_IP_PRI_FILTER=0,NF_IP_PRI_NAT_SRC=100,NF_IP_PRI_LAST=INT_MAX,};对于流入的包,imq把自己注册为优先权等于NF_IP_PRI_MANGLE+1,也就是说数据包在经过了PREROUTING链的mangle表之后才进入imq设备.
对于流出的包,imq使用优先权等于NF_IP_PRI_LAST,也就是说不会白白处理本应该被filter表丢弃的数据包.
关于补丁和更多的文档请参阅imq网站.
59第10章多网卡的负载均衡有多种手段实现这个功能.
最简单、最直接的方法之一就是"TEQL"——真(或"普通的")链路均衡.
就象用队列实现的大多数事情一样,负载均衡也需要双向实现.
链路的两端都要参与,才有完整的效果.
想象下列情况:eth1"网络1"A||B|----'网络2'eth2A和B是路由器,我们当然假定它们全是Linux机器.
如果从网络1发往网络2的流量需要A路由器同时使用两条链路发给B路由器.
B路由器需要进行配置以便适应这种情况.
反向传输时也一样,当数据包从网络2发往网络1时,B路由器同时使用eth1和eth2.
分配的功能是用"TEQL"设备实现的,象这样(没有比这更简单的了):#tcqdiscadddeveth1rootteql0#tcqdiscadddeveth2rootteql0#iplinksetdevteql0up别忘了"iplinksetup"命令!
这在两台机器上都要做.
teql0设备基本上是在eth1和eth2之间进行轮转发帧.
用源也不会有数据从teql设备上进来,只是出现在原来的eth1和eth2上.
我们现在有了网络设备,还需要有合适的路由.
方法之一就是给两个链路分配一个/31的网络,teql0也一样:在A路由器上:#ipaddradddeveth110.
0.
0.
0/31#ipaddradddeveth210.
0.
0.
2/31#ipaddradddevteql010.
0.
0.
4/31在B路由器上:#ipaddradddeveth110.
0.
0.
1/31#ipaddradddeveth210.
0.
0.
3/31#ipaddradddevteql010.
0.
0.
5/31A路由器现在应该能够通过2个真实链路和一个均衡网卡ping通10.
0.
0.
1、10.
0.
0.
3和10.
0.
0.
5.
B路由器应该能够ping通10.
0.
0.
0、10.
0.
0.
2和10.
0.
0.
4.
如果成功的话,A路由器应该把10.
0.
0.
5作为到达网络2的路由,B路由器应该把10.
0.
0.
4作为去往网络1的路由.
在网络1是你家里的网络,而网络2是Internet这种特定场合下,A路由器的缺省网关应该设为10.
0.
0.
5.
6010.
1.
告诫事情永远不会是表面看上去那样简单.
A路由器和B路由器上的eth1和eth2需要关闭"返回路径过滤",否则它们会丢弃那些返回地址不同于其源地址的数据包:#echo0>/proc/sys/net/ipv4/conf/eth1/rp_filter#echo0>/proc/sys/net/ipv4/conf/eth2/rp_filter包的乱序也是一个大问题.
比如,有6个数据包需要从A发到B,eth1可能分到第1、3、5个包,而eth2分到第2、4、6个.
在理想情况下,B路由器会按顺序收到第1、2、3、4、5、6号包.
但实际上B路由器的内核很可能按照类似2、1、4、3、6、5这样的随机顺序收到包.
这个问题会把TCP/IP搞糊涂.
虽然在链路上承载不同的TCP/IP会话并没有问题,但你无法通过捆绑多个链路来增加一个ftp文件的下载速度,除非两端的操作系统都是Linux,因为Linux的TCP/IP协议栈不那么容易被这种简单的乱序问题所蒙蔽.
当然,对于大多数应用系统来说,链路的负载均衡是一个好主意.
10.
2.
其它可能性WilliamStearns已经利用高级隧道来达到捆绑多重Internet连接的效果.
可以在他的隧道网页找到.
本HOWTO将来可能更多地描述这个问题.
61第11章Netfilter和iproute——给数据包作标记迄今为止我们已经了解了iproute是如何工作的,并且多次提到了netfilter.
这里,你正好可以趁机看一看Rusty出名地不可靠的指南.
Netfilter本身可以在这里找到.
Netfilter可以让我们进行包过滤或者篡改数据包头.
有一个特别的功能就是我们可以给数据包打上一个数字标记.
使用--set-mark机制就可以.
例如,这个命令把所有发往25/tcp(发出邮件)的数据包都打上了标记:#iptables-APREROUTING-ieth0-tmangle-ptcp--dport25\-jMARK--set-mark1比如说,我们有多个连接,一个是按流量计费的,比较贵但是很快,另一个是包月的,但是比较慢.
我们当然愿意让发送电子邮件的数据包走那条比较便宜的路由.
我们已经给那些数据包打上了标记"1",我们现在命令路由策略数据库实现这个功能:#echo201mail.
out>>/etc/iproute2/rt_tables#ipruleaddfwmark1tablemail.
out#iprulels0:fromalllookuplocal32764:fromallfwmark1lookupmail.
out32766:fromalllookupmain32767:fromalllookupdefault现在我们建立一个通往那条便宜链路的路由,从而生成mail.
out路由表:#/sbin/iprouteadddefaultvia195.
96.
98.
253devppp0tablemail.
out这就做完了.
我们可能需要一些例外,有很多方法都能达到目的.
我们可以修改netfilter命令来排除一些主机,也可以插入一些优先权值更低的规则把需要排除的主机的数据包发往main路由表.
我们还可以通过识别数据包的TOS位,来给不同服务类型的数据包打上不同的标记,再为它们分别建立规则.
你甚至可以利用这种方法让诸如ISDN线路支持交互业务.
62不用说,这当然也可以用于正在进行NAT("伪装")的机器上.
重要提醒:我们收到报告说MASQ和SNAT功能与数据包标记有冲突.
RustyRussell在这个帖子中作了解释.
关闭反方向的过滤就可以正常工作.
注意:想给数据包打标记的话,你的内和需要一些配置:IP:advancedrouter(CONFIG_IP_ADVANCED_ROUTER)[Y/n/]IP:policyrouting(CONFIG_IP_MULTIPLE_TABLES)[Y/n/]IP:usenetfilterMARKvalueasroutingkey(CONFIG_IP_ROUTE_FWMARK)[Y/n/]参考方便菜谱一章中的15.
5.
63第12章对包进行分类的高级过滤器就象在分类的队列规定一段中解释的,过滤器用与把数据包分类并放入相应的子队列.
这些过滤器在分类的队列规定内部被调用.
下面就是我们可用的分类器(部分):fw根据防火墙如何对这个数据包做标记进行判断.
如果你不想学习tc的过滤器语法,这倒是一个捷径.
细节请参见队列那一章.
u32根据数据包中的各个字段进行判断,如源IP地址等等.
route根据数据包将被哪条路由进行路由来判断.
rsvp,rsvp6根据数据包的RSVP情况进行判断.
只能用于你自己的网络,互联网并不遵守RSVP.
tcindex用于DSMARK队列规定,参见相关章节.
通常来说,总有很多途径可实现对数据包分类,最终取决于你喜欢使用哪种系统.
分类器一般都能接受几个参数,为了方便我们列出来:protocol这个分类器所接受的协议.
一般来说你只会接受IP数据.
必要参数.
parent这个分类器附带在哪个句柄上.
句柄必须是一个已经存在的类.
必要参数.
prio这个分类器的优先权值.
优先权值低的优先.
handle对于不同过滤器,它的意义不同.
后面所有的节都假定你试图对去往HostA的流量进行整形.
并且假定根类配置为1:,并且你希望把选中的数据包送给1:1类.
6412.
1.
u32分类器U32分类器是当前实现中最先进的过滤器.
全部基于哈希表实现,所以当有很多过滤器的时候仍然能够保持健壮.
U32过滤器最简单的形式就是一系列记录,每条记录包含两个部分:一个选择器和一个动作.
下面要讲的选择器用来与IP包相匹配,一旦成功匹配就执行其指定的动作.
最简单的动作就是把数据包发送到特定的类队列.
用来配置过滤器的tc命令行由三部分组成:过滤器说明、选择器和动作.
一个过滤器可以如下定义:tcfilteradddevIF[protocolPROTO][(preference|priority)PRIO][parentCBQ]上面行中,protocol字段描述了过滤器要匹配的协议.
我们将只讨论IP协议的情况.
preference字段(也可以用priority代替)设置该过滤器的优先权.
这非常重要,因为你可能有几条拥有不同优先权的过滤器.
每个过滤器列表都按照输入的顺序被扫描一遍,然后优先权值低(更高的偏好值)的列表优先被处理.
"parent"字段定义了过滤器所属的CBQ的顶部(如1:0).
上面描述的选项适用于所有过滤器,而不仅仅适用于U32.
12.
1.
1.
U32选择器u32选择器包含了能够对当前通过的数据包进行匹配的特征定义.
它其实只是定义了IP包头中某些位的匹配而已,但这种看似简单的方法却非常有效.
让我们看看这个从实际应用的系统中抄来的例子:#tcfilteradddeveth0protocolipparent1:0pref10u32\matchu320010000000ff0000at0flowid1:10现在,命令的第一行已经不用解释了,前面都说过了.
我们把精力集中在用"match"选项描述选择器的第二行.
这个选择器将匹配那些IP头部的第二个字节是0x10的数据包.
你应该猜到了,00ff就是匹配掩码,确切地告诉过滤器应该匹配哪些位.
在这个例子中是0xff,所以会精确地匹配这个字节是否等于0x10.
"at"关键字的意思是指出从数据包的第几个字节开始匹配——本例中是从数据包的开头开始.
完全地翻译成人类语言就是:"匹配那些TOS字段带有'最小延迟'属性的数据包".
让我们看看另一个例子:#tcfilteradddeveth0protocolipparent1:0pref10u32\matchu32000000160000ffffatnexthdr+0flowid1:10"nexthdr"选项意味着封装在IP包中的下一个PDU的头部,也就是说它的上层协议的头.
匹配操作就是从这个协议的头部开始的,应该发生在头部开始的第16位处.
在TCP和UDP协议的头部,这个部分存放的是这个报文的目标端口.
数字是按照先高厚低的格式存储的,所以0x0016就是十进制的22(如果是TCP的话就是ssh服务).
其实,这个匹配在没有上下文的情况下含义很模糊,我们放65在后面讨论.
理解了上面的例子之后,下面这条选择器就很好懂了:matchc0a80100ffffff00at16表示了:匹配从IP头开始数的第17个字节到第19个字节.
这个选择器将匹配所有去往192.
168.
1.
0/24的数据包.
成功分析完上面这个例子后,我们就已经掌握u32选择器了.
12.
1.
2.
普通选择器普通选择器定义了要对数据包进行匹配的特征、掩码和偏移量.
使用普通选择器,你实际上可以匹配IP(或者上层协议)头部的任意一个bit,虽然这样的选择器比特殊选择器难读和难写.
一般选择器的语法是:match[u32|u16|u8]PATTERNMASK[atOFFSET|nexthdr+OFFSET]利用u32、u16或u8三个关键字中的一个来指明特征的bit数.
然后PATTERN和MASK应该按照它定义的长度紧挨着写.
OFFSET参数是开始进行比较的偏移量(以字节计).
如果给出了"nexthdr+"关键字,偏移量就移到上层协议头部开始的位置.
一些例子:#tcfilteradddevppp14parent1:0prio10u32\matchu8640xffat8\flowid1:4如果一个数据包的TTL值等于64,就将匹配这个选择器.
TTL就位于IP包头的第9个字节.
匹配带有ACK位的TCP数据包:#tcfilteradddevppp14parent1:0prio10u32\matchipprotocol60xff\matchu80x100xffatnexthdr+13\flowid1:3用这个匹配小于64字节的ACK包:##matchacksthehardway,##IPprotocol6,##IPheaderlength0x5(32bitwords),##IPTotallength0x34(ACK+12bytesofTCPoptions)##TCPackset(bit5,offset33)#tcfilteradddevppp14parent1:0protocolipprio10u32\matchipprotocol60xff\matchu80x050x0fat0\matchu160x00000xffc0at2\matchu80x100xffat33\flowid1:3这个规则匹配了带有ACK位,且没有载荷的TCP数据包.
这里我们看见了同时使用两个选择器的例子,这样用的结果是两个条件进行逻辑"与"运算.
如果我们查查TCP头的结构,就会知道ACK标志位于第14个字节的第5个bit(0x10).
66作为第二个选择器,如果我们采用更复杂的表达,可以写成"matchu80x060xffat9",而不是使用特殊选择器protocol,因为TCP的协议号是6(写在IP头的第十个字节).
另一方面,在这个例子中我们不使用特殊选择器也是因为没有用来匹配TCP的ACK标志的特殊选择器.
下面这个选择器是上面选择器的改进版,区别在于它不检查IP头部的长度.
为什么呢因为上面的过滤器只能在32位系统上工作.
tcfilteradddevppp14parent1:0protocolipprio10u32\matchipprotocol60xff\matchu80x100xffatnexthdr+13\matchu160x00000xffc0at2\flowid1:312.
1.
3.
特殊选择器下面的表收入了本节文档的作者从tc程序的源代码中找出的所有特殊选择器.
它们能够让你更容易、更可靠地配置过滤器.
求助:tableplaceholder-thetableisinseparatefile,,selector.
html''求助:it'salsostillinPolish:-(求助:mustbesgml'ized一些范例:#tcfilteradddevppp0parent1:0prio10u32\matchiptos0x100xff\flowid1:4求助:tcpdportmatchdoesnotworkasdescribedbelow:上述规则匹配那些TOS字段等于0x10的数据包.
TOS字段位于数据包的第二个字节,所以与值等价的普通选择器就是:"matchu80x100xffat1".
这其实给了我们一个关于U32过滤器的启示:特殊选择器全都可以翻译成等价的普通选择器,而且在内核的内存中,恰恰就是按这种方式存储的.
这也可以导出另一个结论:tcp和udp的选择器实际上是完全一样的,这也就是为什么不能仅用"matchtcpdport530xffff"一个选择器去匹配发到指定端口的TCP包,因为它也会匹配送往指定端口的UDP包.
你一定不能忘了还得匹配协议类型,按下述方式来表示:#tcfilteradddevppp0parent1:0prio10u32\matchtcpdport530xffff\matchipprotocol0x60xff\flowid1:212.
2.
路由分类器这个分类器过滤器基于路由表的路由结果.
当一个数据包穿越一个类,并到达一个标有"route"的过滤器的时候,它就会按照路由表内的信息进行分裂.
当一个67数据包遍历类,并到达一个标记"路由"过滤器的时候,就会按照路由表的相应信息分类.
#tcfilteradddeveth1parent1:0protocolipprio100route我们向节点1:0里添加了一个优先级是100的路由分类器.
当数据包到达这个节点时,就会查询路由表,如果匹配就会被发送到给定的类,并赋予优先级100.
要最后完成,你还要添加一条适当的路由项:这里的窍门就是基于目的或者源地址来定义"realm".
象这样做:#iprouteaddHost/NetworkviaGatewaydevDevicerealmRealmNumber例如,我们可以把目标网络192.
168.
10.
0定义为realm10:#iprouteadd192.
168.
10.
0/24via192.
168.
10.
1deveth1realm10我们再使用路由过滤器的时候,就可以用realm号码来表示网络或者主机了,并可以用来描述路由如何匹配过滤器:#tcfilteradddeveth1parent1:0protocolipprio100\routeto10classid1:10这个规则说:凡是去往192.
168.
10.
0子网的数据包匹配到类1:10.
路由过滤器也可以用来匹配源策略路由.
比如,一个Linux路由器的eth2上连接了一个子网:#iprouteadd192.
168.
2.
0/24deveth2realm2#tcfilteradddeveth1parent1:0protocolipprio100\routefrom2classid1:2这个规则说:凡是来自192.
168.
2.
0子网(realm2)的数据包,匹配到1:2.
12.
3.
管制分类器为了能够实现更复杂的配置,你可以通过一些过滤器来匹配那些达到特定带宽的数据包.
你可以声明一个过滤器来来按一定比率抑制传输速率,或者仅仅不匹配那些超过特定速率的数据包.
如果现在的流量是5M,而你想把它管制在4M,那么你要么可以停止匹配整个的5M带宽,要么停止匹配1M带宽,来给所配置的类进行4M速率的传输.
如果带宽超过了配置的速率,你可以丢包、可以重新分类或者看看是否别的过滤器能匹配它.
12.
3.
1.
管制的方式有两种方法进行管制.
如果你编译内核的时候加上了"Estimators",内核就可以替你为每一个过滤器测量通过了多少数据,多了还是少了.
这些评估对于CPU来讲非常轻松,它只不过是每秒钟累计25次通过了多少数据,计算出速率.
68另一种方法是在你的过滤器内部,通过TBF(令牌桶过滤器)来实现.
TBF只匹配到达到你配置带宽的数据流,超过的部分则按照事先指定的"越限动作"来处理.
12.
3.
1.
1.
靠内核评估这种方式非常简单,只有一个参数"avrate".
所有低于avrate的数据包被保留,并被过滤器分到所指定的类中去,而那些超过了avrate的数据包则按照越限动作来处理,缺省的越限动作是"reclassify"(重分类).
内核使用EWMA算法来核算带宽,以防止对瞬时突发过于敏感.
12.
3.
1.
2.
靠令牌桶过滤器使用下列参数:buffer/maxburstmtu/minburstmpurate它们的意义与前面介绍TBF时所说的完全一样.
但仍然要指出的是:如果把一个TBF管制器的mtu参数设置过小的话,将没有数据包通过,whereastheegressTBFqdiscwilljustpassthemslower.
另一个区别是,管制器只能够通过或者丢弃一个数据包,而不能因为为了延迟而暂停发送.
12.
3.
2.
越限动作如果你的过滤器认定一个数据包越限,就会按照"越限动作"来处理它.
当前,支持三种动作:continue让这个过滤器不要匹配这个包,但是其它的过滤器可能会匹配它.
drop这是个非常强硬的选项——让越限的数据包消失.
它的用途不多,经常被用于ingress管制.
比如,你的DNS在请求流量大于5Mbps的时候就会失灵,你就可以利用这个来保证请求量不会超标.
Pass/OK让数据包通过.
可用于避免复杂的过滤器,但要放在合适的地方.
reclassify69最经常用于对数据包进行重分类以达到最好效果.
这是缺省动作.
12.
3.
3.
范例现在最真实的范例就是下面第十五章提到的"防护SYN洪水攻击".
求助:ifyouhaveusedthis,pleaseshareyourexperiencewithus12.
4.
当过滤器很多时如何使用散列表如果你需要使用上千个规则——比如你有很多需要不同QoS的客户机——你可能会发现内核花了很多时间用于匹配那些规则.
缺省情况下,所有的过滤器都是靠一个链表来组织的,链表按priority的降序排列.
如果你有1000个规则,那么就有可能需要1000次匹配来决定一个包如何处理.
而如果你有256个链表,每个链表4条规则的话,这个过程可以更快.
也就是说如果你能把数据包放到合适的链表上,可能只需要匹配4次就可以了.
利用散列表可以实现.
比如说你有1024个用户使用一个CableMODEM,IP地址范围是1.
2.
0.
0到1.
2.
3.
255,每个IP都需要不同容器来对待,比如"轻量级"、"中量级"和"重量级".
你可能要写1024个规则,象这样:#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
0.
0classid1:1#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
0.
1classid1:1.
.
.
#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
3.
254classid1:3#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
3.
255classid1:2为了提高效率,我们应该利用IP地址的后半部分作为散列因子,建立256个散列表项.
第一个表项里的规则应该是这样:#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
0.
0classid1:1#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
1.
0classid1:1#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
2.
0classid1:3#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
3.
0classid1:2下一个表项应该这么开始:#tcfilteradddeveth1parent1:0protocolipprio100matchipsrc\1.
2.
0.
1classid1:1.
.
.
这样的话,最坏情况下也只需要4次匹配,平均2次.
70具体配置有些复杂,但是如果你真有很多规则的话,还是值得的.
我们首先生成root过滤器,然后创建一个256项的散列表:#tcfilteradddeveth1parent1:0prio5protocolipu32#tcfilteradddeveth1parent1:0prio5handle2:protocolipu32divisor256然后我们向表项中添加一些规则:#tcfilteradddeveth1protocolipparent1:0prio5u32ht2:7b:\matchipsrc1.
2.
0.
123flowid1:1#tcfilteradddeveth1protocolipparent1:0prio5u32ht2:7b:\matchipsrc1.
2.
1.
123flowid1:2#tcfilteradddeveth1protocolipparent1:0prio5u32ht2:7b:\matchipsrc1.
2.
3.
123flowid1:3#tcfilteradddeveth1protocolipparent1:0prio5u32ht2:7b:\matchipsrc1.
2.
4.
123flowid1:2这是第123项,包含了为1.
2.
0.
123、1.
2.
1.
123、1.
2.
2.
123和1.
2.
3.
123准备的匹配规则,分别把它们发给1:1、1:2、1:3和1:2.
注意,我们必须用16进制来表示散列表项,0x7b就是123.
然后创建一个"散列过滤器",直接把数据包发给散列表中的合适表项:#tcfilteradddeveth1protocolipparent1:0prio5u32ht800::\matchipsrc1.
2.
0.
0/16\hashkeymask0x000000ffat12\link2:好了,有些数字需要解释.
我们定义散列表叫做"800:",所有的过滤都从这里开始.
然后我们选择源地址(它们位于IP头的第12、13、14、15字节),并声明我们只对它的最后一部分感兴趣.
这个例子中,我们发送到了前面创建的第2个散列表项.
这比较复杂,然而实际上确实有效而且性能令人惊讶.
注意,这个例子我们也可以处理成理想情况——每个表项中只有一个过滤器!
71第13章内核网络参数内核有很多可以在不同环境下调整的参数.
通常,预设的缺省值可以满足99%的环境要求,wedon'tcallthistheAdvancedHOWTOforthefunofit!
有个很有趣的地方:/proc/sys/net,你应该看看.
这里一开始并没有把所有的内容归档,但我们正在尽力如此.
有时候你需要看看Linux的内核源代码、读读Documentation/filesystems/proc.
txt.
绝大多数特性那里都有解释.
(求助)13.
1.
反向路径过滤缺省情况下,路由器路由一切包,即使某些数据包"明显地"不属于你的网络.
一个简单的例子就是私有IP空间溢出到了Internet上.
如果你有一个网卡带有去往195.
96.
96.
0/24的路由,你不会期望从这里收到来自212.
64.
94.
1的数据包.
很多人都希望关掉这个功能,所以内核作者们按照这种要求做了.
你可以利用/proc下的一些文件来配置内核是否使用这个功能.
这种方法就叫"反向路径过滤".
基本上说,就是如果一个数据包的回应包不是从它来的网卡发出去,就认为这是一个伪造包,应该丢弃.
下面的片段将在所有的(包括将来的)网卡上启用这个功能:#foriin/proc/sys/net/ipv4/conf/*/rp_filter;do>echo2>$i>done依照上面的例子,如果一个源地址是来自office+isp子网的数据包,却从Linux路由器的eth1口到达,那么它将被丢弃.
同理,如果一个来自office子网的数据包却声明它来自防火墙外面的某个地方,也会被丢弃.
上面说的是完整的反向路径过滤.
却省情况是只基于直接与网卡相接的子网地址进行过滤.
这是因为完整的反向路径过滤会破坏非对称路由(就是说数据包从一条路来而从另一条路离开——比如你在网络中使用了动态路由(bgp、ospf、rip)或者比如卫星通讯,数据从卫星下行到路由器,上行数据则经过地面线路传输.
).
如果你有这些情况,就可以简单地关掉卫星下行数据要进入那块网卡的rp_filter.
如果你想看一看丢弃了哪些包,可以通过相同目录下的log_martians文件通知内核向你的syslog中写日志.
#echo1>/proc/sys/net/ipv4/conf//log_martians求助:issettingtheconf/[default,all]/*filesenough-martijn7213.
2.
深层设置有很多参数可以修改.
我们希望能够全列出来.
在Documentation/ip-sysctl.
txt中也有部分记载.
这些设置中的部分缺省值取决于你在内核配置时是否选择了"Configureasrouterandnothost".
OskarAndreasson也有一个网页比我们讨论得更详细的网页:http://ipsysctl-tutorial.
frozentux.
net/13.
2.
1.
ipv4一般设置作为一个一般性的提醒,多数速度限制功能都不对loopback起作用,所以不要进行本地测试.
限制是由"jiffies"来提供的,并强迫使用前面提到过的TBF.
内核内部有一个时钟,每秒钟发生"HZ"个jiffies(滴嗒).
在Intel平台上,HZ的值一般都是100.
所以设置*_rate文件的时候,如果是50,就意味着每秒允许2个包.
TBF配置成为如果有多余的令牌就允许6个包的突发.
下面列表中的一些条目摘录自AlexeyKuznetsovkuznet@ms2.
inr.
ac.
ru和AndiKleenak@muc.
de写的/usr/src/linux/Documentation/networking/ip-sysctl.
txt.
/proc/sys/net/ipv4/icmp_destunreach_rate一旦内核认为它无法发包,就会丢弃这个包,并向发包的主机发送ICMP通知.
/proc/sys/net/ipv4/icmp_echo_ignore_all根本不要响应echo包.
请不要设置为缺省,它可能在你正被利用成为DoS攻击的跳板时可能有用.
/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts[Useful]如果你ping子网的子网地址,所有的机器都应该予以回应.
这可能成为非常好用的拒绝服务攻击工具.
设置为1来忽略这些子网广播消息.
/proc/sys/net/ipv4/icmp_echoreply_rate设置了向任意主机回应echo请求的比率.
/proc/sys/net/ipv4/icmp_ignore_bogus_error_responses设置它之后,可以忽略由网络中的那些声称回应地址是广播地址的主机生成的ICMP错误.
/proc/sys/net/ipv4/icmp_paramprob_rate一个相对不很明确的ICMP消息,用来回应IP头或TCP头损坏的异常数据包.
你可以通过这个文件控制消息的发送比率.
73/proc/sys/net/ipv4/icmp_timeexceed_rate这个在traceroute时导致著名的"Solarismiddlestar".
这个文件控制发送ICMPTimeExceeded消息的比率.
/proc/sys/net/ipv4/igmp_max_memberships主机上最多有多少个igmp(多播)套接字进行监听.
求助:Isthistrue/proc/sys/net/ipv4/inet_peer_gc_maxtime求助:AddalittleexplanationabouttheinetpeerstorageMinimumintervalbetweengarbagecollectionpasses.
Thisintervalisineffectunderlow(orabsent)memorypressureonthepool.
Measuredinjiffies.
/proc/sys/net/ipv4/inet_peer_gc_mintime每一遍碎片收集之间的最小时间间隔.
当内存压力比较大的时候,调整这个间隔很有效.
以jiffies计.
/proc/sys/net/ipv4/inet_peer_maxttlentries的最大生存期.
在pool没有内存压力的情况下(比如,pool中entries的数量很少的时候),未使用的entries经过一段时间就会过期.
以jiffies计.
/proc/sys/net/ipv4/inet_peer_minttlentries的最小生存期.
应该不小于汇聚端分片的生存期.
当pool的大小不大于inet_peer_threshold时,这个最小生存期必须予以保证.
以jiffies计.
/proc/sys/net/ipv4/inet_peer_thresholdTheapproximatesizeoftheINETpeerstorage.
Startingfromthisthresholdentrieswillbethrownaggressively.
Thisthresholdalsodeterminesentries'time-to-liveandtimeintervalsbetweengarbagecollectionpasses.
Moreentries,lesstime-to-live,lessGCinterval.
/proc/sys/net/ipv4/ip_autoconfig这个文件里面写着一个数字,表示主机是否通过RARP、BOOTP、DHCP或者其它机制取得其IP配置.
否则就是0.
/proc/sys/net/ipv4/ip_default_ttl数据包的生存期.
设置为64是安全的.
如果你的网络规模巨大就提高这个值.
不要因为好玩而这么做——那样会产生有害的路由环路.
实际上,在很多情况下你要考虑能否减小这个值.
/proc/sys/net/ipv4/ip_dynaddr74如果你有一个动态地址的自动拨号接口,就得设置它.
当你的自动拨号接口激活的时候,本地所有没有收到答复的TCP套接字会重新绑定到正确的地址上.
这可以解决引发拨号的套接字本身无法工作,重试一次却可以的问题.
/proc/sys/net/ipv4/ip_forward内核是否转发数据包.
缺省禁止.
/proc/sys/net/ipv4/ip_local_port_range用于向外连接的端口范围.
缺省情况下其实很小:1024到4999.
/proc/sys/net/ipv4/ip_no_pmtu_disc如果你想禁止"沿途MTU发现"就设置它.
"沿途MTU发现"是一种技术,可以在传输路径上检测出最大可能的MTU值.
参见Cookbook一章中关于"沿途MTU发现"的内容.
/proc/sys/net/ipv4/ipfrag_high_thresh用于IP分片汇聚的最大内存用量.
分配了这么多字节的内存后,一旦用尽,分片处理程序就会丢弃分片.
Whenipfrag_high_threshbytesofmemoryisallocatedforthispurpose,thefragmenthandlerwilltosspacketsuntilipfrag_low_threshisreached.
/proc/sys/net/ipv4/ip_nonlocal_bind如果你希望你的应用程序能够绑定到不属于本地网卡的地址上时,设置这个选项.
如果你的机器没有专线连接(甚至是动态连接)时非常有用,即使你的连接断开,你的服务也可以启动并绑定在一个指定的地址上.
/proc/sys/net/ipv4/ipfrag_low_thresh用于IP分片汇聚的最小内存用量.
/proc/sys/net/ipv4/ipfrag_timeIP分片在内存中的保留时间(秒数).
/proc/sys/net/ipv4/tcp_abort_on_overflow一个布尔类型的标志,控制着当有很多的连接请求时内核的行为.
启用的话,如果服务超载,内核将主动地发送RST包.
/proc/sys/net/ipv4/tcp_fin_timeout如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间.
对端可以出错并永远不关闭连接,甚至意外当机.
缺省值是60秒.
2.
2内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN-WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.
5K内存,但是它们的生存期长些.
参见tcp_max_orphans.
75/proc/sys/net/ipv4/tcp_keepalive_time当keepalive起用的时候,TCP发送keepalive消息的频度.
缺省是2小时.
/proc/sys/net/ipv4/tcp_keepalive_intvl当探测没有确认时,重新发送探测的频度.
缺省是75秒.
/proc/sys/net/ipv4/tcp_keepalive_probes在认定连接失效之前,发送多少个TCP的keepalive探测包.
缺省值是9.
这个值乘以tcp_keepalive_intvl之后决定了,一个连接发送了keepalive之后可以有多少时间没有回应.
/proc/sys/net/ipv4/tcp_max_orphans系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上.
如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息.
这个限制仅仅是为了防止简单的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后).
ThislimitexistsonlytopreventsimpleDoSattacks,you_must_notrelyonthisorlowerthelimitartificially,butratherincreaseit(probably,afterincreasinginstalledmemory),ifnetworkconditionsrequiremorethandefaultvalue,andtunenetworkservicestolingerandkillsuchstatesmoreaggressively.
让我再次提醒你:每个孤儿套接字最多能够吃掉你64K不可交换的内存.
/proc/sys/net/ipv4/tcp_orphan_retries本端试图关闭TCP连接之前重试多少次.
缺省值是7,相当于50秒~16分钟(取决于RTO).
如果你的机器是一个重载的WEB服务器,你应该考虑减低这个值,因为这样的套接字会消耗很多重要的资源.
参见tcp_max_orphans.
/proc/sys/net/ipv4/tcp_max_syn_backlog记录的那些尚未收到客户端确认信息的连接请求的最大值.
对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128.
如果服务器不堪重负,试试提高这个值.
注意!
如果你设置这个值大于1024,最好同时调整include/net/tcp.
h中的TCP_SYNQ_HSIZE,以保证TCP_SYNQ_HSIZE*16≤tcp_max_syn_backlo,然后重新编译内核.
/proc/sys/net/ipv4/tcp_max_tw_buckets系统同时保持timewait套接字的最大数量.
如果超过这个数字,time-wait套接字将立刻被清除并打印警告信息.
这个限制仅仅是为了防止简单的DoS攻击,你绝对不能过分依靠它或者人为地减小这个值,如果网络实际需要大于缺省值,更应该增加这个值(如果增加了内存之后).
/proc/sys/net/ipv4/tcp_retrans_collapse为兼容某些糟糕的打印机设置的"将错就错"选项.
再次发送时,把数据包增大一些,来避免某些TCP协议栈的BUG.
76/proc/sys/net/ipv4/tcp_retries1在认定出错并向网络层提交错误报告之前,重试多少次.
缺省设置为RFC规定的最小值:3,相当于3秒~8分钟(取决于RIO).
/proc/sys/net/ipv4/tcp_retries2在杀死一个活动的TCP连接之前重试多少次.
RFC1122规定这个限制应该长于100秒.
这个值太小了.
缺省值是15,相当于13~30分钟(取决于RIO).
/proc/sys/net/ipv4/tcp_rfc1337这个开关可以启动对于在RFC1337中描述的"tcp的time-wait暗杀危机"问题的修复.
启用后,内核将丢弃那些发往time-wait状态TCP套接字的RST包.
却省为0.
/proc/sys/net/ipv4/tcp_sack特别针对丢失的数据包使用选择性ACK,这样有助于快速恢复.
/proc/sys/net/ipv4/tcp_stdurg使用TCP紧急指针的主机需求解释.
因为绝大多数主机采用BSD解释,所以如果你在Linux上打开它,可能会影响它与其它机器的正常通讯.
缺省是FALSE.
/proc/sys/net/ipv4/tcp_syn_retries在内核放弃建立连接之前发送SYN包的数量.
/proc/sys/net/ipv4/tcp_synack_retries为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK.
也就是所谓三次握手中的第二次握手.
这个设置决定了内核放弃连接之前发送SYN+ACK包的数量.
/proc/sys/net/ipv4/tcp_timestamps时间戳可以避免序列号的卷绕.
一个1Gbps的链路肯定会遇到以前用过的序列号.
时间戳能够让内核接受这种"异常"的数据包.
/proc/sys/net/ipv4/tcp_tw_recycle能够更快地回收TIME-WAIT套接字.
缺省值是1.
除非有技术专家的建议和要求,否则不应修改.
/proc/sys/net/ipv4/tcp_window_scaling一般来说TCP/IP允许窗口尺寸达到65535字节.
对于速度确实很高的网络而言这个值可能还是太小.
这个选项允许设置上G字节的窗口大小,有利于在带宽*延迟很大的环境中使用.
7713.
2.
2.
网卡的分别设置DEV可以是真实网卡名,或者是"all"、"default".
Defaulr同时将来才出现的网卡有效.
/proc/sys/net/ipv4/conf/DEV/accept_redirects如果路由器认定你使用不当(比如,它发现要在收到包的网卡上转发出数据包),它就会发送ICMP重定向报文.
然而这会引发一些轻微的安全问题,所以你可能希望关掉它,或者使用安全的重定向.
/proc/sys/net/ipv4/conf/DEV/accept_source_route已经不是那么常用了.
以前,你可以发出一个数据包,其中指出它应该路经的路由器的IP.
Linux可以设置为尊重这个IP选项.
/proc/sys/net/ipv4/conf/DEV/bootp_relay接受来自0.
b.
c.
d,去往非本地地址的数据包.
并假定BOOTP中继守护程序会得到并转发这个包.
缺省值为0.
至少到2.
2.
12为止这个功能还没有完成.
/proc/sys/net/ipv4/conf/DEV/forwarding在该网卡上启用或禁止IP转发.
/proc/sys/net/ipv4/conf/DEV/log_martians参见ReversePathFiltering.
/proc/sys/net/ipv4/conf/DEV/mc_forwarding是否在这个网卡上进行多播转发.
/proc/sys/net/ipv4/conf/DEV/proxy_arp如果设置为1,这个网卡将回应那些询问内核已知路由的IP地址的ARP请求.
这对于构建"ip伪网桥"非常有用.
在启用它之前,一定要确认你的子网掩码正确!
还要保证前面提到的rp_filter能够正确处理ARP请求!
16.
3用ARP代理实现伪网桥.
参见/proc/sys/net/ipv4/conf/DEV/rp_filterReversePathFiltering.
参见/proc/sys/net/ipv4/conf/DEV/secure_redirects只接受对于网关的ICMP重定向消息,网关写在缺省网关列表中.
缺省打开.
/proc/sys/net/ipv4/conf/DEV/send_redirects78我们是否发送上述重定向.
/proc/sys/net/ipv4/conf/DEV/shared_media告诉内核这个设备上是否有能够直接访问到的不同子网.
缺省是"yes".
/proc/sys/net/ipv4/conf/DEV/tag求助:fillthisin13.
2.
3.
邻居策略Dev可以是标准的真实网卡,也可以是"all"或者"default".
default也会修改将要生成的网卡的设置.
/proc/sys/net/ipv4/neigh/DEV/anycast_delay回答邻居请求消息前的随机延迟的最大值,以jiffies计.
尚未完成(Linux还不支持anycast).
/proc/sys/net/ipv4/neigh/DEV/app_solicit决定了发往用户级ARP守护程序的请求数量.
设为0等于关闭.
/proc/sys/net/ipv4/neigh/DEV/base_reachable_time一个根据RFC2461来计算随机可到达时间的基值.
/proc/sys/net/ipv4/neigh/DEV/delay_first_probe_time第一次探测邻机是否可到达的延迟.
(参见gc_stale_time)/proc/sys/net/ipv4/neigh/DEV/gc_stale_time决定了检查陈旧ARP条目的频度.
当一个ARP条目老化之后将被重新解析(尤其是一个IP地址转移到另一台机器上的时候).
如果ucast_solicit值大于0,就会先试图向已知主机发送一个单播ARP包,如果失败并且mcast_solicit值大于0的话,在发送广播ARP包.
/proc/sys/net/ipv4/neigh/DEV/locktime如果一个ARP/neighbor条目超过了locktime,就将被一个新的条目取代.
这可以防止ARP缓冲过于臃肿.
/proc/sys/net/ipv4/neigh/DEV/mcast_solicit多播请求的最大重试次数.
/proc/sys/net/ipv4/neigh/DEV/proxy_delay在启用了ARP代理的网卡上,回应一个ARP请求前需要等待的时间(实际值是[0.
.
proxytime]区间的随机值).
在某些情况下,这可以避免网络洪水.
/proc/sys/net/ipv4/neigh/DEV/proxy_qlen79用于延迟代理ARP定时器的最大队列长度(参见proxy_delay).
/proc/sys/net/ipv4/neigh/DEV/retrans_time两次重发NeighborSolicitation消息之间的时间间隔,以jiffies计(1/100秒).
用于地址解析和确定邻居是否可到达.
/proc/sys/net/ipv4/neigh/DEV/ucast_solicit单播请求的最大次数.
/proc/sys/net/ipv4/neigh/DEV/unres_qlen存储未完成ARP请求的最大队列长度(包的个数).
未完成ARP请求:beacceptedfromotherlayerswhiletheARPaddressisstillresolved.
InternetQoS:ArchitecturesandMechanismsforQualityofService,ZhengWang,ISBN1-55860-608-4一本精装教科书,涵盖了与QoS相关的主题.
对于掌握基本概念很有好处.
13.
2.
4.
路由设置/proc/sys/net/ipv4/route/error_burst这些参数用来限制从路由代码写入内核日志的warning消息.
error_cost的值越高,写的消息越少.
error_burst控制什么时候应该丢弃消息.
缺省设置是限制为每5秒钟一次.
/proc/sys/net/ipv4/route/error_cost这些参数用来限制从路由代码写入内核日志的warning消息.
error_cost的值越高,写的消息越少.
error_burst控制什么时候应该丢弃消息.
缺省设置是限制为每5秒钟一次.
/proc/sys/net/ipv4/route/flush对这个文件的写操作导致刷新路由缓冲.
/proc/sys/net/ipv4/route/gc_elasticity一个数值,用来控制路由缓冲碎片收集算法的频度和行为.
用来设置失败恢复时非常重要.
一旦上一次路由操作失败,经过这么多秒后,Linux会跳过失败而转向其它路由.
缺省值为300,如果你想要更快速的失败恢复就可以降低这个值.
参见ArdvanBreemen的这篇帖子.
/proc/sys/net/ipv4/route/gc_interval参见/proc/sys/net/ipv4/route/gc_elasticity.
80/proc/sys/net/ipv4/route/gc_min_interval参见/proc/sys/net/ipv4/route/gc_elasticity.
/proc/sys/net/ipv4/route/gc_thresh参见/proc/sys/net/ipv4/route/gc_elasticity.
/proc/sys/net/ipv4/route/gc_timeout参见/proc/sys/net/ipv4/route/gc_elasticity.
/proc/sys/net/ipv4/route/max_delay刷新路由缓冲的延迟.
/proc/sys/net/ipv4/route/max_size路由缓冲的最大值.
一旦缓冲满,就清除旧的条目.
/proc/sys/net/ipv4/route/min_adv_mss求助:fillthisin/proc/sys/net/ipv4/route/min_delay刷新路由缓冲的延迟.
/proc/sys/net/ipv4/route/min_pmtu求助:fillthisin/proc/sys/net/ipv4/route/mtu_expires求助:fillthisin/proc/sys/net/ipv4/route/redirect_load决定是否要向特定主机发送更多的ICMP重定向的因子.
一旦达到load限制或者number限制就不再发送.
/proc/sys/net/ipv4/route/redirect_number参见/proc/sys/net/ipv4/route/redirect_load.
/proc/sys/net/ipv4/route/redirect_silence重定向的超时.
经过这么长时间后,重定向会重发,而不管是否已经因为超过load或者number限制而停止.
81第14章不经常使用的高级队列规定你应该发现有时候前面提到的那些队列不能满足你的需要,这里列出了一些内核包含的其它更多类型的队列.
14.
1.
bfifo/pfifo这些无类的队列因为没有内部频道而显得比pfifo_fast还要简单,所有的流量都均等处理.
但是它们还是有一个好处:他们可以做统计.
所以即使你不想整形或排序,你也可是使用这个队列规定来检查网卡的后台日志.
pfifo的长度单位以包的个数计,而bfifo以字节计.
14.
1.
1.
参数与使用limit规定了队列的长度.
对于bfifo用字节计,对于pfifo用包的个数计.
缺省值就是网卡的txqueuelen个包那么长(参见pfifo_fast那一章),对于bfifo就是txqueuelen*mtu个字节.
14.
2.
Clark-Shenker-Zhang算法(CSZ)它的理论性是如此之强,以至于连Alexey(CBQ的主要作者)都不指望去理解它.
从它的原始资料来看:DavidD.
Clark、ScottShenker和LixiaZhang在综合业务分组网络中支持实时应用系统:体系与机制.
据我的理解,它的主要意图是为每一个担保的服务创建WFQ流,并分配剩下的带宽给伪设备flow-0.
Flow-0由服务预测和bestefforttraffic组成,并由一个优先调度器来处理……(谁能帮忙翻一下).
AsIunderstandit,themainideaistocreateWFQflowsforeachguaranteedserviceandtoallocatetherestofbandwithtodummyflow-0.
Flow-0comprisesthepredictiveservicesandthebestefforttraffic;itishandledbyapriorityschedulerwiththehighestprioritybandallocatedforpredictiveservices,andtherest---tothebesteffortpackets.
NotethatinCSZflowsareNOTlimitedtotheirbandwidth.
Itis82supposedthattheflowpassedadmissioncontrolattheedgeoftheQoSnetworkanditdoesn'tneedfurthershaping.
Anyattempttoimprovetheflowortoshapeittoatokenbucketatintermediatehopswillintroduceundesireddelaysandraisejitter.
迄今为止,CSZ是唯一一个能够真正提供服务担保的调度器.
其它的方案(包括CBQ)不提供延迟保证和随机抖动.
"现在看来不是一个好的候选方案,除非你已经阅读并理解了上面提到的文章.
14.
3.
DSMARKEsteveCamps这些文字是从我2000年9月的《Linux的QoS》支持这篇论文上摘录的.
参考文档:Draft-almesberger-wajhak-diffserv-linux-01.
txt.
iproute2发行版中的范例.
QOS论坛的QoS协议与体系白皮书和IPQoS常见问题.
本章作者:EsteveCamps.
14.
3.
1.
介绍首先的首先,你应该到IETFDiffServ工作组的网站和WernerAlmesberger的网站(他为Linux的DifferentiatedServices撰写了代码)阅读一下有关的RFC(RFC2474、RFC2475、RFC2597和RFC2598).
.
14.
3.
2.
Dsmark与什么相关Dsmark是一个队列规定,提供了DifferentiatedServices(也叫DiffServ,简称为DS)所需要的能力.
DiffServ是两种QoS体系中的一种(另一种叫做IntegratedServices),基于IP头中DS字段的值来工作.
最开始的实现方案中的一种是IP头中的服务类型字段(TOS值),也就是设计由IP来提供一些QoS级别.
改变这个值,我们可以选择吞吐量、延迟或者可靠性的高/低级别.
但是这并没有提供足够的灵活性来应付新的服务(比如说实时应用、交互应用等等).
后来,新的体系出现了.
其中之一就是DiffServ,保留了TOS位并renamedDS字段.
8314.
3.
3.
DifferentiatedServices指导DifferentiatedServices是面向组的.
也就是说,我们没有必要了解数据流(那是IntegratedServices的风格),我们只了解"流会聚",并根据一个数据包属于哪个流会聚来采取不同的行动.
当一个数据包到达边缘节点(entrynodetoaDiffServdomain),并进入DiffServ域之后,我们就得进行对它们进行策略控制、整形或标记(关于标记请参考"给DS字段分配一个值".
就象一头牛一样.
).
这个值将被DiffServ域中的内部/核心代码参考,以决定如何处置或者适用什么QoS级别.
如你的推断,DS包括一个"域"的概念,他规定了所有DS规则的作用范围.
事实上你可以认为我把所有的数据包都分类到我的域中.
他们一旦进入我的域,就会根据分类而隶属于某条规则,每个所经过的节点都会应用那个QoS级别.
其实你可以在你的本地域内使用你自己的策略控制器,但当与其它的DS域相连时要考虑一些服务级别协议.
这里,你可能由很多问题.
DiffServ不仅限于我所解释的.
其实你应该能理解我不可能在50行中引用3个RFC.
14.
3.
4.
使用Dsmark根据DiffServ文档的说法,我们区分边界节点和内部节点.
这是传输路径上的两个关键点.
但数据包到达时二者都会对包进行分类.
在数据包被发送到网络上之前,它的分类结果可能会被这个DS过程的其他地方用到.
正是因此,Diffserv的代码提供了一个叫做sk_buff的结构,包含了一个新的字段叫做skb->tc_index用来存储一开始分类的结果,以便其它可能用到这个结果的地方引用.
skb->tc_index的值被DSMARK队列规定根据每个IP包头的DS字段的值进行初始设置.
此外,cls_tcindex分类器将读取skb->tcindex的全部或部分,并用来选择类.
但是,首先看一看DSMARK队列规定的命令行和参数:.
.
.
dsmarkindicesINDICES[default_indexDEFAULT_INDEX][set_tc_index]这些参数是什么意思呢indices:存储(掩码,数值)的表格尺寸.
最大值是2n(n≥0).
Default_index:当分类器没有找到匹配项时的缺省表格项索引.
Set_tc_index:给dsmark设置接收DS字段并存储到skb->tc_index规定.
让我们看看DSMARK的工作过程.
14.
3.
5.
SCH_DSMARK如何工作84这个队列规定将进行下列步骤:如果我们在队列规定的命令行中声明了set_tc_index选项,DS字段的值将被取出并存储在skb->tc_index中.
调用分类器.
分类器被调用并将返回将被存储于skb_tcindex的类编号.
如果没有找到能匹配的过滤器,会返回default_index的值.
如果既没有声明set_tc_index又没有声明default_index的值,结果可能是不可预知的.
在数据包被发送到你可以重用分类器的结果的内部队列之后,由内部队列规定返回的类代码被保存在skb->tc_index.
我们将来检索mask-value表的时候还会用到这个值.
随后的结果将决定数据包下一步的处理:New_Ds_field=(Old_DS_field&mask)|value然后,值将与ds_field的值和掩码进行"与"运算,然后再与参数值相"或".
下面的图表有利于你理解这个过程:skb->ihp->tos||^|--Ifyoudeclareset_tc_index,wesetDS可能改变|valueintoskb->tc_indexvariable||ODS字段|A||RInternal+-++---N|||||tcD|||indexQdiscv||||||filter|mask,value)|-->|O||sch_dsmarktc_index->||||(read)|maychangeIndextothemask,value)v|vv|pairstableskb->tc_index如何进行标记只需改变你想重新标记的类的掩码和值就行了.
看着一行代码:tcclasschangedeveth0classid1:1dsmarkmask0x3value0xb8它改变了散列表中的(mask,value)记录,来重新标记属于类1:1的数据包.
你必须使用"change"命令,因为(mask,value)记录已经有了缺省值.
(seetablebelow).
现在,我们解释一下TC_INDEX过滤器如何工作和适用场合.
此外,TC_INDEX过滤器也可以应用于没有DS服务的配置下.
14.
3.
6.
TC_INDEX过滤器这是声明TC_INDEX过滤器的基本命令:.
.
.
tcindex[hashSIZE][maskMASK][shiftSHIFT][pass_on|fall_through][classidCLASSID][policePOLICE_SPEC]85接下来,我们说明一下用来解释TC_INDEX操作模式的例子.
注意斜体字:tcqdiscadddeveth0handle1:0rootdsmarkindices64set_tc_indextcfilteradddeveth0parent1:0protocolipprio1tcindexmask0xfcshift2tcqdiscadddeveth0parent1:0handle2:0cbqbandwidth10Mbitcell8avpkt1000mpu64#EFtrafficclasstcclassadddeveth0parent2:0classid2:1cbqbandwidth10Mbitrate1500Kbitavpkt1000prio1boundedisolatedallot1514weight1maxburst10#PacketfifoqdiscforEFtraffictcqdiscadddeveth0parent2:1pfifolimit5tcfilteradddeveth0parent2:0protocolipprio1handle0x2etcindexclassid2:1pass_on(这里的代码尚未完成.
只是从iproute2发行中的EFCBQ范例中摘出来的.
).
首先,假设我们收到标记为"EF"的数据包.
如果你读了RFC2598,你就会明白DSCP推荐EF数据包的值是101110.
也就是说DS字段的值是10111000(记住TOS字节中次要的位没有在DS中使用)或者是16进制的0xb8.
TCINDEXFILTERFILTER|MASKHANDLE|0xfc0x2ESHIFT2CBQ2:0||||||||DSMARK1:0|数据包到来后就把DS字段设置为0xb8.
象前面所解释的,例子中被标记为1:0的dsmark队列规定找到DS字段的值并存储在skb->tc_index变量中.
下一步将对应关联到这个队列规定的过滤器(例子中的第二行).
这将进行下列操作:Value1=skb->tc_index&MASKKey=Value1>>SHIFT在例子中,MASK=0xFCiSHIFT=2.
Value1=10111000&11111100=10111000Key=10111000>>2=00101110->0x2Einhexadecimal返回值将对应一个队列规定的内部过滤器句柄(比如说:2:0).
如果这个编号的过滤器存在的话,将核对管制和测量条件(如果过滤器有这些的话),并返回类编号(我们的例子中是2:1),存储于skb->tc_index变量.
但是如果找到了那个编号的过滤器,结果将取决于是否声明了fall_through标志.
如果有,key值将作为类编号返回.
如果没有,将返回一个错误并继续处理其它过滤器.
要当心的是如果你使用了fall_through标志,并且在skb->tc_index和类编号之间存在着一个简单的关系.
Becarefulifyouusefall_throughflag;thiscanbedoneifasimplerelationexistsbetweenvaluesofskb->tc_indexvariableandclassid's.
最后要说的参数是hash和pass_on.
前者与散列表的大小有关.
后者用于指出如86果找不到这个过滤器的返回值指定的类编号,就进行下一个过滤器.
缺省行为是isfall_through(参见下表).
最后,让我们看一看TCINDEX的参数都有那些可能的值:TCNameValueDefaultHash1.
.
.
0x10000ImplementationdependentMask0.
.
.
0xffff0xffffShift0.
.
.
150Fallthrough/Pass_onFlagFall_throughClassidMajor:minorNonePolice.
.
.
.
.
None这种过滤器的功能非常强大,有必要挖掘它所有的可能性.
此外,这种过滤器不仅仅适用于Diffserv配置,你可以与其它任意类型的过滤器一起使用.
我建议你看看iproute2中自带的所有的Diffserv范例.
我保证我会尽力补全这个文档.
此外,我所解释的内容是很多测试的结果.
如果你能指出我的错误我将非常感激.
14.
4.
入口队列规定迄今为止所讨论的队列规定都是出口队列规定.
每个网卡其实还都可以拥有一个入口队列规定,它不是用来控制向网络上发数据包的,而是用来让你对从网络上收到的数据包应用tc过滤器的,而不管它们是发到本机还是需要转发.
由于tc过滤器包括了完整地实现了令牌桶过滤器,并且也能够匹配内核的流评估,所以能够实现非常多的功能.
这些能够有效地帮助你对输入流进行管制,甚至在它尚未进入IP协议层之前.
14.
4.
1.
参数与使用输入队列规定本身并不需要任何参数.
它不同于其它队列规定之处在于它不占用网卡的根.
象这样设置:#tcqdiscadddeveth0ingress这能够让你除了拥有输入队列规定之外,还有其它的发送队列规定.
关于输入队列的一些人为的例子,请参看"方便菜谱".
14.
5.
RED(RandomEarlyDetection,随机提前检测)这一节的意图是引入骨干路由,经常包括IPTABLES=/sbin/iptablesINDEV=eth2##tagallincomingSYNpacketsthrough$INDEVasmarkvalue1$iptables-APREROUTING-i$INDEV-tmangle-ptcp--syn\-jMARK--set-mark1##installtheingressqdiscontheingressinterface$TCqdiscadddev$INDEVhandleffff:ingress###SYNpacketsare40bytes(320bits)sothreeSYNsequals#960bits(approximately1kbit);soweratelimitbelow#theincomingSYNsto3/sec(notveryusefulreally;but#servestoshowthepoint-JHS$TCfilteradddev$INDEVparentffff:protocolipprio50handle1fw\policerate1kbitburst40mtu9kdropflowid:1#echo"----qdiscparametersIngress$TCqdisclsdev$INDEVecho"----ClassparametersIngress$TCclasslsdev$INDEVecho"----filterparametersIngress$TCfilterlsdev$INDEVparentffff:#deletingtheingressqdisc#$TCqdiscdel$INDEVingress15.
3.
为防止DDoS而对ICMP限速91最近一段,分布式拒绝服务攻击成了Internet上最让人讨厌的东西.
通过对你的网络正确地设置过滤和限速可以避免成为攻击的目标和跳板.
你应该过滤你的网络,禁止非本地IP源地址的数据包离开网络,这可以阻止其它人向Internet上发送垃圾包.
限速就象以前所展示的那样.
如果忘了,就是这张图:[Internet]------[Linux路由器]---[办公室+ISP]eth1eth0我们先进行预备设置:#tcqdiscadddeveth0roothandle10:cbqbandwidth10Mbitavpkt1000#tcclassadddeveth0parent10:0classid10:1cbqbandwidth10Mbitrate\10Mbitallot1514prio5maxburst20avpkt1000如果你有一个100Mbps的网卡,调整这些数据.
现在你需要决定允许多大的ICMP流量.
你可以用tcpdump测量段时间,把结果写入一个文件,看一看有多少ICMP数据包流经网络.
别忘了适当延长监测时间!
如果没有条件进行测量,不妨就选择带宽的5%.
设置我们的类:#tcclassadddeveth0parent10:1classid10:100cbqbandwidth10Mbitrate\100Kbitallot1514weight800Kbitprio5maxburst20avpkt250\bounded限速为100Kbps.
然后我们设置过滤器把ICMP数据包送给这个类:#tcfilteradddeveth0parent10:0protocolipprio100u32matchipprotocol10xFFflowid10:10015.
4.
为交互流量设置优先权当有很多上行和下行数据充斥了你的链路,而你正在使用telnet或者ssh维护一些系统的话,情况会很糟.
器它的数据包会挡住你的按键数据包.
有没有一种方法,能让我们的交互数据包在大批数据传输中取得优先呢Linux可以实现!
像以前一样,我们得处理量个方向的数据流.
明显地,这在链路两端都是Linux的时候非常有效——虽然其它的UNIX也能实现.
问问你周围的Solaris/BSD大拿们.
标准的pfifo_fast调度器有3个频道.
0频道中的数据优先传输,然后才是1频道、2频道.
关键就在于让我们的交互数据通过0频道!
我们改编自马上就要过期的ipchainsHOWTO:在IP头部有4个不常使用的bit,叫做TOS位.
它们影响数据包的待遇,4个bit分别代表"最小延迟"、"最大吞吐"、"最大可靠"和"最小成本".
同时只允许设置一位.
RobvanNieuwkerk——ipchainsTOS-mangling代码的作者——这样说:"最小延迟"对我来说特别重要.
我在一个33k6的MODEM链路后面.
Linux把数据包区分到3个队列中.
我在上行(Linux)路由器上为"交互"数据包打开它.
这样我就可以在大批下在数据的同时得到可接受的交互性能了.
92最通常的用发就是为设置telnet和ftp控制连接设置"最小延迟",并为ftp数据连接设置"最大吞吐".
在你的上行路由器上如此实现:#iptables-APREROUTING-tmangle-ptcp--sporttelnet\-jTOS--set-tosMinimize-Delay#iptables-APREROUTING-tmangle-ptcp--sportftp\-jTOS--set-tosMinimize-Delay#iptables-APREROUTING-tmangle-ptcp--sportftp-data\-jTOS--set-tosMaximize-Throughput现在,所有从你的telnet/ftp主机到达你本机方向的数据流就设置好了.
另外一个方向的设置似乎应该由你来设置,比如telnet、ssh等等都应自动在发出的数据包上做好TOS标记.
你随时都可以用netfilter来实现.
在你的本机上:#iptables-AOUTPUT-tmangle-ptcp--dporttelnet\-jTOS--set-tosMinimize-Delay#iptables-AOUTPUT-tmangle-ptcp--dportftp\-jTOS--set-tosMinimize-Delay#iptables-AOUTPUT-tmangle-ptcp--dportftp-data\-jTOS--set-tosMaximize-Throughput15.
5.
使用netfilter、iproute2和squid实现WEB透明代理本节内容由读者RamNarula(泰国)从Internet提交用于教学.
在Linux上实现这个功能的最正规方法可能是确认数据包的目标端口是80(web)后,使用ipchains/iptables把它们路由到运行着squid的服务器上.
有3种常规手段来实现,我们这里着重介绍第4种.
让网关路由器来路由你可以告诉路由器把目标端口是80的数据包发送到squid服务器上.
但是这会给你的路由器增加负载,况且很多商业路由器根本就不支持.
使用4层交换器4层交换器支持这个功能毫无问题.
但是这种设备的价格通常非常高.
典型4层交换器的价格要超过一个典型路由器外加一个好Linux服务器的价格.
让cache服务器做网关你可以强迫所有的数据包都流经cache服务器.
但是93这有相当风险,因为squid会占用很多CPU时间造成总体网络性能的下降,而且服务器本身的崩溃会造成所有人都无法上网.
Linux+NetFilter路由器使用NetFilter的另一种技术,我们可以让Netfilter给去往80口的数据包打上一个标记,然后用iproute2把打过标记的数据包路由给Squid服务器.
|实现|地址使用情况10.
0.
0.
1naret(NetFilter服务器)10.
0.
0.
2silom(Squid服务器)10.
0.
0.
3donmuang(连接Internet的路由器)10.
0.
0.
4kaosarn(网络上的其它服务器)10.
0.
0.
5RAS远程访问服务器10.
0.
0.
0/24主网络10.
0.
0.
0/19全体网络|网络结构|Internet|donmuang|hub/switch-||||naretsilomkaosarnRASetc.
首先,让naret成为除silom之外所有工作站的缺省网关,让所有的数据包都流经它.
Silom的缺省网关是donmuang(10.
0.
0.
3),否则就会造成web数据的环流.
(allserversonmynetworkhad10.
0.
0.
1asthedefaultgatewaywhichwastheformerIPaddressofdonmuangroutersowhatIdidwaschangedtheIPaddressofdonmuangto10.
0.
0.
3andgavenaretipaddressof10.
0.
0.
1)Silom------设置squid和ipchains在silom上设置好squid,保证它能支持透明缓冲/代理,缺省服务端口是3128,所以所有去网80口的数据流都应该重定向到它的3128口上.
这可以用ipchains命令完成:silom#ipchains-Nallow1silom#ipchains-Aallow1-pTCP-s10.
0.
0.
0/19-d0/080-jREDIRECT3128silom#ipchains-Iinput-jallow1或者,用netfilter来表达:silom#iptables-tnat-APREROUTING-ieth0-ptcp--dport80-jREDIRECT--to-port312894(注意:你可能还有其它条目)关于squid服务器的配置,请参考Squid常见问题网页:http://squid.
nlanr.
net).
请确认这台服务器的IP转发已经打开,且缺省网关指向了路由器donmuang(不是NOTnaret).
Naret------设置iptables和iproute2-如果需要,禁止icmpREDIRECT消息1.
"Mark"给去往80口的数据包打上标记"2"naret#iptables-APREROUTING-ieth0-tmangle-ptcp--dport80\-jMARK--set-mark22.
设置iproute2,把带有标记"2"的数据包路由到silomnaret#echo202www.
out>>/etc/iproute2/rt_tablesnaret#ipruleaddfwmark2tablewww.
outnaret#iprouteadddefaultvia10.
0.
0.
2deveth0tablewww.
outnaret#iprouteflushcache如果donmuang和naret在同一个子网中,naret不应发出icmp重定向消息.
所以应该禁止icmpREDIRECT:naret#echo0>/proc/sys/net/ipv4/conf/all/send_redirectsnaret#echo0>/proc/sys/net/ipv4/conf/default/send_redirectsnaret#echo0>/proc/sys/net/ipv4/conf/eth0/send_redirects设置完成,检查配置:在naret上:naret#iptables-tmangle-LChainPREROUTING(policyACCEPT)targetprotoptsourcedestinationMARKtcp--anywhereanywheretcpdpt:wwwMARKset0x2ChainOUTPUT(policyACCEPT)targetprotoptsourcedestinationnaret#iprulels0:fromalllookuplocal32765:fromallfwmark2lookupwww.
out32766:fromalllookupmain32767:fromalllookupdefaultnaret#iproutelisttablewww.
outdefaultvia203.
114.
224.
8deveth0naret#iproute10.
0.
0.
1deveth0scopelink10.
0.
0.
0/24deveth0protokernelscopelinksrc10.
0.
0.
1127.
0.
0.
0/8devloscopelinkdefaultvia10.
0.
0.
3deveth095(makesuresilombelongstooneoftheabovelines,inthiscaseit'sthelinewith10.
0.
0.
0/24)|------||-结束-||------|15.
5.
1.
实现之后的数据流图|Trafficflowdiagramafterimplementation|INTERNET/\||\/donmuangrouter-/\/\||||||||||\/||naretsilom||*destinationport80traffic=cache)||/\||||||\/\/kaosarn,RAS,etc.
请注意网络是非对称的,输出的路径多了一跳.
HereisrundownforpackettraversingthenetworkfromkaosarntoandfromtheInternet.
Forweb/httptraffic:kaosarnhttprequest->naret->silom->donmuang->internethttprepliesfromInternet->donmuang->silom->kaosarnFornon-web/httprequests(eg.
telnet):kaosarnoutgoingdata->naret->donmuang->internetincomingdatafromInternet->donmuang->kaosarn15.
6.
与PMTU发现有关的"基于路由的MTU设置"在传输大量数据的时候,使用大一些的数据包会让Internet表现好些.
每个数据包都意味着一次路由判断,如果要发送1M字节的文件,即使让包处于最大尺寸的话只需要大约700个包,而让包尽量小的话则需要4000个包.
然而,不是Internet的所有部分都能支持每个数据包1460字节负载.
因此,为了优化连接,就得检测一下,找到数据包最大的合适尺寸.
这个过程就叫"沿途MTU发现",MTU意思是"最大传输单位".
如果一个路由器发现一个数据包太大而无法在一个数据包中传输,而且数据包标明了"不可分片",就会返回一个ICMP消息,报告说不得不丢弃这个包.
发包的主机根据这个提示就会减小发包尺寸,反复几次之后,就能够找到一个连接沿96特定路径传输时最优的包尺寸.
但是好景不长,因为Internet被一帮竭尽全力破坏通信的臭流氓们发现了.
然后导致网管们被误导去通过阻挡或限制ICMP流量的方法来增加他们Internet服务的安全性和坚固性.
现在的情况就是"沿途MTU发现"工作得越来越不好了甚至在某些路由器上失败,以至于TCP/IP会话在一段时间之后奇怪地中止.
虽然我不能证明,但是我曾经有两个运行AlteonAcedirectors的曾经站点有这个问题,也许有人能提供更多线索说明为什么发生.
15.
6.
1.
解决方案当你碰到被这个问题困扰的网站,可以手工地禁止沿途MTU发现功能.
KoosvandenHout写到(经简单编辑):下列问题:因为我的专线只有33k6,我设置了ppp的MTU值是296,但是我无法影响对端的队列.
设置为296后,击健的响应时间比较合理.
在我这一端,是一个运行了Linux的伪装路由器.
最近,我把服务器和路由器分开了,所以绝大多数应用程序挪到了路由器的外面.
然后麻烦了,我无法登录到irc里.
可怕!
经过研究发现我已经连接到了irc上,但是没有从irc那里收到motd.
我检查了可能出错的地方,注意到以前我的MTU是1500时,连接某些web服务器没有问题,但设置为296后不行了,应该是与MTU相关的问题.
因为irc服务器阻挡几乎每种它不直接需要的流量,也包括icmp.
我试图向一个web服务器的操作员证明这就是引起问题的原因,但他们不愿意解决问题.
所以我不得不保证输出伪装过的数据包的时候使用比外部链路更低的MTU.
但是我又希望本地以太网使用普通的MTU(为了nfs之类服务).
解决方案:iprouteadddefaultvia10.
0.
0.
1mtu296(10.
0.
0.
1是缺省网关,伪装路由器的内网地址)一般而言,可以通过设置特定的路由器来抑制PMTU发现.
比如,如果只有某些子网由这个问题,那么:iprouteadd195.
96.
96.
0/24via10.
0.
0.
1mtu10009715.
7.
与PMTU发现有关的MSS箝位(给ADSL,cable,PPPoE和PPtP用户)象上面解释的那样,PMTU发现不再像以前那样有效了.
所以如果你知道网络中某一跳的MTU比较小(iptables-1.
2.
1a和Linux2.
4.
3才行.
基本命令行是:#iptables-AFORWARD-ptcp--tcp-flagsSYN,RSTSYN-jTCPMSS--clamp-mss-to-pmtu它为你的链路计算出合适的MSS值.
如果你足够勇敢,或者是高手,你也可以这样做:#iptables-AFORWARD-ptcp--tcp-flagsSYN,RSTSYN-jTCPMSS--set-mss128它设置了发送SYN的数据包的MSS为128.
如果你有小数据包的VoIP应用,而巨大的http数据包导致了语音的不连续,就可以这样设置.
15.
8.
终极的流量控制:低延迟、高速上/下载Note:ThisscripthasrecentlybeenupgradedandpreviouslyonlyworkedforLinuxclientsinyournetwork!
SoyoumightwanttoupdateifyouhaveWindowsmachinesorMacsinyournetworkandnoticedthattheywerenotabletodownloadfasterwhileotherswereuploading.
我试图打造圣杯:让交互数据包保持较低的延迟时间也就是说上载或下载文件不会打扰SSH/telnet等.
这是最重要的,即使是200ms的延迟也会感觉很差.
上载或下载期间有合理的速率用于网页浏览即使http属于一种大量数据传输,也不应受其它传输影响太大.
保证上载不会影响下载98上载数据流会影响下载的速率,这是相当普遍的现象.
只要花费一点点带宽就可以实现它们.
之所以上载流、下载流和ssh之间要互相伤害,是因为像Cable或者DSLModem这样的家用设备中的队列太大.
下面一节进一步解释了造成延迟的原因和如何缓解.
如果你对魔术的咒语不感兴趣,完全可以不看它,直接参考脚本那一节.
15.
8.
1.
为什么缺省设置不让人满意ISP们知道人们评价他们的时候要看看下载的速度.
除了可用带宽之外,丢包因为会严重地牵制TCP/IP效率而极大地影响下载速度.
大队列有助于改善丢包,进而提高下载速度.
所以ISP们都配置成大队列.
然而,大队列会破坏交互性.
一次击键行为首先要在上行流队列中排队(可能长达数秒!
)才能到达对端主机.
回显的时候,数据包还要回来,所以还要在你ISP的下行流队列中排队,你的本端显示器才有显示.
这个HOWTO教你如何用多种方法重组和处理队列,但是,并不是所有的队列配置我们都有权访问.
你的ISP的队列肯定是无法配置的.
上行流可能存在于你的cablemodem或DSL设备中,你也许可以配置,也许不可以配置.
绝大多数不能配置.
那怎么办呢既然无法控制那些队列,那就除去它们,让数据包在我们的Linux路由器内排队.
幸运的是,这是可能的.
限制上载速率把上载速率限制在比可用带宽稍小一些的位置上,于是你的MODEM中就不会形成队列了.
也就是说,队列转移到你的Linux路由器中了.
限制下载速率这带点欺骗的味道,因为我们实际上不能控制Internet向我们发包的速率.
但我们可以丢掉那些太快到来的数据包,不让他们导致TCP/IP的速率低于我们期望的速率.
因为我们不希望轻易地丢弃数据包,所以我们要配置"burst"来容纳突发传输.
在做完了这些之后,我们就排除了下行队列(除了偶尔的突发),并让上行队列存在于我们的Linux路由器中,以便施展Linux非凡的能力.
剩下的事情就是保证交互数据包永远排在上行队列的最前面.
为了保证上行数据流不会伤害下行流,我们还要把ACK数据包排在队列前面.
这就是当发生大批量数据流的时候,双向传输均受到严重影响的原因.
因为下行数据的ACK必须同上行流进行竞争,并在处理过程中被延迟.
为了进一步配置,我们使用一个高质量的ADSL连接(荷兰的xs4all)得出了如下测量结果:基准延迟:round-tripmin/avg/max=14.
4/17.
1/21.
7ms99不进行流量控制,下载数据:round-tripmin/avg/max=560.
9/573.
6/586.
4ms不进行流量控制,上载数据:round-tripmin/avg/max=2041.
4/2332.
1/2427.
6ms进行流量控制,220kbit/s上行:round-tripmin/avg/max=15.
7/51.
8/79.
9ms进行流量控制,850kbit/s下行:round-tripmin/avg/max=20.
4/46.
9/74.
0ms上载数据时,下载得到约80%的带宽.
当上行流达到90%时,延迟一下子增加到850ms,也能说明问题.
这个脚本的期望效果极大地取决于你的实际上行速率.
当你满速率上载的时候,你击键的那个数据包前面总会有一个数据包.
这就是你能够达到的上行延迟的下限——MTU除以上行速率.
典型值会比它稍高一些.
要达到更好效果,可以降低MTU!
然后是两个版本的脚本一个是使用Devik的HTB,另一个是使用Linux内部自带的CBQ.
都已经过测试,正常工作.
15.
8.
2.
实际的脚本(CBQ)适用于所有内核.
我们在CBQ的内部放置2个随机公平队列,以保证多个大吞吐量数据流之间不会互相淹没.
下行流使用带有令牌桶过滤器的tc过滤器进行管制.
你可以往脚本中"tcclassadd.
.
classid1:20"那一行添加"bounded"进行改进.
如果你降低了MTU,同时也得降低allot和avpkt!
#!
/bin/bash#TheUltimateSetupForYourInternetConnectionAtHome###Setthefollowingvaluestosomewhatlessthanyouractualdownload#anduplinkspeed.
InkilobitsDOWNLINK=800UPLINK=220DEV=ppp0#cleanexistingdown-anduplinkqdiscs,hideerrorstcqdiscdeldev$DEVroot2>/dev/null>/dev/nulltcqdiscdeldev$DEVingress2>/dev/null>/dev/null######uplink#installrootCBQtcqdiscadddev$DEVroothandle1:cbqavpkt1000bandwidth10mbit#shapeeverythingat$UPLINKspeed-thispreventshugequeuesinyour100#DSLmodemwhichdestroylatency:#mainclasstcclassadddev$DEVparent1:classid1:1cbqrate${UPLINK}kbit\allot1500prio5boundedisolated#highprioclass1:10:tcclassadddev$DEVparent1:1classid1:10cbqrate${UPLINK}kbit\allot1600prio1avpkt1000#bulkanddefaultclass1:20-getsslightlylesstraffic,#andalowerpriority:tcclassadddev$DEVparent1:1classid1:20cbqrate$[9*$UPLINK/10]kbit\allot1600prio2avpkt1000#bothgetStochasticFairness:tcqdiscadddev$DEVparent1:10handle10:sfqperturb10tcqdiscadddev$DEVparent1:20handle20:sfqperturb10#startfilters#TOSMinimumDelay(ssh,NOTscp)in1:10:tcfilteradddev$DEVparent1:0protocolipprio10u32\matchiptos0x100xffflowid1:10#ICMP(ipprotocol1)intheinteractiveclass1:10sowe#candomeasurements&impressourfriends:tcfilteradddev$DEVparent1:0protocolipprio11u32\matchipprotocol10xffflowid1:10#Tospeedupdownloadswhileanuploadisgoingon,putACKpacketsin#theinteractiveclass:tcfilteradddev$DEVparent1:protocolipprio12u32\matchipprotocol60xff\matchu80x050x0fat0\matchu160x00000xffc0at2\matchu80x100xffat33\flowid1:10#restis'non-interactive'ie'bulk'andendsupin1:20tcfilteradddev$DEVparent1:protocolipprio13u32\matchipdst0.
0.
0.
0/0flowid1:20downlink#slowdownloadsdowntosomewhatlessthantherealspeedtoprevent#queuingatourISP.
Tunetoseehowhighyoucansetit.
#ISPstendtohave*huge*queuestomakesurebigdownloadsarefast##attachingresspolicer:tcqdiscadddev$DEVhandleffff:ingress#filter*everything*toit(0.
0.
0.
0/0),dropeverythingthat's#comingintoofast:tcfilteradddev$DEVparentffff:protocolipprio50u32matchipsrc\0.
0.
0.
0/0policerate${DOWNLINK}kbitburst10kdropflowid:1如果你希望这个脚本在ppp连接时执行,就把它拷贝成/etc/ppp/ip-up.
d.
101如果在最后两行报错,请更新你的tc工具!
15.
8.
3.
实际的脚本(HTB)下面的脚本通过HTB完成那些功能,参见相关章节.
你真的值得为它打个补丁!
#!
/bin/bash#TheUltimateSetupForYourInternetConnectionAtHome###Setthefollowingvaluestosomewhatlessthanyouractualdownload#anduplinkspeed.
InkilobitsDOWNLINK=800UPLINK=220DEV=ppp0#cleanexistingdown-anduplinkqdiscs,hideerrorstcqdiscdeldev$DEVroot2>/dev/null>/dev/nulltcqdiscdeldev$DEVingress2>/dev/null>/dev/null######uplink#installrootHTB,pointdefaulttrafficto1:20:tcqdiscadddev$DEVroothandle1:htbdefault20#shapeeverythingat$UPLINKspeed-thispreventshugequeuesinyour#DSLmodemwhichdestroylatency:tcclassadddev$DEVparent1:classid1:1htbrate${UPLINK}kbitburst6k#highprioclass1:10:tcclassadddev$DEVparent1:1classid1:10htbrate${UPLINK}kbit\burst6kprio1#bulk&defaultclass1:20-getsslightlylesstraffic,#andalowerpriority:tcclassadddev$DEVparent1:1classid1:20htbrate$[9*$UPLINK/10]kbit\burst6kprio2#bothgetStochasticFairness:tcqdiscadddev$DEVparent1:10handle10:sfqperturb10tcqdiscadddev$DEVparent1:20handle20:sfqperturb10#TOSMinimumDelay(ssh,NOTscp)in1:10:tcfilteradddev$DEVparent1:0protocolipprio10u32\matchiptos0x100xffflowid1:10#ICMP(ipprotocol1)intheinteractiveclass1:10sowe#candomeasurements&impressourfriends:tcfilteradddev$DEVparent1:0protocolipprio10u32\matchipprotocol10xffflowid1:10#Tospeedupdownloadswhileanuploadisgoingon,putACKpacketsin#theinteractiveclass:102tcfilteradddev$DEVparent1:protocolipprio10u32\matchipprotocol60xff\matchu80x050x0fat0\matchu160x00000xffc0at2\matchu80x100xffat33\flowid1:10#restis'non-interactive'ie'bulk'andendsupin1:20downlink#slowdownloadsdowntosomewhatlessthantherealspeedtoprevent#queuingatourISP.
Tunetoseehowhighyoucansetit.
#ISPstendtohave*huge*queuestomakesurebigdownloadsarefast##attachingresspolicer:tcqdiscadddev$DEVhandleffff:ingress#filter*everything*toit(0.
0.
0.
0/0),dropeverythingthat's#comingintoofast:tcfilteradddev$DEVparentffff:protocolipprio50u32matchipsrc\0.
0.
0.
0/0policerate${DOWNLINK}kbitburst10kdropflowid:1如果你希望这个脚本在ppp连接时执行,就把它拷贝成/etc/ppp/ip-up.
d.
如果在最后两行报错,请更新你的tc工具!
15.
9.
为单个主机或子网限速虽然在我们的man和其它地方对于这个问题描述的相当详细,但是这个经常被问到的问题存在简单的回答,不需要完全地理解流量控制.
这招就三行:tcqdiscadddev$DEVroothandle1:cbqavpkt1000bandwidth10mbittcclassadddev$DEVparent1:classid1:1cbqrate512kbit\allot1500prio5boundedisolatedtcfilteradddev$DEVparent1:protocolipprio16u32\matchipdst195.
96.
96.
97flowid1:1第一行在你的网卡中添加一个基于类的队列,并告诉内核如何计算,假定是一个10M的网卡.
如果你做错了,不会有什么实际的损害.
但是正确地设置会使结果更加精确.
第二行用一些合理的参数创建了一个512kbit的类.
关于细节,参见cbq的手册和第9章.
最后一行表明那些数据包应该交给整形的类.
未匹配的数据包就不整形.
如果想103进行更精确的匹配(子网、端口等等),参见9.
6.
2.
如果你修改了什么,就得重新装入脚本,执行"tcqdiscdeldev$DEVroot"可以清除当前配置.
脚本还可以进一步地改进,最后加上一行"tcqdiscadddev$DEVparent1:1sfqperturb10".
它的作用和细节参见9.
2.
3.
15.
10.
一个完全NAT和QoS的范例我是PedroLarroy我这里描述的环境是这样的:有很多用户,通过一个Linux路由器连接到Internet上,路由器上有一个公网IP地址,通过NAT让大家上网.
我作为网管,使用这个QoS设置来让我们宿舍的198个用户访问Internet.
这里的用户们非常多地使用对等应用,所以合理的带宽管理是必须的.
我希望这个例子能够帮助那些对lartc感兴趣的读者们.
我一开始一步步地作了实际的实现,最后我会解释如何让这个过程在启动期间自动进行.
这个网络的私有LAN通过Linux路由器连接到Internet,路由器上有一个公网IP.
把它扩展成拥有多个公网IP非常简单,改动一些iptables规则即可.
为了顺利配置,我们需要:Linux2.
4.
18或更高版本的内核如果你使用2.
4.
18,需要打上HTB补丁.
iproute也是要确认"tc"是否支持HTB.
iptables15.
10.
1.
开始优化那不多的带宽首先,我们设置一些用来分类的队列规定.
我们创建一个由6个类组成的HTB队列规定,各类按升序排列.
这样,我们就有了一些总能够分配到制定带宽的类,但是他们还可以利用其它类没有用到的一部分带宽.
更高优先权(更小的优先权值)的类优先获得剩余的带宽.
我们的连接是一个2Mbps下行/300kbps上行的ADSL线路.
我使用240kbps作为峰值速率,因为这个值比延迟开始增长时的带宽值稍高一点,延迟的原因是在本端与对端主机之间任意地点的缓冲区造成的.
BuyVM商家属于比较老牌的服务商,早年有提供低价年付便宜VPS主机还记得曾经半夜的时候抢购的。但是由于这个商家风控非常严格,即便是有些是正常的操作也会导致被封账户,所以后来陆续无人去理睬,估计被我们风控的抢购低价VPS主机已经手足无措。这两年商家重新调整,而且风控也比较规范,比如才入手他们新上线的流媒体优化VPS主机也没有不适的提示。目前,BuyVM商家有提供新泽西、迈阿密等四个机房的VPS主机...
目前,我们都在用哪个FTP软件?喜欢用的是WinSCP,是一款免费的FTP/SFTP软件。今天在帮助一个网友远程解决问题的时候看到他用的是FlashFXP FTP工具,这个工具以前我也用过,不过正版是需要付费的,但是网上有很多的绿色版本和破解版本。考虑到安全的问题,个人不建议选择破解版。但是这款软件还是比较好用的。今天主要是遇到他的虚拟主机无法通过FTP连接主机,这里我就帮忙看看到底是什么问题。一...
趣米云早期为做技术起家,为3家IDC提供技术服务2年多,目前商家在售的服务有香港vps、香港独立服务器、香港站群服务器等,线路方面都是目前最优质的CN2,直连大陆,延时非常低,适合做站,目前商家正在做七月优惠活动,VPS低至18元,价格算是比较便宜的了。趣米云vps优惠套餐:KVM虚拟架构,香港沙田机房,线路采用三网(电信,联通,移动)回程电信cn2、cn2 gia优质网络,延迟低,速度快。自行封...