上一节我们探索了网络消息在协议栈的TCP模块内是如何封装、发送和接收:浏览器输入一个网址发生了什么(二) TCP模块封装和传输机制
本节将会补充协议栈中的IP模块对报文的封装,以及网络包是如何通过网卡发送出去计算机。
1. IP模块封装IP头部
当数据报文从TCP模块传递给IP模块时,IP模块需要封装IP头部。IP头部中包含以下信息,其中接收方的IP由TCP模块传递给IP模块,而发送方的IP是什么需要取决于IP模块通过电脑的哪一块网卡发送(由上一节我们知道IP不是与主机绑定,而是与主机上的网卡绑定,如果主机上有多个网卡,则该主机可以有多个IP)。
为了得知包由主机上的哪块网卡发送,IP模块会根据目标IP为条件查询本机的“路由表”,该路由表在windows系统中可以通过 route print命令显示,如下:
首先IP模块会拿目标ip和第1列字段进行比对,这里的比对不是全值匹配,而是只匹配其网络号。而网络号的位数由第2列字段决定。
例如,当目标IP是192.168.1.21时会匹配到倒数第3行,因为这一行要求比对的网络号是前24位,而192.168.1.21和192.168.1.0的前24位相同,都是192.168.1。同理,如果目标IP是10.10.1.166则匹配到第3行。
如果目标IP与路由表中的所有条目都不匹配,则会匹配到第1行,第一行的网络号全为0,即默认网关(网关本质也是一个转发路由器),也意味着这个包将被发送到局域网中默认的一个路由器,由该路由器进行转发。
第4列Interface代表网卡的网络接口,也就是本机网卡的IP。第3列Gateway代表包要被发送到的下一个路由器的IP。
如果目标IP是192.168.1.21,则会匹配到倒数第3行,此时IP模块会用第4列的10.10.1.16代表的网卡发送包到第3列的10.10.1.2这个路由器。
从上面的路由表来看,该主机只有10.10.1.16这1个IP,1块网卡。
第5列表示从发送方IP到目标IP需要经历多少跳,即包传输的距离。
回到正题,除了发送方IP和目标IP,IP头部还需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是TCP模块委托的内容,则设置为06(十六进制),如果是UDP模块委托的内容,则设置为 17(十六进制),这些值都是按照规则来设置的。
在现在我们使用的浏览器中, HTTP请求消息都是通过TCP来传输的,因此这里就会填写表示TCP的06(十六进制)。其他字段内也需要填写相应的值,但对大局没什么影响,这里先省略。
2. IP模块封装以太网用的MAC头部
IP头部中的IP地址可以由网络层的IP模块进行识别(当包到达接收方的IP模块后,在IP模块中会检查包头部的目标IP地址是否为接收方的IP地址,如果不是则丢弃包;除了IP地址外,IP模块还会识别IP头部中的其他信息如协议号等)。
但是在以太网(以太网是局域网中的一种)中,有很多设备只有数据链路层而没有网络层,没有IP模块,因此也无法识别IP头部,例如集线器等。此时需要为包添加以太网能识别的MAC头部,如下所示:
MAC头部实际上也是IP模块生成的,它包含了接收方和发送方的MAC地址,此时包从IP包变成以太网包。以太网包的内容不一定是IP包,也可以是ARP包等,可以从MAC头部的以太网类型字段得知。
MAC地址也是网卡的硬件地址。IP和MAC都是网卡的地址,但IP地址是可以动态变化的,这取决于主机所处的网络环境,而MAC地址是固定的,当网卡生产时就写入到ROM中,在上一步中根据目标IP查询路由表得知该由哪块网卡发送包后,再从这块网卡中读取MAC地址并写入MAC头部即可。
下面是MAC地址的格式:
得知发送方的MAC地址容易,那如何得知接收方的MAC地址呢?这里需要使用ARP协议和广播。
ARP协议(地址解析协议)是一个实现从IP地址到MAC地址的映射,即询问目标IP其 对应的MAC地址的协议。ARP协议仅用于IPv4,IPv6则是使用NDP(邻居发现协议)。
ARP协议与IP协议处于同一层,也是网络层协议,因此ARP报文是在网络层被封装的网络包。
ARP的工作机制:
- 首先IP模块会封装ARP请求包,ARP请求包中包括发送方的IP地址和MAC地址,以及目标主机的IP地址(这段话中的目标主机的IP地址其实是下一跳路由器的IP地址而非最终互联网另一端的目标IP)和MAC地址(由于不知道目标主机的MAC地址,因此会填写为全0)。
- 客户端计算机通过交换机向局域网内同一链路的所有机器发送ARP请求(即广播),该ARP请求包在数据链路层会被同一链路上所有的主机、路由器和交换机接收和解析。
- 交换机接收到这个ARP请求包(应该说是包含该ARP包的帧)会将其广播到同一链路的其他机器(因为交换机只负责数据的转发,它没有网络层且它没有被分配IP);
- 之后这个包被路由器或主机接收到,则他们会检测ARP包头部的目标IP地址,如果不是自己的IP地址则会丢弃该包。如果是则会将他们自己的MAC地址写入到ARP响应包返回给发送方主机。并且目标主机会将发送方主机的IP地址和MAC地址的映射关系缓存到ARP表中。
ARP表是每个主机内存储着其他机器的IP和MAC地址的映射关系表。通过查询ARP缓存就不用每次查询MAC地址时都去进行ARP请求广播,从而防止ARP大量广播。但是ARP表中的每条缓存都有自己的有效期,过期则删除,有效期在几分钟到十几分钟。可以通过 arp -a命令查看ARP表内容。
回到正题,将目标主机的MAC地址写入到MAC头部之后,MAC包就封装好了,接下来IP模块会将MAC包传递给网卡,网卡对MAC包进一步封装为帧(无论是IP包还是ARP包,都会在网卡被封装为帧之后才发送出去)。
需要注意的是,此时网络包MAC头部的接收方MAC地址是下一跳路由器的MAC地址而非互联网另一端目标主机的MAC地址。当然如果这个包的目标主机本身就是局域网中的一台机器,那么发送ARP请求时的ARP头部的目标IP就可能是目标主机的IP而非路由器IP,那么请求的MAC地址就直接是这个目标主机的MAC地址。
3. 网络包封装成帧
在介绍数据包封装成帧并发送之前先介绍一下网卡和数据在网卡传输的路径。
网卡和网卡驱动程序
IP模块生成的网络包是存在内存中的一堆数字信息,需要被转为电或光信号才能在网线传输,而网卡负责这一任务。网卡本身是硬件,无法完成这些任务,还需要网卡驱动程序对网卡进行操作和控制。
网卡在电脑开机时会进行初始化,初始化时网卡驱动程序会读取ROM中的MAC地址并分配给网卡中的MAC模块,此时网卡的MAC地址才生效。
下图是网卡的结构以及网络包是如何在网卡中处理和发送的:
其中,MAC模块负责将IP包封装为帧,并检测接收到其他机器发送过来的帧是否正确(FCS校验)以及接收到的包中的目标MAC地址是否是自己的MAC地址;PHY(MUA)负责将帧从数字信息转为能在网线传输的电信号。
IP模块将以太网包传递给网卡驱动,网卡驱动会将其复制到网卡内的缓冲区,再向MAC模块发送发送包的命令。MAC模块将包从缓冲区取出在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列,此时网络包被封装成帧。
报头是一串像10101010…这样1和0交替出现的比特序列,长度为56比特,它的作用是确定包的读取时机。起始帧分界符是一个用来表示包起始位置的标记。末尾的FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误。
4. 向集线器发送网络包
包封装成帧后,MAC模块从报头开始将数字信息按每个比特转换成电信号,然后由PHY,或者叫MAU的信号收发模块发送出去。在网线中实际的输出信号如下
发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式。
在使用集线器的半双工模式中,发送和接收不能同时进行,PHY(MAU)模块会保证网线中没有其他设备发送信号给自己才会将自己的信号发送出去(如果有则等待其他信号传输完)。如果网线中有其他信号在传输的情况下发送出去自己的信号,则两组信号会发生碰撞。在全双工模式中,发送和接收能够同时进行。
电信号由PHY(MUA)信号收发模块发送给自己连接的集线器,再由集线器转发给连接到该集线器上的所有设备。数据在网络中(局域网中)传递的过程如下:
信号经集线器发给所有设备后,这些设备都会接收到这个信号,然后由他们的PHY(MUA)模块从电信号转为通用格式给MAC模块。
MAC模块先将信号转为数字信息(还原为数据帧),然后检测数据帧是否受到噪声干扰而紊乱,即把包开头到结尾的比特套用公式计算出的内容与帧末尾的FCS比对,如果不一致则会被当做错误包丢弃。
如果FCS校验没问题,MAC模块会检测包MAC头部的接收方MAC地址是否是自己的MAC地址,不是则直接丢弃,是则存到网卡缓冲区中。
之后网卡会发出中断信号给CPU。换句话说,计算机不会一直监控网卡的活动而是在运行其他的任务,因此操作系统是无法得知包到达网卡这件事。
因此包到达后需要由网卡向扩展总线中的中断信号线发送信号,该信号线通过中断控制器连接到CPU,CPU接收到中断信号后会挂起正在处理的任务,切换到操作系统的中断处理程序,中断处理程序会调用网卡驱动,网卡驱动从网卡缓冲区取出包交给TCP/IP协议栈。
协议栈中,IP模块会校验IP头部格式是否正确,以及包的头部接收方IP地址是否是自己的地址,如果不是自己的地址,IP模块会通过ICMP消息将错误告知发送方,但不会将这个包转发给真正的目标IP主机。
接下来需要再说一下 ICMP 互联网控制协议。
该协议用于在主机和路由器之间发送控制消息,这里的控制消息是指“网络不通”,“主机是否可到达”等消息,这些消息不传输用户数据。ICMP位于网络层,但是在IP之上,ICMP报文和TCP与UDP一样会被添加IP头部封装为包。ICMP是无连接的协议。
ICMP的主要功能有两个:
- 确认IP包是否成功到达目标地址,当IP包从源发送方到目标主机之间的任何一个设备(如路由器)出错(如包无法到达下一跳的设备),则中间的设备会生成ICMP数据包发送给源发送方。
- 网络诊断,ICMP可以检测两台设备之间能否互连,即主机A能否到达主机B,以及主机A与主机B的连接速度,准确报告ICMP包到达目标地的时间。ping和traceroute命令就是通过发送ICMP包实现的功能。其中traceroute可以显示两台机器之间可能的路径并测量数据包在IP网络上的时延,ping则是traceroute的简化版,功能与traceroute类似。
IP包不可到达目标主机情况下发送ICMP包的流程:
图中,主机A发送了一个IP数据包,其目标地址是主机B。IP包经过路由器1到达路由器2,假设路由器2和主机B在同一链路,为了根据主机B的IP获取B的MAC地址,路由器2在局域网内广播了ARP包。由于主机B关机导致该ARP包没有被响应,多次重发ARP无果后,路由器2会生成一个类型为“目标不可到达类型”的ICMP包经过路由器1发送主机A。
“目标不可到达”是其中的一种ICMP消息类型。
主要的ICMP的消息类型如下:
通知类型(十进制数) | 具体内容 |
0 | 回送应答(Echo Reply) |
3 | 目标不可达(Destination Unreachable) |
4 | 原点抑制(Source Quench) |
5 | 重定向或改变路由(Redirect) |
8 | 回送请求(Echo Request) |
9 | 路由器公告(Router Advertisement) |
10 | 路由器请求(Router Solicitation) |
11 | ICMP 超时(Time Exceeded) |
17 | 地址子网请求(Address Mask Request) |
18 | 地址子网应答(Address Mask Reply) |
ICMP通知类型分为两类:对于发送错误消息的ICMP报文叫差错报文;对于为了采集信息和配置的ICMP报文叫信息类报文(如ping和traceroute命令所发的ICMP报文)。
ping命令检测两端主机能否互连情况下发送ICMP包的流程:
图中主机A执行ping命令检测能否与主机B通信,此时主机A会生成一个通知类型为“8 回送请求”的ICMP包给B。ICMP包经过多个路由器如果能够到达主机B则B接收到“回送请求”的ICMP包后会返回一个“0 回送应答”的ICMP包给A,这个包会沿着原路经过路由器4~1到达主机A,主机A就知道能够与B通信,以及包送达B花费的时间。
回到正题,当接收方主机发现数据包头部的接收方IP地址不是自己的IP地址,则会由IP模块生成一个类型为 “目标不可到达类型”的ICMP包给源发送方。
如果IP正确,则这个包会被接收。如果这个包是经过分片的,则IP模块会将其暂存到内存中,等IP头部具有相同ID号的包全部到达,会根据ID号以及IP头部的“分片偏移量”将所有分片重组完整的IP包。(这里的分片不是指TCP模块对应用程序数据[即http消息]的拆分,而是指路由器对某个以太网包内部IP包数据[即TCP头部+http消息]的拆分,这部分知识会在之后介绍路由器时介绍)。
之后,这个完整IP包的数据会被递交给TCP模块,TCP模块根据IP头部的接收方IP地址和TCP头部的接收方端口号查找对应的套接字,根据套接字记录的通信状态执行相应操作:如果包的数据是应用程序数据则返回确认接收的ACK包,并将数据存放到缓冲区等待应用程序来读取;如果是建立或断开连接的控制包,则返回响应控制包,并告知应用程序建立和断开连接的操作状态。
说到这里,我们可以说已经大概了解了发送方数据从本机的应用层到本机网络层的传输和处理,以及接收方接收数据时数据从接收方机器的网络层到应用层的传输与处理过程了。
下节预告:浏览器输入一个网址发生了什么(四) 网络包在局域网中的传输