无论您是在监控您的网络以便发现性能问题、调试一个应用,还是在您的网络上发现一个您不认识的应用,有时您都需要对您的 UNIX® 网络中使用的协议进行深度分析,以便了解它们的作用。有一些协议很容易确定和理解,即使它们使用非标准端口。而另外一些协议则需要进行更多的观察以了解它们在做什么、在交换什么信息。在本文中,我们将介绍对 UNIX 网络中使用的协议进行深度分析的技术。

Martin Brown, 自由撰稿人

Martin Brown 成为职业撰稿人已有八年之久。他所撰写的众多书籍和文章涵盖了各种各样的主题。他擅长的领域包括许多开发语言和平台(Perl、Python、Java、JavaScript、Basic、Pascal, Modula-2、C、C++、Rebol、Gawk、Shellscript、Windows、Solaris、Linux、BeOS 和 Mac OS/X 等等)以及 Web 编程和系统管理与集成。他是 ServerWatch.com、LinuxToday.com 和 IBM developerWorks 的定期撰稿人,还为 Computerworld、The Apple Blog 和其他站点定期编写博客,同时还是 Microsoft 的主题专家(SME)。



2010 年 7 月 14 日

引言

网络已经是无处不在,很多时候我们都会利用网络与不同主机进行通信,包括网络内部和外部的。大多数情况下这不会遇到问题,但是有时您需要仔细检查您的网络以查明问题原因。

仔细检查网络流量内容的原因有很多。其中第一个原因是您可能正在调试一个现有的网络应用,或者您正在开发一个应用,而您想要监控通过您的网络的流量。第二个原因是需要识别可能耗尽网络带宽和资源的流量。对于前一种情况,您可能已经知道协议的内容了,但是您希望能够更深入地了解正在传输的实际数据,例如,在使用 Web 服务时。对于后一种情况,确定数据包的内容需要了解正在使用的协议的一些扩展知识。

在 TCP/IP 和 UDP/IP 的通信中,最主要的元素是用于确定主机和端口号的 IP 地址。端口号用于提供额外的通信通道,这样您才能够在两个主机之间实现多个连接。其中端口定义还有一些标准。例如,端口 25 是专用于电子邮件(SMTP)传输,而大多数网站都是运行在端口 80(HTTP)上的。这些规范可以使程序之间通过一个熟悉的通道进行通信,这与您选择电话或传真号的道理是一样的。

虽然有这样一些规范,但是您实际想使用哪些端口是没有任何限制或约束的。事实上,大多数情况下一些破坏性网络应用和一些安全性方法会故意使用非标准端口。例如,有些应用会通过将一个标准端口用于不同的协议而隐藏内容,如在 端口 25 上使用 HTTP 协议。此外,有时某些应用也会使用与标准不同的端口,这样端口的用途就明显了(如,将端口 99 用于 HTTP),或者将特定的协议流量封装到另一个协议中。最后的方法实际上是网络通道和虚拟私有网络(VPN)所使用的方法。

不管网络流量原因和复杂性,第一个步骤都会开始记录数据。


记录原始数据

如果您希望记录网络原始数据,以便自己检查这些信息,那么您可以使用许多不同的工具。大多数的网络嗅探器也能够解码和解密特定的数据包内容,这能够帮助您研究一个已知协议的内容。

在 Solaris 上,您可以使用 snoop 工具,而在 AIX 上,您可以使用 iptrace 工具。您也可以尝试使用跨平台的 tcpdump 工具,它支持大多数的 UNIX 和 Linux 操作系统。这些工具能够帮您捕捉和解码数据包,通常也能为您执行大多数的协议分析。注意,现代交换机不会将 Ethernet 数据包发送到每一个端口上,这通常会限制您从当前主机获取的信息量。许多现代交换机具有一个管理端口,它通常带有与这种监控完全相同的所有数据包的副本。

解码网络传输最复杂的是网络数据包中信息的级别。此外,大部分信息也会经过二进制编码后再发送,从网络捕捉完全原始的数据包需要进行大量的操作才能捕捉您需要的数据。通过使用实现某些处理的工具,您可以简化解码网络数据的过程。

