如何更方便地排查网络错误可能是一个涵盖面很广的话题。多数情况下,能否调试和处理TCP/IP网络问题取决于在你设计Ineternet协议时是否考虑了今后如何调试, 这里引入一个概念——Internet控制信息协议(ICMP).
ICMP是一个用来在主机和网关之间发送或接收控制信息的协议, 主要用来提供给发送者一个非常规或不同条件下到目标主机的反馈。
实际在你浏览互联网、阅读email或传送文件时存在很多的ICMP通讯, 通常它们在后台默默地工作。
在一个会话中,路由器 (你没忘了你正在建立一台路由器吧?) 使用ICMP来协商数据包大小和其它传送参数,这通常被称为路径MTU发现(path MTU discovery)。
也许你从网络管理员那里听到的都是有关ICMP的负面评论,或者如果他们对IMCP理解的透彻一些, 可能会对ICMP是又爱又恨——尽管IMCP可能“有缺陷”,但它却是必须的, 实际上这纯粹是因为历史原因造成的思维定势。 就在几年前,人们发现有几种操作系统的网络堆栈包含的代码存在缺陷—— 如果向安装这些操作系统的主机上发送一个足够大的ICMP请求时,可能导致它崩溃。
Microsoft就是曾因此遭受重创公司之一, 如果你使用搜索引擎找一下ping of death,可以发现大量有关该bug的相关资料。不过, 这一切全发生在1990年的下半年, 而所有现代的操作系统从那以后早已修正了相关网络代码。至少, 据我们知道的消息。这个问题的早期解决方案是简单地阻止ICMP ECHO (ping) 请求,或者阻止所有的IMCP通讯。尽管这类规则集已经存在了大约10年了, 但是现在有些还在应用这些规则集的人们却始终对ICMP谈虎色变。 尽管看起来一点也没必要再担心ICMP通讯的危害性了, 但是我们还是会在后面讲解怎样管理和如何仅放行某类IMCP数据包进出站。
那么, 我们放行所有的IMCP通讯吗?
问题明显变成了, 如果ICMP是如此的好和有用, 我们应该放行所有的IMCP通信吗? 答案是, 要看情况。
当然让诊断通讯无条件的通过的确可以使调试工作变得容易一些, 但是它也能使其他人更容易提取到你的网络信息。这意味着如果你想隐藏你的内部网络信息,一条像这样的规则
代码: 全选
pass inet proto icmp from any to any
从公正的角度, 你也应该发现有些在keep state规则上搭载的ICMP通讯基本无害。
最省事的做法: 也是最后的解决方法
最省事也是很好的做法是放行本地网络所有发起的ICMP通讯,并且在网关上对所有来自其它地方的IMCP通讯进行检查:
代码: 全选
pass inet proto icmp icmp-type $icmp_types from $localnet to any keep state
pass inet proto icmp icmp-type $icmp_types from any to $ext_if keep state
放行ping
到目前为止我们谈论的规则有一个明显的缺陷: 通常的排错命令,例如ping和traceroute无法工作。 这可能对你的用户无所谓, 某些人最初就是因为害怕这个ping命令而过滤或阻止ICMP通讯,他们看起来不希望有这个命令。 如果你愿意听我的建议, 你最好还是让这些工具可用。这需要在规则集里添加一些额外的内容。ping命令使用ICMP, 为了保持我们规则集的整洁, 我们在起始处定义另一个macro
代码: 全选
icmp_types = "echoreq"
代码: 全选
pass inet proto icmp all icmp-type $icmp_types keep state
帮助traceroute
在你的用户抱怨Internet不通时,另一个非常有用的命令就是traceroute。 默认情况下, Unix的traceroute使用一个根据规则基于目的地的UDP连接。下面的规则(见说明4)可以在所有我用过的类Unix系统中让traceroute命令正常工作, 当然也包含GNU/Linux:
代码: 全选
# allow out the default range for traceroute(8):
# "base+nhops*nqueries-1" (33434+64*3-1)
pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state
说明4
这里第一次让您了解怎样表示端口范围,这在某些情况下很有用处。
根据以往的经验,在其它的操作系统中也可以采用相似的方式让traceroute正常工作。 一个明显的例外是Microsoft Windows, 在这个平台上, TRACERT.EXE程序使用ICMP ECHO来实现这个目的。所以,如果你想让Windows的traceroute通过, 你只需要参照前一小节的第一条规则, 也就是允许ping通过。你也可以指定Unix的traceroute程序使用其它的协议, 这样可以让它的形式更像Microsoft中使用的命令行参数 -I (我怀疑这里翻译的有问题,因为在目前的windows里,tracert.exe没有-I参数)。 你可以参考一下traceroute的用户手册 (或源代码) 以了解更多的细节。
这个解决方案摘自一个openbsd-misc的帖子。 我已经发现了,如果你想了解OpenBSD或者PF的相关信息,这个邮件列表和其它可搜索的邮件列表档案 (你可以通过其它的地方获取,例如http://marc.info) 是非常有用的资源。
路径MTU发现(Path MTU Discovery)
当进行网络错误排查时,最后一点要注意的就是路径MTU发现。Internet协议被设计为与设备无关, 而与设备无关的一个问题就是你永远无法准确地预测出给定连接的最佳数据包大小。最大的数据包大小限制被称为Maximum Transmission Unit, 或者简称为MTU, 它为一个接口设置了数据包大小的上限。ifconfig命令可以用来显示网络接口的MTU。
现代的 TCP/IP 的工作时期望能测定出一个连接的准确数据包大小,它是通过发送大小不同的带有“do not fragment” 标志设定的一个简单方式实现的, 并希望达到上限时ICMP返回数据包声明类型是“type 3, code 4”。就目前来说, 你无需深入研究RFCs的相关知识。
Type 3 表示无法抵达目标, 而code 4是fragmentation的简要表达方式, 但是因为设置了“do not fragment”标志,所以如果你要连接的那个网络的MTU与你自己的不同, 就并非最佳, 你可以尝试稍微修改一下你的ICMP types列表以便让不可抵达目的地的数据包通过:
代码: 全选
icmp_types = "{ echoreq, unreach }"
代码: 全选
pass inet proto icmp all icmp-type $icmp_types keep state
不管怎么说, 尽管PF默认的keep state功能可以处理绝大部分你所需要的ICMP通讯, 但是PF的确可用来过滤各种不同ICMP类型或代码。如果你想深入研究更多的细节, 可能的类型和代码收录在用户手册icmp(4)和icmp6(4)里面。
相关的背景知识请参阅RFCs。
在Internet RFCs里,ICMP和一些相关技术知识的主要描述是 RFC 792, RFC 950, RFC 1191, RFC 1256, RFC 2521, 和RFC 2765, 而在IPv6上ICMP的更新请参阅RFC 1885, RFC 2463, 和 RFC 2466。
这些文档在Internet上全可以找到, 例如http://www.ietf.org 和 http://www.faqs.org, 还有就是可能在你的package系统中也会有所涉及。