网络层向上层提供简单灵活的、无连接的、尽最大努力交付的数据报服务。该层重要的协议有IP、ICMP、IGMP、ARP、RARP等。
IP是TCP/IP协议族中最为核心的协议。它把上层数据报封装成IP数据报后进行传输。如果IP数据报太大,还要对数据报进行分片后再传输,到了目的地址处再进行组装还原,以适应不同物理网络对一次所能传输的数据大小的要求。
不可靠的意思是它不能保证IP数据报成功地到达目的地。IP仅提供最好的传输服务,如果发生某种错误,比如某个路由器暂时用完了缓冲区,IP有一个简单的差错处理算法:丢弃该数据报,然后发送ICMP消息给信源端。任何要求的可靠性必须由上层协议来提供(如TCP)。
无连接的意思是IP并不维护任何关于后续数据报的状态信息。每个数据报的处理是相互独立的,这也说明IP数据报可以不按发送顺序接收。如果一个信源向相同的信宿发送两个连续的数据报(先是A,再是B),每个数据报都是独立地进行路由选择,可能选择不同的路线,因此B可能在A之前到达。
无状态的意思是通信双方不同步传输数据的状态信息,无法处理乱序和重复的IP数据报。IP数据报提供了标识字段用来唯一标识IP数据报,用来处理IP分片和重组,不指示接收顺序。
IPv4数据报的报头格式如图1-8所示。
图1-8
IPv4的报头结构与IPv6的报头结构不同,图1-8中的“数据”以上部分就是IP报头的内容。因为有了选项部分,所以IP报头长度是不定长的。如果没有选项部分,则IP报头的长度为(4+4+8+16+16+3+13+8+8+16+32+32)bit=160bit=20Byte,这也就是IP报头的最小长度。
表1-1 PPP取值及其含义
PPP后面的DTRC0含义如下:
D:延迟,0表示常规,1表示延迟尽量小。
T:吞吐量,0表示常规,1表示吞吐量尽量大。
R:可靠性,0表示常规,1表示可靠性尽量大。
M:传输成本,0表示常规,1表示成本尽量小。
0:这是最后一位,被保留,恒定为0。
在Linux源码中,IP报头的定义如下:
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif __u8 tos; __be16 tot_len; __be16 id; __be16 frag_off; __u8 ttl; __u8 protocol; __sum16 check; __be32 saddr; __be32 daddr; /*The options start here. */ };
这个定义可以在源码目录的include/uapi/linux/ip.h中查到。
IP在传输数据报时,将数据报分为若干分片(小数据报)后进行传输,并在目的端系统中进行重组。这一过程称为分片。
要理解IP分片,首先要理解MTU(Maximum Transmission Unit,最大传输单元),物理网络一次传送的数据是有最大长度的,因此网络层下一层(数据链路层)的传输单元(数据帧)也有一个最大长度,该最大长度值就是MTU。每一种物理网络都会规定数据链路层数据帧的最大长度,比如以太网的MTU为1500字节。
IP在传输数据报时,如果IP数据报加上数据帧报头后长度大于MTU,则将数据报切分成若干分片(小数据报)后再进行传输,并在目的端系统中进行重组。IP分片既可能在源端主机进行,又可能发生在中间的路由器处,因为不同网络的MTU是不一样的,而传输的整个过程可能会经过不同的物理网络。如果传输路径上的某个网络的MTU比源端网络的MTU要小,路由器就可能对IP数据报再次进行分片。分片数据的重组只会发生在目的端的IP层。
IP中有一个非常重要的内容是IP地址。所谓IP地址,就是互联网中主机的标识,互联网中的主机要与别的主机通信必须具有一个IP地址。就像房子要有一个门牌号,这样邮递员才能根据信封上的家庭地址送到目的地。
IP地址现在有两个版本,分别是32位的IPv4和128位的IPv6,后者是为了解决前者不够用而产生的。每个IP数据报都必须携带目的IP地址和源IP地址,路由器依据此信息为数据报选择路由。
这里以IPv4为例,IP地址是由4个数字组成的,数字之间用小圆点隔开,每个数字的取值范围为0~255(包括0和255)。通常有两种表示形式:
两种形式可以相互转换,每8位二进制数对应1位十进制数,如图1-9所示。
图1-9
在实际应用中多用十进制表示。
互联网由很多网络构成,每个网络上都有很多主机,这样就构成了一个有层次的结构。IP地址在设计时就考虑到地址分配的层次特点,把每个IP地址分割成网络号(NetID)和主机号(HostID)两部分。网络号表示主机属于互联网中的哪一个网络,而主机号则表示其属于该网络中的哪一台主机。两者之间是主从关系,同一网络中绝对不能有主机号完全相同的两台计算机,否则会导致IP地址冲突。IP地址分为两部分后,IP数据报从网际上的一个网络到达另一个网络时,选择路径可以基于网络而不是主机。在大型的网际中,这一优势特别明显,因为路由表中只存储网络信息而不是主机信息,这样可以大大简化路由表,大大方便路由器的IP寻址。
根据网络地址和主机地址在IP地址中所占的位数可将IP地址分为A、B、C、D、E五类,每一类网络可以从IP地址的第一个数字看出,如图1-10所示。
图1-10
A类IP地址的第一个字节范围是0~126,B类IP地址的第一个字节范围是128~191,C类IP地址的第一个字节范围是192~223,所以看到192.X.X.X肯定是C类IP地址,读者根据IP地址的第一个字节的范围就能够推导出该IP地址属于A类、B类还是C类。
IP地址以A、B、C三类为主,又以B、C两类更为常见。除此之外,还有一些特殊用途的IP地址:广播地址(主机地址全为1,用于广播,这里的广播是指同时向网上的所有主机发送报文,不是指我们日常听的那种广播)、有限广播地址(所有地址全为1,用于本网广播)、本网地址(网络地址全0,后面的主机号表示本网地址)、回送测试地址(127.x.x.x型,用于网络软件测试及本地机进程间通信)、主机位全0地址(这种地址的网络地址就是本网地址)及保留地址(网络号全1和32位全0两种)。由此可见,网络位全1或全0和主机位全1或全0都是不能随意分配的,这也是前面的A、B、C类网络的网络数及主机数要减2的原因。
总之,主机号全为0和全为1时分别作为本网地址和广播地址使用,这种IP地址不能分配给用户使用。D类网络用于广播,它可以将信息同时传送到网上的所有设备,而不是点对点的信息传送,这种网络可以用来召开电视/电话会议。E类网络常用于进行试验。网络管理员在配置网络时不应该采用D类和E类网络。我们把特殊的IP地址列在表1-2中。
表1-2 特殊的IP地址
当前,A类地址已经全部分配完,B类也不多了,为了有效并连续地利用剩下的C类地址,互联网采用CIDR(Classless Inter-Domain Routing,无类别域间路由)方式把许多C类地址合起来作为B类地址分配。整个世界被分为4个地区,每个地区分配一段连续的C类地址:欧洲(194.0.0.0~195.255.255.255)、北美(198.0.0.0~199.255.255.255)、中南美(200.0.0.0~201.255.255.255)、亚太地区(202.0.0.0~203.255.255.255)、保留备用(204.0.0.0~223.255.255.255)。这样每一类都有约3200万网址可供使用。
在IP地址的两级编址中,IP地址由网络号和主机号两部分组成。如果我们把主机号部分全部置为0,此时得到的地址就是网络地址。网络地址可以用于确定主机所在的网络,为此路由器只需计算出IP地址中的网络地址,然后跟路由表中存储的网络地址相比较,就可以知道这个分组应该从哪个接口发送出去。当分组达到目的网络后,再根据主机号抵达目的主机。
要计算出IP地址中的网络地址,需要借助网络掩码,或称默认掩码。它是一个32位的数,左边连续 n 位全部为1,右边32- n 位连续为0。A、B、C三类地址的网络掩码分别为255.0.0.0、255.255.0.0和255.255.255.0。我们通过IP地址和网络掩码进行“与”运算,得到的结果就是该IP地址的网络地址。网络地址相同的两台主机处于同一个网络中,它们可以直接通信,而不必借助路由器。
举一个例子,现在有两台主机A和B,A的IP地址为192.168.0.1,网络掩码为255.255.255.0,B的IP地址为192.168.0.254,网络掩码为255.255.255.0。我们先对A做运算,把它的IP地址和子网掩码按位相“与”:
IP: 11010000.10101000.00000000.00000001 子网掩码: 11111111.11111111.11111111.00000000 AND运算 网络号: 11000000.10101000.00000000.00000000 转换为十进制:192.168.0.0
再把B的IP地址和子网掩码按位相“与”:
IP: 11010000.10101000.00000000.11111110 子网掩码: 11111111.11111111.11111111.00000000 AND运算 网络号: 11000000.10101000.00000000.00000000 转换为十进制:192.168.0.0
我们看到,A和B两台主机的网络号是相同的,因此可以认为它们处于同一网络。
由于IP地址越来越不够用,为了不浪费,人们对每类网络进一步划分出了子网,为此IP地址又有了三级编址的方法,即子网内的某个主机IP地址={<网络号>,<子网号>,<主机号>}。该方法中有了子网掩码的概念,后来又提出了超网、无分类编址和IPv6。限于篇幅,这里不再赘述。
网络上的IP数据报到达最终目的网络后,必须通过MAC地址来找到最终目的主机,而数据报中只有IP地址,为此需要把IP地址转为MAC地址,这个工作就由ARP来完成。ARP是网络层中的协议,用于将IP地址解析为MAC地址。通常,ARP只适用于局域网。ARP的工作过程如下:
1)本地主机在局域网中广播ARP请求,ARP请求数据帧中包含目的主机的IP地址。这一步所表达的意思是“如果你是这个IP地址的拥有者,请回答你的硬件地址”。
2)目的主机收到这个广播报文后,用ARP解析这份数据报,识别出是询问其硬件地址,于是发送ARP应答数据报,其中包含IP地址及其对应的硬件地址。
3)本地主机收到ARP应答后,知道了目的地址的硬件地址,之后的数据报就可以传送了。同时,会把目的主机的IP地址和MAC地址保存在本机的ARP表中,以后通信直接查找此表即可。
可以在Windows操作系统的命令行下使用“arp –a”命令来查询本机ARP缓存列表,如图1-11所示。
图1-11
另外,可以使用“arp -d”命令来清除ARP缓存表。
ARP通过发送和接收ARP数据报来获取物理地址,ARP数据报的格式如图1-12所示。
图1-12
ether_header结构定义了以太网报头;arphdr结构定义了其后的5个字段,其信息用于在任何类型的介质上传送ARP请求和应答;ether_arp结构除了包含arphdr结构外,还包含源主机和目的主机的地址。如果这个数据报格式用C语言来表述,可以这样编写:
//定义常量 #define EPT_IP 0x0800 /* type: IP */ #define EPT_ARP 0x0806 /* type: ARP */ #define EPT_RARP 0x8035 /* type: RARP */ #define ARP_HARDWARE 0x0001 /* Dummy type for 802.3 frames */ #define ARP_REQUEST 0x0001 /* ARP request */ #define ARP_REPLY 0x0002 /* ARP reply */ //定义以太网报头 typedef struct ehhdr { unsigned char eh_dst[6]; /* destination ethernet address */ unsigned char eh_src[6]; /* source ethernet address */ unsigned short eh_type; /* ethernet packet type */ }EHHDR, *PEHHDR; //定义以太网ARP字段 typedef struct arphdr { //ARP报头 unsigned short arp_hrd; /* format of hardware address */ unsigned short arp_pro; /* format of protocol address */ unsigned char arp_hln; /* length of hardware address */ unsigned char arp_pln; /* length of protocol address */ unsigned short arp_op; /* ARP/RARP operation */ unsigned char arp_sha[6]; /* sender hardware address */ unsigned long arp_spa; /* sender protocol address */ unsigned char arp_tha[6]; /* target hardware address */ unsigned long arp_tpa; /* target protocol address */ }ARPHDR, *PARPHDR;
定义整个ARP数据报,总长度为42字节:
typedef struct arpPacket { EHHDR; ARPHDR; } ARPPACKET, *PARPPACKET;
RARP允许局域网的物理机器从网关服务器的ARP表或者缓存上请求其IP地址。比如局域网中有一台主机只知道自己的物理地址而不知道自己的IP地址,那么可以通过RARP发出征求自身IP地址的广播请求,然后由RARP服务器负责回答。RARP广泛应用于无盘工作站引导时获取IP地址。RARP允许局域网的物理机器从网管服务器ARP表或者缓存上请求其IP地址。
RARP的工作过程如下:
1)主机发送一个本地的RARP广播,在此广播包中声明自己的MAC地址并且请求任何收到此请求的RARP服务器分配一个IP地址。
2)本地网段上的RARP服务器收到此请求后,检查其RARP列表,查找该MAC地址对应的IP地址。
3)如果IP地址存在,RARP服务器就给源主机发送一个响应数据报并将此IP地址提供给对方主机使用。如果IP地址不存在,RARP服务器对此不做任何响应。
4)源主机收到RARP服务器的响应信息后,将利用得到的IP地址进行通信。如果一直没有收到RARP服务器的响应信息,则表示初始化失败。
RARP的帧格式与ARP只是帧类型字段和操作类型不同。
ICMP是网络层的一个协议,用于探测网络是否连通、主机是否可达、路由是否可用等。简单来讲,它是用来查询、诊断网络的。
虽然和IP协议同处网络层,但ICMP报文却是作为IP数据报的数据,然后加上IP报头后再发送出去的,如图1-13所示。
图1-13
IP报头的长度为20字节。ICMP报文作为IP数据报的数据部分,当IP报头的协议字段取值为1时,其数据部分是ICMP报文。ICMP报文格式如图1-14所示。
图1-14
其中,最上面(0,8,16,31)指的是比特位,所以前3个字段(类型、代码、校验和)共占了32比特(类型占8位,代码占8位,校验和占16位),即4字节。所有ICMP报文前4字节的格式都是一样的,即任何ICMP报文都含有类型、代码、校验和这3个字段,8位类型和8位代码字段一起决定了ICMP报文的种类。紧接着后面4字节取决于ICMP报文的种类。前面8字节就是ICMP报文的报头,后面的ICMP数据部分的内容和长度也取决于ICMP报文的种类。16位的校验和字段是包括选项数据在内的整个ICMP数据报文的校验和,其计算方法和IP头部校验和的计算方法是一样的。
ICMP报文可分为两大类别:差错报告报文和查询报文。报文的代码及其含义如表1-3所示。
表1-3 报文的代码及其含义
从表1-3中可以看出,每一行都是一条(或称每一种)ICMP报文,它要么属于查询,要么属于差错报告。
从表1-3中可以发现,属于差错报告报文的ICMP报文蛮多的,为了方便归纳,可以将这些差错报告报文分为5种类型:目的不可达(类型=3)、源端被关闭(类型=4)、重定向(类型=5)、超时(类型=11)和参数问题(类型=12)。
代码字段不同的取值进一步表明了该类型ICMP报文的具体情况,比如类型为3的ICMP报文都是表明目的不可达,这是什么原因呢?此时就用代码字段进一步说明,比如代码为1表示网络不可达,代码为2表示主机不可达,等等。
ICMP规定,ICMP差错报告报文必须包括产生该差错报告报文的源数据报的IP报头,还必须包括跟在该IP(源IP)报头后面的前8字节,这样ICMP差错报告报文的IP报长度=本IP报头(20字节)+本ICMP报头(8字节)+源IP报头(20字节)+源IP报的IP报头后的8字节=56字节。可以使用图1-15来表示ICMP差错报告报文。
图1-15
比如我们来看一个具体的UDP端口不可达的差错报文,如图1-16所示。
图1-16
从图1-16可以看到,IP数据报的长度是56字节。为了让读者更形象地了解这5类差错报告报文的格式,我们用图形来表示每一类报文。
目的不可达也称终点不可达,可分为网络不可达、主机不可达、协议不可达、端口不可达、需要分片但设置了不分片比特(DF(方向标志)比特置为1)等16种报文,其代码字段分别置为0~15。当出现以上16种情况时就向源站发送目的不可达报文。目的不可达报文的格式如图1-17所示。
图1-17
也称源站抑制,当路由器或主机由于拥塞而丢弃数据报时,就向源站发送源站抑制报文,让源站知道应当将数据报的发送放慢。这类报文的格式如图1-18所示。
图1-18
当IP数据报应该被发送到另一个路由器时,收到该数据报的当前路由器就要发送ICMP重定向报文给IP数据报的发送端。重定向一般用来让具有很少路由信息的主机逐渐建立更完善的路由表。ICMP重定向报文只能由路由器产生。这类报文的格式如图1-19所示。
图1-19
当路由器收到生存时间为零的数据报时,除了丢弃该数据报外,还要向源站发送超时报文。当目的站在预先规定的时间内不能收到一个数据报的全部分片时,就将已收到的分片丢弃,并向源站发送超时报文。这类报文的格式如图1-20所示。
图1-20
当路由器或目的主机收到的数据报的报头中的字段值不正确时,就丢弃该数据报,并向源站发送参数问题报文。这类报文的格式如图1-21所示。
图1-21
根据功能的不同,ICMP查询报文可以分为4大类:回显(Echo)请求或应答、时间戳(Timestamp)请求或应答、地址掩码(Address Mask)请求或应答、路由器请求或通告。前面提到,种类由类型和代码字段决定,我们来看一下它们的类型和代码,如表1-4所示。
表1-4 ICMP查询报文的类型和代码
这里要提一下回显请求和应答,Echo的中文翻译为回声,有的文献翻译为回送或回显,本书采用回显。回显请求的含义就是请求对方回复一个应答。我们知道Linux和Windows下各有一个ping命令,Linux下的ping命令产生的ICMP报文大小是(56+8=)64字节,56是ICMP报文数据部分长度,8是ICMP报头部分长度,而Windows下ping命令产生的ICMP报文大小是(32+8=)40字节。该命令就是本机向一个目的主机发送一个回显请求(类型为8)的ICMP报文,如果途中没有异常(例如被路由器丢弃、目标不回应ICMP或传输失败),则目标返回一个回显应答的ICMP报文(类型为0),表明这台主机存在。后面的章节还会讲到ping命令的抓包和编程。
为了让读者更加形象地了解这4类查询报文的格式,我们用图形来表示每一类报文。
1)ICMP回显请求和应答报文的格式如图1-22所示。
图1-22
ICMP时间戳请求和应答报文的格式如图1-23所示。
图1-23
3)ICMP地址掩码请求和应答报文的格式如图1-24所示。
图1-24
4)ICMP路由器请求和通告报文的格式分别如图1-25和图1-26所示。
图1-25
图1-26
1)启动虚拟机VMware下的Windows操作系统,设置网络连接方式为NAT,则虚拟机中的Windows 7会连接到虚拟交换机VMnet8上。
2)在Windows下安装并打开抓包软件Wireshark,选择要捕获网络数据报的网卡是VMware Network Adapter VMnet8,如图1-27所示。
图1-27
双击图1-27中选中的网卡,即可在该网卡上捕获数据。此时我们在虚拟机的Windows(192.168.80.129)下ping宿主机(192.168.80.1),可以在Wireshark下看到捕获到的ping包。图1-28所示是回显请求,可以看到ICMP报文的数据部分是32字节,如果加上ICMP报头(8字节),那就是40字节。
图1-28
我们可以再看一下回显应答,ICMP报文的数据部分长度依然是32字节,如图1-29所示。
图1-29
1)启动VMware下的Linux系统,设置网络连接方式为NAT,则虚拟机中的Linux会连接到虚拟交换机VMnet8上。
2)在Linux下安装并打开抓包软件Wireshark,选择要捕获网络数据报的网卡是VMware Network Adapter VMnet8,其显示结果类似于图1-27。
在虚拟机的Linux(192.168.80.128)下ping宿主机(192.168.80.1),可以在Wireshark下看到捕获的ping包。图1-30所示是回显请求,可以看到ICMP报文的数据部分是56字节,如果加上ICMP报头(8字节),那就是64字节。
图1-30
我们可以再看一下回显应答,ICMP报文的数据部分长度依然是56字节,如图1-31所示。
图1-31