例如,在一个 Ethernet 上查看一个典型的 TCP/IP 协议,您将会发现网络中传输的数据包括:

  • Ethernet 数据包头,包括 Ethernet 来源和目标地址,数据包大小和 Ethernet 数据包类型。
  • IP 报头,由 IP 寻址(来源和目标),协议标识和 IP 标记。您也会得到关于分片和数据包顺序的信息。
  • TCP 报头,它包含端口上的信息、隐含的协议、标记和顺序编号。

即使有这些信息,我们仍然无法了解实际内容。在 TCP(或 UDP)协议之下还有额外的协议,标准数据协议(包括 HTTP、SMTP 和 FTP),或者封装性协议,如 Remote Procedure Call (RPC) 和 RPC 的子类型,如 NFS。

通常这些工具必须使用协议和/或端口号来确定正在传输的内容。所以,如果流量是通过非标准端口传输的,那么这个些信息可能无法正确解码。


基本的网络分析

本文之前提到的许多网络嗅探工具都提供了不同级别的协议解码,它们是通过检查端口和内容来确定所使用的协议实现的。

例如,snoop 和 tcpdump 都提供了关于 UDP 和 TCP 上不同协议的不同级别的详细信息。例如,在 snoop 中,您可以获得从顶级协议到所传输的单个数据块的关于 NFS 操作的详细信息。例如,您可以通过指定监控 RPC 使用 NFS 协议来实现对 NFS 流量的监控:$ snoop -v rpc nfs

它能输出非常详细的数据包信息,并且它们应该进行更紧密的观察。清单 1 提供了 Ethernet 报头数据。

清单 1. Ethernet 报头数据
ETHER:  ----- Ether Header -----
ETHER:  
ETHER:  Packet 64 arrived at 16:14:41.79434
ETHER:  Packet size = 238 bytes
ETHER:  Destination = 0:1a:ee:1:1:c0, 
ETHER:  Source      = 0:21:28:3c:c0:61, 
ETHER:  Ethertype = 0800 (IP)
ETHER:

这里的输出表明 Ethernet 数据包包含了 IP 数据、全部数据包大小和时间,以及数据包的目标和来源地址。

清单 2 显示的是 IP 报头。其中除了协议和来源/目标地址信息,其余许多 IP 数据是没有用的。

清单 2. IP 报头
IP:   ----- IP Header -----
IP:   
IP:   Version = 4
IP:   Header length = 20 bytes
IP:   Type of service = 0x00
IP:         xxx. .... = 0 (precedence)
IP:         ...0 .... = normal delay
IP:         .... 0... = normal throughput
IP:         .... .0.. = normal reliability
IP:         .... ..0. = not ECN capable transport
IP:         .... ...0 = no ECN congestion experienced
IP:   Total length = 224 bytes
IP:   Identification = 27460
IP:   Flags = 0x4
IP:         .1.. .... = do not fragment
IP:         ..0. .... = last fragment
IP:   Fragment offset = 0 bytes
IP:   Time to live = 64 seconds/hops
IP:   Protocol = 6 (TCP)
IP:   Header checksum = 4d11
IP:   Source address = 192.168.0.112, tiger.mcslp.pri
IP:   Destination address = 192.168.0.2, bear.mcslp.pri
IP:   No options
IP:

清单 3 中,您可以看到 TCP 报头。同样,这些信息中通常只有来源和目标端口号才是有用的,因为这些信息将可用于确定预期的协议,或者提供给您可用于更进一步观察这个端口所传输流量的信息。

清单 3. TCP 报头
TCP:  ----- TCP Header -----
TCP:  
TCP:  Source port = 2049
TCP:  Destination port = 889 (Sun RPC)
TCP:  Sequence number = 2834727685
TCP:  Acknowledgement number = 2654368001
TCP:  Data offset = 32 bytes
TCP:  Flags = 0x18
TCP:        0... .... = No ECN congestion window reduced
TCP:        .0.. .... = No ECN echo
TCP:        ..0. .... = No urgent pointer
TCP:        ...1 .... = Acknowledgement
TCP:        .... 1... = Push
TCP:        .... .0.. = No reset
TCP:        .... ..0. = No Syn
TCP:        .... ...0 = No Fin
TCP:  Window = 32806
TCP:  Checksum = 0x4852
TCP:  Urgent pointer = 0
TCP:  Options: (12 bytes)
TCP:    - No operation
TCP:    - No operation
TCP:    - TS Val = 34449495, TS Echo = 253458642
TCP:

