甲虫天下

天下行,行天下。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

IPSEC:Internet上安全的IP

Posted on 2007-05-18 10:48  hexapod  阅读(721)  评论(0编辑  收藏  举报
         现在,在Linux上有两种IPSEC可用.对于2.2和2.4,第一个比较正规的实现有FreeS/WAN.他们有一个官方站点和一个经常维护的非官方站点.出于多种原因,FreeS/WAN历来没有被合并到内核的主线上.最常提到的原因就是政治问题——违反了美国的密码产品扩散条例.所以它不会被集成到Linux内核中. 
         另外,很多合作伙伴表明了他们对代码质量的忧虑.关于如何设置FreeS/WAN,有很多文档可以参考. 
         Linux 2.5.47版内核里有一个内置的IPSEC实现,是由Alexey Kuznetsov和Dave Miller在USAGI IPv6 小组的启发下写成的.经过这次合并,James Morris的CrypoAPI也成了内核的一部分——它能够真正地加密. 
         本HOWTO文档仅收录2.5以上版本内核的IPSEC.Linux 2.4内核的用户推荐使用FreeS/WAN.但是要注意,它的配置方法与内置IPSEC不同. 
         2.5.49版内核的IPSEC不再需要任何补丁. 
         我在这里收集了Alexey或Dave Miller发布的一些补丁.对于2.5.48版的内核,在报告BUG之前请确认已经打上了那些补丁!(迄今还没有2.5.49这方面的补丁).一些简单的用户级工具可以在这里 (编译好的可执行文件和手册)找到.编译这些用户级工具需要修改Makefiles指向你的2.5.x内核.这种情况可望很快解决. 
         编译你的内核的时候,要确认已经打开CryptoAPI中的"PF_KEY","AH","ESP"以及其他所有选项!netfilter中的TCP_MSS方法现在不能用,请关掉. 
         本章的作者对于IPSEC完全是外行(nitwit)!如果你发现了错误,请email
         通知bert hubert . 
         首先,我们展示一下如何在两个主机之间手动设置安全通讯.其间的大部分工作都可以自动完成,但是为了了解细节我们仍然用手动完成. 如果你仅仅对自动密钥管理感兴趣,请跳过下面一节.但是要知道,了解手动密要管理是很有用的. 
         7.1. 从手动密钥管理开始 
         22 
         IPSEC是一个复杂的主题.有很多在线信息可以查阅,这个HOWTO将集中讲解如何设置,运行并解释一些基本原理. 很多iptables配置会丢弃IPSEC数据包!要想让IPSEC通过,运行 
         iptables -A xxx -p 50 -j ACCEPT 
         iptables -A xxx -p 51 -j ACCEPT 
         IPSEC提供了一个安全的IP协议版本.所谓的"安全"意味着两件事情:加密与验证.如果认为安全仅仅就是加密哪就太天真了,很容易看出来那是不够的——你的通讯过程或许是加密的,但是你如何保证与你通讯的对端就是你期望中的人呢? 
         IPSEC使用ESP('Encapsulated Security Payload',安全载荷封装) 来支持加密,使用AH(Authentication Header,头部验证)来支持对端验证.你可以同时使用二者,也可以使用二者之一. 
         ESP和AH都依靠SA(security associations,安全联盟)来工作.一个SA由一个源,
         一个目的和一个说明组成.一个验证SA看上去应该是: 
         add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456";
意思是:"从10.0.0.11到10.0.0.216的数据包需要AH,使用HMAC-MD5签名,
密码是1234567890123456".这个说明的SPI(Security Parameter Index,安全参数
索引)号码是'15700',细节后面再谈.有意思的是一个会话的两端都使用相同的
SA——它们是全等的,而不是镜像对称的.要注意的是,SA并没有自动翻转规
则——这个SA仅仅描述了从10.0.0.11到10.0.0.216的认证.如果需要双向安全,
需要2条SA.
一个简单地ESP SA:
add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012";
意思是:"从10.0.0.11到10.0.0.216的数据包需要ESP,使用3des-cbc加密算法,
密码是123456789012123456789012".SPI号码是'15701'.
到此,我们看到SA描述了所有的说明,但它并没有描述应该什么时候用到这些
策略.实际上,可以有很多完全相同的SA,除了它们之间的SPI不同.顺便提
一句,SPI的意思是Security Parameter Index(安全参数索引).为了进行加密操作,
我们需要声明一个策略.这个策略可以包含诸如"如果可能的话使用ipsec"或
者"除非我们有ipsec否则丢弃数据包"这样的东西.
最简单的典型SP(Security Policy,安全策略)应该是这样:
spdadd 10.0.0.216 10.0.0.11 any -P out ipsec
esp/transport//require
ah/transport//require;
如果在10.0.0.216上输入这个,就意味着凡是去往10.0.0.11的数据包必须经过加
密并附带AH头验证.要注意,它并没有指出使用哪一个SA,那是留给内核来
23
10.0.0.216: icmp: echo reply
注意,可看出从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 -f
spdadd 10.0.0.216 10.0.0.11 any -P IN ipsec
esp/transport//require
ah/transport//require;
这就指明了10.0.0.11收到来自10.0.0.216的包的时候需要正确的ESP和AH处理.
现在,我们完成这个配置,我们当然也希望回去的数据包也进行加密和头验证.
10.0.0.216上完整的配置应该是:
24
#!/sbin/setkey -f
flush;
spdflush;
# AH
add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456";
add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456";
# ESP
add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012";
add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012";
spdadd 10.0.0.216 10.0.0.11 any -P out ipsec
esp/transport//require
ah/transport//require;
spdadd 10.0.0.11 10.0.0.216 any -P in ipsec
esp/transport//require
ah/transport//require;
10.0.0.11上:
#!/sbin/setkey -f
flush;
spdflush;
# AH
add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456";
add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456";
# ESP
add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012";
add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012";
spdadd 10.0.0.11 10.0.0.216 any -P out ipsec
esp/transport//require
ah/transport//require;
spdadd 10.0.0.216 10.0.0.11 any -P in ipsec
esp/transport//require
ah/transport//require;
注意,本例中通信双方的加密密钥是一样的.这在实际中是不会出现的.
为了检测一下我们刚才的配置,运行一下:
setkey -D
就会显示出SA,或者用
setkey -DP
显示出SP.
7.2. 自动密钥管理
25
在上一节中,使用了简单共享的密码进行加密.换句话说,为了保密,我们必须
通过一个安全的通道把加密配置传给对方.如果我们使用telnet配置对端的机器,
任何一个第三方都可能知道我们的共享密码,那么设置就是不安全的.
而且,因为密码是共享的,所以它就不成为真正意义的密码.就算对端不能用它
做什么,但我们需要为每一个要进行IPSEC通讯的对方都设置互补相同的密码.
这需要生成大量的密钥,如果有10个成员需要通讯,就至少需要50个不同的密
码.
除了对称密钥的问题之外,还有解决密钥轮转的问题.如果第三方搜集到足够的
数据包,就有可能反向计算出密钥.这可以通过每隔一段时间换用一个新密钥来
解决,但是这必须自动完成.
另一个问题是,如上所述的手动密钥管理,我们必须精确地指定算法和密钥长度,
与对端的协调也是个大问题.我们渴望能够用一种比较宽松的方式来描述密钥策
略,比如说:"我们可以用3DES或者Blowfish算法,密钥长度至少是多少多少
位".
为了满足这些要求,IPSEC提供了IKE(Internet Key Exchange,Internet密钥交换)
来自动随机生成密钥,并使用协商好的非对称加密算法进行密钥交换.
Linux 2.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.
我们尽量保持缺省配置,两台机器是一样的:
path pre_shared_key "/usr/local/etc/racoon/psk.txt";
remote anonymous
{
exchange_mode aggressive,main;
doi ipsec_doi;
situation identity_only;
my_identifier address;
lifetime time 2 min; # sec,min,hour
initial_contact on;
proposal_check obey; #
obey, strict or claim
proposal {
encryption_algorithm 3des;
hash_algorithm sha1;
authentication_method pre_shared_key;
dh_group 2 ;
}
}
sainfo anonymous
{
pfs_group 1;
lifetime time 2 min;
encryption_algorithm 3des ;
authentication_algorithm hmac_sha1;
compression_algorithm deflate ;
}
有很多设置,我认为仍然有很多可以去掉而更接近缺省配置.很少有值得注意的
事情.我们已经配置了两个匿名配置支持所有的对端机器,让将来的配置简单些.
这里没有必要为每台机器各写一个段落,除非真的必要.
此外,我们还设置了我们基于我们的IP地址来识别我们自己('my_identifier
address'),并声明我们可以进行3DES,sha1,并且我们将使用预先共享的密钥,
写在psk.txt中.
在psk.txt中,我们设置两个条目,两台机器上都不一样.
在10.0.0.11上:
10.0.0.216
password2
在10.0.0.216上:
10.0.0.11
password2
确认这些文件必须为root所有,属性是0600,否则racoon将不信任其内容.两
个机器上的这个文件是镜像对称的.
现在,我们就剩下设置SP了,这比较简单.
在10.0.0.216上:
27
#!/sbin/setkey -f
flush;
spdflush;
spdadd 10.0.0.216 10.0.0.11 any -P out ipsec
esp/transport//require;
spdadd 10.0.0.11 10.0.0.216 any -P in ipsec
esp/transport//require;
在10.0.0.11上:
#!/sbin/setkey -f
flush;
spdflush;
spdadd 10.0.0.11 10.0.0.216 any -P out ipsec
esp/transport//require;
spdadd 10.0.0.216 10.0.0.11 any -P in ipsec
esp/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-SA
request for 10.0.0.11 queued due to no phase1 found.
12:18:44: INFO: isakmp.c:794:isakmp_ph1begin_i(): initiate new
phase 1 negotiation: 10.0.0.216[500]10.0.0.11[500]
12:18:44: INFO: isakmp.c:799:isakmp_ph1begin_i(): begin Aggressive mode.
12:18:44: INFO: vendorid.c:128:check_vendorid(): received Vendor ID:
KAME/racoon
12:18:44: NOTIFY: oakley.c:2037akley_skeyid(): couldn't find
the proper pskey, try to get one by the peer's address.
12:18:44: INFO: isakmp.c:2417:log_ph1established(): ISAKMP-SA
established 10.0.0.216[500]-10.0.0.11[500] spi:044d25dede78a4d1:ff01e5b4804f0680
12:18:45: INFO: isakmp.c:938:isakmp_ph2begin_i(): initiate new phase 2
negotiation: 10.0.0.216[0]10.0.0.11 spi=15863890(0xf21052)
如果我们现在运行setkey -D列出SA,就会发现已经存在了:
10.0.0.216 10.0.0.11
esp mode=transport spi=224162611(0x0d5c7333) reqid=0(0x00000000)
E: 3des-cbc 5d421c1b d33b2a9f 4e9055e3 857db9fc 211d9c95 ebaead04
A: hmac-sha1 c5537d66 f3c5d869 bd736ae2 08d22133 27f7aa99
seq=0x00000000 replay=4 flags=0x00000000 state=mature
created: Nov 11 12:28:45 2002
current: Nov 11 12:29:16 2002
diff: 31(s)
hard: 600(s)
soft: 480(s)
last: Nov 11 12:29:12 2002
hard: 0(s)
soft: 0(s)
current: 304(bytes)
hard: 0(bytes)
soft: 0(bytes)
28
allocated: 3
hard: 0
soft: 0
sadb_seq=1 pid=17112 refcnt=0
10.0.0.11 10.0.0.216
esp mode=transport spi=165123736(0x09d7969 reqid=0(0x00000000)
E: 3des-cbc d7af8466 acd4f14c 872c5443 ec45a719 d4b3fde1 8d239d6a
A: hmac-sha1 41ccc388 4568ac49 19e4e024 628e240c 141ffe2f
seq=0x00000000 replay=4 flags=0x00000000 state=mature
created: Nov 11 12:28:45 2002
current: Nov 11 12:29:16 2002
diff: 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: 2
hard: 0
soft: 0
sadb_seq=0 pid=17112 refcnt=0
我们的SP是如下配置的:
10.0.0.11[any] 10.0.0.216[any] tcp
in ipsec
esp/transport//require
created:Nov 11 12:28:28 2002 lastused:Nov 11 12:29:12 2002
lifetime:0(s) validtime:0(s)
spid=3616 seq=5 pid=17134
refcnt=3
10.0.0.216[any] 10.0.0.11[any] tcp
out ipsec
esp/transport//require
created:Nov 11 12:28:28 2002 lastused:Nov 11 12:28:44 2002
lifetime:0(s) validtime:0(s)
spid=3609 seq=4 pid=17134
refcnt=3
7.2.2.1. 问题和常见的疏忽
如果不工作,检查一下所有的配置文件是不是为root所有,而且只有root才能
读取.如想前台启动racoon,就加上"-F"参数.如想强制它读取某一个配置文
件来取代缺省配置文件,使用参数"-f".如想看到超级详细的细节,往racoon.conf
中加入"log debug;"一行.
7.2.3. 使用X.509证书进行自动密钥管理
如前所述,之所以共享密码很困难,是因为它们一旦共享,就不再成为真正意义
的密码.幸运的是,我们仍可以用非对称加密技术来解决这个问题.
如果IPSEC的每个参与者都生成一对公钥和私钥,就可以让双方公开它们的公
29
钥并设置策略,从而建立安全连接.
虽然需要一些计算,但生成密钥还是相对比较简单的.以下都是基于openssl工
具实现的.
7.2.3.1. 为你的主机生成一个X.509证书
OpenSSL搭好了很多基础结构,以便我们能够使用经过或者没有经过CA签署的
密钥.现在,我们就围绕这些基础结构,并练习一下使用著名的Snake Oil安全,
而不是使用CA.
首先,我们为主机laptop发起一个"证书请求":
$ openssl req -new -nodes -newkey rsa:1024 -sha1 -keyform PEM -keyout
laptop.private -outform PEM -out request.pem
这是可能问我们的问题:
Country Name (2 letter code) [AU]:NL
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []elft
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Linux Advanced
Routing & Traffic Control
Organizational Unit Name (eg, section) []:laptop
Common Name (eg, YOUR name) []:bert hubert
Email Address []:ahu@ds9a.nl
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
请你根据自己的实际情况完整填写.你可以把你的主机名写进去,也可以不写,
取决于你的安全需求.这个例子中,我们写了.
我们现在自己签署这个请求:
$ openssl x509 -req -in request.pem -signkey laptop.private -out
laptop.public
Signature ok
subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic
Control/OU=laptop/CN=bert hubert/Email=ahu@ds9a.nl
Getting Private key
现在,"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中,我们添加:
30
path certificate "/usr/local/etc/racoon/certs";
remote 10.0.0.216
{
exchange_mode aggressive,main;
my_identifier asn1dn;
peers_identifier asn1dn;
certificate_type x509 "upstairs.public" "upstairs.private";
peers_certfile "laptop.public";
proposal {
encryption_algorithm 3des;
hash_algorithm sha1;
authentication_method rsasig;
dh_group 2 ;
}
}
它们告诉racoon:证书可以在/usr/local/etc/racoon/certs/那里找到.而且还包含了
专门为10.0.0.216而写的配置项.
包含"asn1dn"的行告诉racoon,本端和对端的标识都从公钥中提取.也就是上
面输出的"subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic
Control/OU=laptop/CN=bert hubert/Email=ahu@ds9a.nl".
"certificate_type"那一行配置了本地的公钥和私钥."peers_certfile"这行告诉
racoon读取名叫"laptop.public"的文件取得对端的公钥.
"proposal"这一段与你以前看到的基本一致,除了"authentication_method"
的值变成了"rsasig",意思是使用RSA 公钥/私钥对.
在10.0.0.216上面的配置文件与上面的是完全镜像关系,没有其它改变:
path certificate "/usr/local/etc/racoon/certs";
remote 10.0.0.11
{
exchange_mode aggressive,main;
my_identifier asn1dn;
peers_identifier asn1dn;
certificate_type x509 "laptop.public" "laptop.private";
peers_certfile "upstairs.public";
proposal {
encryption_algorithm 3des;
hash_algorithm sha1;
authentication_method rsasig;
dh_group 2 ;
}
}
现在,我们已经把两台机器的配置文件改好了,然后就应该把证书文件拷贝到正
确的位置."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"命令:
$ openssl dgst upstairs.public
MD5(upstairs.public)= 78a3bddafb4d681c1ca8ed4d23da4ff1
现在我们要做的就是检验一下对方是否能够得到相同的MD5散列值.这可以通
过真实地接触来完成,也可以通过电话,但是一定不要与公钥放在同一封电子邮
件里发送!
另一个办法是通过一个可信的第三方(CA)来实现.这个CA会为你的密钥进
行签名,而不是象上面那样由我们自己签名.
7.3. IPSEC隧道
迄今为止,我们只是认识了IPSEC的"transport"透明)模式,也就是通讯的两
端都能够直接理解IPSEC.这不能代表所有的情况,有时候我们只需要路由器理
解IPSEC,路由器后面的机器利用它们进行通讯.这就是所谓的"tunnel mode"
(隧道模式).
设置这个极其简单.如果想通过10.0.0.216与10.0.0.11建立的隧道来传输从
10.0.0.0/24到130.161.0.0/16的数据包,按下面配置就可以:
#!/sbin/setkey -f
flush;
spdflush;
add 10.0.0.216 10.0.0.11 esp 34501
-m tunnel
-E 3des-cbc "123456789012123456789012";
spdadd 10.0.0.0/24 130.161.0.0/16 any -P out ipsec
esp/tunnel/10.0.0.216-10.0.0.11/require;
注意."-m tunnel"是关键!这里首先配置了一个隧道两端(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 -f
flush;
32
spdflush;
add 10.0.0.216 10.0.0.11 esp 34501
-m tunnel
-E 3des-cbc "123456789012123456789012";
spdadd 10.0.0.0/24 130.161.0.0/16 any -P in ipsec
esp/tunnel/10.0.0.216-10.0.0.11/require;
注意,与上面基本一样,除了把"-P out"换成了"-P in".就象先前的例子一样,
我们只配置了单向的传输.完整地实现双向传输就留给读者自己研究实现吧.
这种配置的另一个更直观的名字叫做"ESP代理(proxy ESP)".
IPSEC隧道需要内核能够进行IP转发!
7.4. 其它IPSEC软件
Thomas Walpuski报告说它已经写了一个补丁,可以让OpenBSD的isakpmd与
Linux 2.5的IPSEC协同工作.可以在这个网页找到.他指出isakpmd在Linux
上仅仅需要libkeynote支持.根据他的说法,在Linux 2.5.59上面工作得很好.
isakpmd与前面提到的racoon由很大的不同,但是很多人喜欢用它,可以在这里
找到.这里有OpenBSD CVS.Thomas还为那些不习惯用CVS或者patch的人
们制作了一个tarball.
7.5. IPSEC与其它系统的互操作
求助: Write this
7.5.1. Windows
33
/proc/sys/net/ipv4/ip_forward
在这里,你可能想知道是否起了作用.所以我们ping一下缺省组224.0.0.1,看
看有没有人在.在你的LAN上所有配置并启用了多播的机器都应该予以回应,
其他机器则不会.但你会注意到,没有任何一台机器回应的时候声明自己是
224.0.0.1,多么令人惊奇 !因为这是一个组地址(对于接收者来说是"广播",
所以组中的所有成员都用它们的地址来回应,而不是用组地址来回应.
ping -c 2 224.0.0.1
34
到此,你已经可以实现真正的多播路由了.好的,假定你需要在两个网络间进行
路由.
(To Be Continued!)
35
byte/s
9.1. 解释队列和队列规定
利用队列,我们决定了数据被发送的方式.必须认识到,我们只能对发送数据进
行整形.
根据Internet的工作方式,我们无法直接控制别人向我们发送什么数据.有点象
我们家里的信报箱,你不可能控制全世界,联系每一个人,修改别人对你发送邮
件的数量.
然而,Internet主要依靠TCP/IP,它的一些特性很有用.因为TCP/IP没办法知道
两个主机之间的网络容量,所以它会试图越来越快地发送数据(所谓的"慢起技
术" ,当因为网络容量不够而开始丢失数据时,再放慢速度.实际情况要比这
种方法更聪明,我们以后再讨论.
这就象当你尚未读完一半邮件时,希望别人停止给你寄信.与现实世界不同,在
Internet上可以做到这一点.(译注:这个例子并不恰当,TCP/IP的这种机制并不
是在网络层实现的,而是要靠传输层的TCP协议)
如果你有一个路由器,并且希望能够防止某些主机下载速度太快,你需要在你路
由器的内网卡——也就是向你的网内主机发送数据包的网卡——上进行流量整
形.
你还要保证你正在控制的是瓶颈环节.如果你有一个100M以太网卡,而你的路
由器的链路速度是256k,你必须保证你发送的数据量没有超过路由器的处理能
力.否则,就是路由器在控制链路和对带宽进行整形,而不是你.可以说,我们
需要拥有的队列必须是一系列链路中最慢的环节.幸运的是这很容易.
36
9.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看上去是这样的:
0 1 2 3 4 5 6 7
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | |
| 优先权 | TOS | MBZ |
| | | |
+-----+-----+-----+-----+-----+-----+-----+-----+
TOS字段的4个bit是如下定义的:
二进制 十进制 意义
-----------------------------------------
1000 8 最小延迟 (md)
0100 4 最大throughput (mt)
0010 2 最大可靠性 (mr)
37
7),它们不对应TOS映射,但是有其它的意图.
下表来自RFC 1349,告诉你应用程序可能如何设置它们的TOS:
TELNET 1000 (minimize delay)
控制 1000 (minimize delay) FTP
数据 0100 (maximize throughput)
TFTP 1000 (minimize delay)
命令阶段 1000 (minimize delay) SMTP
数据阶段 0100 (maximize throughput)
UDP 查询 1000 (minimize delay)
TCP 查询 0000
Domain Name Service
区域传输 0100 (maximize throughput)
NNTP 0001 (minimize monetary cost)
报错 0000
请求 0000 (mostly)
ICMP
响应 (mostly)
txqueuelen
38
队列的长度来自网卡的配置,你可以用ifconfig和ip命令修改.如设置队
列长度为10,执行:ifconfig eth0 txqueuelen 10
你不能用tc命令设置这个!
9.2.2. 令牌桶过滤器(TBF)
令牌桶过滤器(TBF)是一个简单的队列规定:只允许以不超过事先设定的速率到
来的数据包通过,但可能允许短暂突发流量朝过设定值.
TBF很精确,对于网络和处理器的影响都很小.所以如果您想对一个网卡限速,
它应该成为您的第一选择!
TBF的实现在于一个缓冲器(桶),不断地被一些叫做"令牌"的虚拟数据以特定
速率填充着. (token rate).桶最重要的参数就是它的大小,也就是它能够存储
令牌的数量.
每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除.这个算法关
联到两个流上——令牌流和数据流,于是我们得到3种情景:
数据流以等于令牌流的速率到达TBF.这种情况下,每个到来的数据包都
能对应一个令牌,然后无延迟地通过队列.
数据流以小于令牌流的速度到达TBF.通过队列的数据包只消耗了一部分
令牌,剩下的令牌会在桶里积累下来,直到桶被装满.剩下的令牌可以在
需要以高于令牌流速率发送数据流的时候消耗掉,这种情况下会发生突发
传输.
数据流以大于令牌流的速率到达TBF.这意味着桶里的令牌很快就会被耗
尽.导致TBF中断一段时间,称为"越限".如果数据包持续到来,将发
生丢包.
最后一种情景非常重要,因为它可以用来对数据通过过滤器的速率进行整形.
令牌的积累可以导致越限的数据进行短时间的突发传输而不必丢包,但是持续越
限的话会导致传输延迟直至丢包.
请注意,实际的实现是针对数据的字节数进行的,而不是针对数据包进行的.
9.2.2.1. 参数与使用
即使如此,你还是可能需要进行修改,TBF提供了一些可调控的参数.第一个参
数永远可用:
limit/latency
limit确定最多有多少数据(字节数)在队列中等待可用令牌.你也可以
通过设置latency参数来指定这个参数,latency参数确定了一个包在TBF
中等待传输的最长等待时间.后者计算决定桶的大小,速率和峰值速率.
39
burst/buffer/maxburst
桶的大小,以字节计.这个参数指定了最多可以有多少个令牌能够即刻被
使用.通常,管理的带宽越大,需要的缓冲器就越大.在Intel体系上,
10兆bit/s的速率需要至少10k字节的缓冲区才能达到期望的速率.
如果你的缓冲区太小,就会导致到达的令牌没有地方放(桶满了),这会
导致潜在的丢包.
mpu
一个零长度的包并不是不耗费带宽.比如以太网,数据帧不会小于64字
节.Mpu(Minimum Packet Unit,最小分组单位)决定了令牌的最低消耗.
rate
速度操纵杆.参见上面的limits!
如果桶里存在令牌而且允许没有令牌,相当于不限制速率(缺省情况).If the
bucket contains tokens and is allowed to empty