清单 4 的倒数第二部分显示的是 RPC 报头数据。

清单 4. RPC 报头数据
RPC:  ----- SUN RPC Header -----
RPC:  
RPC:  Record Mark: last fragment, length = 168
RPC:  Transaction id = 3041181596
RPC:  Type = 1 (Reply)
RPC:  This is a reply to frame 63
RPC:  Status = 0 (Accepted)
RPC:  Verifier   : Flavor = 0 (None), len = 0 bytes
RPC:  Accept status = 0 (Success)
RPC:

最后,清单 5 提供了 NFS 数据包内容,包括权限(文件模式)、文件大小、拥有者和其他信息。在这里,这个 NFS 操作请求是进行文件系统统计(这等同于 ls 操作结果),因此这就是详细信息。

清单 5. NFS 数据包内容
NFS:  ----- Sun NFS -----
NFS:  
NFS:  Proc = 18 (Get filesystem statistics)
NFS:  Status = 0 (OK)
NFS:  Post-operation attributes: 
NFS:    File type = 2 (Directory)
NFS:    Mode = 0777
NFS:     Setuid = 0, Setgid = 0, Sticky = 0
NFS:     Owner's permissions = rwx
NFS:     Group's permissions = rwx
NFS:     Other's permissions = rwx
NFS:    Link count = 24, User ID = 502, Group ID = 10
NFS:    File size = 29, Used = 2560
NFS:    Special: Major = 4294967295, Minor = 4294967295
NFS:    File system id = 781684113418, File id = 4304616
NFS:    Last access time      = 28-Feb-10 15:49:51.042953989 GMT
NFS:    Modification time     = 25-Feb-10 09:39:07.965422590 GMT
NFS:    Attribute change time = 25-Feb-10 09:39:07.965422590 GMT
NFS:  
NFS:  Total space = 759567510016 bytes
NFS:  Available space = 659048374272 bytes
NFS:  Available space - this user = 659048374272 bytes
NFS:  Total file slots = 1288161604
NFS:  Available file slots = 1287203856
NFS:  Available file slots - this user = 1287203856
NFS:  Invariant time = 0 sec
NFS:

在这里,我们可以看到所查询的文件事实上是一个目录(见文件类型行)。虽然我们没有得到文件的实际路径,但是我们可以通过使用 Find 命令来查询 inode 号对应的文件/路径来查找上面提到的目录(见 清单 6)。

清单 6. 查询 inode 号对应的文件
$ find /scratch -xdev -inum 4304616
/scratch/installed/mysql-6.0.11

如果您准备分辨流量,那么使用这些工具的最佳方法是首先运行它们,收集尽可能多的数据,然后手动检查数据内容,找出您的网络本来不应该出现的数据项。

一旦您识别出了可疑流量,您就可以开始在命令行上添加参数以便关注于流量细节。例如,您可以使用 清单 7 所示的命令之一,规定只显示特定主机的流量。

清单 7. 规定只显示一个特定主机的流量
$ snoop host 192.168.0.2
$ tcpdump host 192.168.0.2

要进一步限制,您可以限制协议信息的端口:$ snoop host 192.168.0.2 and port 25


解析原始数据以理解数据包内容

处理来自 tcpdump 的另一个方法是将原始网络数据包数据保存到一个文件中,然后处理这个文件以便查找和解码出您想要的信息。

有许多使用不同语言编写的模块具有读取和解码 tcpdump 和 snoop 捕捉的数据的功能。例如,有两个用 Perl 编写的模块:Net::SnoopLog(针对 snoop)和 Net::TcpDumpLog(针对 tcpdump)。它们将读取原始数据内容。这些模块的基本接口是相同的。

要开始处理,您首先需要使用 snoop 或 tcpdump 将数据写到一个文件,而创建一个通过网络的数据包的二进制记录。对于本例,我们将使用 tcpdump 和 Net::TcpDumpLog 模块:$ tcpdump -w packets.raw

收集网络数据完成后,您就可以开始处理网络数据内容以查找您想要的信息。Net::TcpDumpLog 会解析 tcpdump 所保存的原始网络数据。因为这些数据是以原始的二进制格式保存的,解析这些信息就需要处理这个二进制数据。为了方便起见,您可以使用另一组模块 NetPacket::* 来解码原始数据。

例如,清单 8 显示的是一个打印所有数据包的 IP 地址信息的简单脚本。

清单 8. 打印所有数据包的 IP 地址的简单脚本
use Net::TcpDumpLog;
    
use NetPacket::Ethernet;
    
use NetPacket::IP;

    
my $log = Net::TcpDumpLog->new();
 
$log->read("packets.raw");
 
 
foreach my $index ($log->indexes)
       
{
    
    my $packet = $log->data($index);
           

    my $ethernet = NetPacket::Ethernet->decode($packet);

  
    if ($ethernet->{type} == 0x0800)
       
    {
    
        my $ip = NetPacket::IP->decode($ethernet->{data});
          

    
        printf("  %s to %s protocol %s \n",
               $ip->{src_ip},$ip->{dest_ip},$ip->{proto});
   }

}

第一部分是提取每一个数据包。Net::TcpDumpLog 模块会将每一个数据包序列化,这样我们就能够通过数据包 ID 读取每一个数据包。然后 data() 函数就会返回整个数据包的原始数据。

通过 snoop 的输出,我们必须从原始网络数据包信息中提取每一个数据块。所以在本例中,我们首先需要从原始网络数据包中提取 Ethernet 数据包,包括数据有效负载。而 NetPacket::Ethernet 模块能够帮我们执行这个操作。

因为我们寻找的是 IP 数据包,所以我们能通过检查 Ethernet 数据包类型来检查 IP 数据包。IP 数据包的 ID 为 0x0800。

然后,NetPacket::IP 模块会被用于从 Ethernet 数据包的数据负载中提取出 IP 信息。这个模块能提供来源 IP、目标 IP 和协议信息等等,然后我们可以打印出这些信息。

通过使用这个基本的框架,您就能够执行不依赖于 tcpdump 或 snoop 的自动化解决方案的更复杂查询和解码。例如,如果您怀疑 HTTP 流量是通过一个非标准端口传输的(如,不是端口 80),那么您可以使用 清单 9 中的脚本在怀疑的主机 IP 的非 80 端口上检查 HTTP 数据包。

清单 9. 在非 80 端口上检查 HTTP 数据包
use Net::TcpDumpLog;
    
use NetPacket::Ethernet;
    
use NetPacket::IP;
    
use NetPacket::TCP;
    

    
my $log = Net::TcpDumpLog->new();
       
$log->read("packets.raw");
       

    
foreach my $index ($log->indexes)
       
{
    
    my $packet = $log->data($index);
       

    
    my $ethernet = NetPacket::Ethernet->decode($packet);
       

    
    if ($ethernet->{type} == 0x0800)
       
    {
    
        my $ip = NetPacket::IP->decode($ethernet->{data});
          

    
        if ($ip->{src_ip} eq '192.168.0.2')
       
        {
    
            if ($ip->{proto} == 6)
       
            {
    
                my $tcp = NetPacket::TCP->decode($ip->{data});
       
                if (($tcp->{src_port} != 80) &&
               
                    ($tcp->{data} =~ m/HTTP/))
       
                {
    
                    print("Found HTTP traffic on non-port 80\n");
    
                    printf("%s (port: %d) to %s (port: %d)\n%s\n",
    
                           $ip->{src_ip},
       
                           $tcp->{src_port},
       
                           $ip->{dest_ip},
       
                           $tcp->{dest_port},
       
                           $tcp->{data});
 
                }
    
            }
    
        }
    
   }
    
}

在一个示例数据包集上运行上面的脚本会返回如 清单 10 所示的结果。

清单 10. 在一个示例数据包集上运行这个脚本
$ perl http-non80.pl
Found HTTP traffic on non-port 80
192.168.0.2 (port: 39280) to 168.143.162.100 (port: 80)
GET /statuses/user_timeline.json HTTP/1.1
Found HTTP traffic on non-port 80
192.168.0.2 (port: 39282) to 168.143.162.100 (port: 80)
GET /statuses/friends_timeline.json HTTP/1

在这种特定的情况下,我们发现主机的流量是通向一个外部网站(Twitter)。

显然,在这个例子中我们处理的是原始数据,但是您可以使用相同的基本结构进行解码,也可以使用任何公开或私有协议结构的格式的数据。如果您正在使用这个方法使用或开发一个协议,并且您知道协议格式,您就能够提取和监控正在传输的数据。


使用一个协议分析器

虽然,正如之前提到的,诸如 tcpdump、iptrace 和 snoop 等工具都提供了基本的网络分析和解码功能,但是还有一些基于 GUI 的工具使这个过程更简单。Wireshark 就是其中一个工具,它支持大量的网络协议解码和分析。

Wireshark 的最主要优点之一是您可以捕捉一段时间内的数据包(如 tcpdump 一样),然后基于不同的协议、端口和其他数据交互地分析和过滤内容。Wireshark 也支持大量的协议解码器,这使您能够检查每分钟内数据包和会话的详细内容。

您可以看到 Wireshark 的简单截图显示了所有类型的所有数据包,如 图 1 所示。这个窗口分成三个主要部分:过滤的数据包列表、解码的协议明细和 HEX/ASCII 格式的原始数据包数据。

图 1. Wireshark 界面
Wireshark 界面接口

作为 Wireshark 工具所提供的一个级别的信息和解码的例子,在撰写本文时我注意网络中的一个 MySQL 服务器返回了一些错误数据包。

为了专注于内容,我首先对输出应用了 MySQL 过滤。您可以通过在 Filter 输入框中输入一个表达式(类似于 tcpdump、snoop 或 iptract)。或者,您可以单击 Expression 按钮,然后从内置的列表的中选择一种过滤。您可以在 图 2 中看到一组示例过滤。一旦您选择了某个过滤,单击 Apply 就可以过滤数据包列表。

图 2. 选择一个 Wireshark 过滤
选择一个 Wireshark 过滤的截图

通过过滤 MySQL 协议,我能够确定错误的数据包。MySQL 协议会传到一个带有错误信息的特殊数据包类型。在这里,错误 1242 表示查询操作失败,由于子查询有问题。您可以通过展开 Wireshark 窗口的 MySQL 协议部分查看 MySQL 协议的内容,如 图 3 所示。

图 3. 检查一个 MySQL 错误数据包
检查一个 MySQL 错误数据包的截图

在这里我们可以看到错误的明细。通过跟踪之前的 ‘Request Query’ 数据包,我们就可能确定出导致产生错误响应的查询(图 4)、

图 4. 导致产生错误响应的 MySQL 查询
导致产生错误响应的 MySQL 查询的截图

通过分析数据包,我能够识别之前并没有注意的错误代码,并且能够指出这个错误及导致发生问题的查询。

Wireshark 支持广泛的协议和过滤器,您可以用它们获得详细的信息。它的另一个常见用途是监控详细协议的确切内容,如 Web 服务。图 5 显示了一个来自 SOAP 请求用于记录状态信息的详细(且结构清晰的)信息。

图 5. 查看 SOAP Web 服务请求的详细信息
SOAP Web 服务请求的详细信息截图

这些详细信息在调试您使用的任何网络协议时是非常有用的。

Wireshark 的另一个有用的特性是它能够处理即时信息,并且它能够记录信息以备将来过滤和处理。这表示您可以使用它监控某个时期的可疑流量,然后您可以在恰当的时候分析这些信息,以查明您的网络中发生了什么操作。


结束语

对于通过您的 UNIX 网络的信息进行协议分析可能是一个复杂的过程。但是,通过使用一些简单而广泛使用的工具,您可以解码和检查您的流量的详细信息,包括来源和目标等基本信息,以及具体的协议和传输的数据。

如本文所述,通过使用诸如 tcpdump、snoop 或 iptrace 的工具,您可以在命令行上提取大量的数据。通过使用诸如 Wireshark 的工具,您可以更一地步地了解更大范围的协议和内容的更多详细信息。对于自定义的协议和结构,您可以使用 Perl 提取原始数据,并获得您需要的所有信息。

参考资料

学习

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Open source, Linux
ArticleID=500405
ArticleTitle=UNIX 网络协议的深度分析
publish-date=07142010