探索协议栈和网卡——IP包与以太网的包收发操作

1、包的基本知识:

包是由头部和数据两部分构成的。头部包含目的地址等控制信息;后面就是委托要发送给对方的数据。

首先,发送方的网络设备会负责创建包。接下来,包会被发往最近的网络转发设备。当到达最近的转发设备之后,转发设备会根据头部中的信息判断接下来应该发往哪里。这个过程需要用到一张表,这张表里记录了每个地址对应的发送方向,也就是按照头部里记录的目标地址在表里进行查询,并根据查到的信息判断接下来应该发往哪个方向。接下来,包在向目的地移动的过程中,又会到达下一个转发设备,然后又会按照同样的方式被发往下一个转发设备。

在之前的文章中我们讲过子网的概念,还讲过网络中有路由器和集线器两种不同的转发设备,它们在传输网络包时有着各自的分工。

(1)路由器根据目标地址判断下一个路由器的位置。

(2)集线器在子网中将网络包传输到下一个路由。

集线器是按照以太网规则传输包的设备,而路由器是按照IP规则传输包的设备,因此我们也可以作如下理解。

(1)IP协议根据目标地址判断下一个IP转发设备的位置。

(2)子网中的以太网协议将包传输到下一个转发设备。

TCP/IP包包含两个头部,分别是MAC头部(用于以太网协议)和IP头部(用于IP协议)。

这两个头部分别具有不同的作用。首先,发送方将包的目的地,也就是要访问的服务器的IP地址写入IP头部中。这样一来,我们就知道这个包应该发往哪里,IP协议就是可以根据这一地址查找包的传输方向,从而找到下一个路由器的位置。接下来,IP协议委托以太网协议将包传输过去。这时,IP协议会查找下一个路由器的以太网地址(MAC地址),并将这个地址写入MAC头部中。这样一来,以太网协议就知道要将这个包发到哪个路由器上了。

当网络包经过集线器时,集线器是根据以太网协议工作的设备。为了判断包接下来应该向什么地方传输,集线器里有一张表(用于以太网协议的表),可根据以太网头部中记录的目的地信息查出相应的传输方向。

接下来,包会到达下一个路由器。路由器中有一张IP协议的表,可根据这张表以及IP头部中记录的目的地信息查出接下来应该发往哪个路由器。为了将包发到下一个路由器,我们还需要查出下一个路由器的MAC地址,并记录到MAC头部中。这样,网络包就又被发往下一个节点了。

IP和以太网的分工看似有点复杂,其实这样设计是由原因的。其中以太网的部分也可以替换为其他的东西,例如无线局域网、ADSL、FTTH等。

2、包收发操作概览:

包收发操作的起点是TCP模块委托IP模块发送包的操作。这个委托的过程就是TCP模块在数据块的前面加上TCP头部,然后整个传输给IP模块,这部分就是网络包的内容。与此同时,TCP模块还需要指定通信对象的IP地址。

IP模块会添加IP头部和MAC头部这两种头部,加上这两个头部之后,一个包就封装好了,这些就是IP模块负责的工作。

接下来,封装好的包会被交给网络硬件(统称网卡),例如以太网、无线局域网等。传递给网卡的网络包是由一连串0和1组成的数字信息,网卡会将这些数字信息转换为电信号或光信号,并通过网线(或光纤)发送出去。接收包的过程于此相反,信息先以电信号的形式从网线传输进来,然后由网卡将其转换为数字信息并传递给IP模块。接下来,IP模块会将MAC头部和IP头部后面的内容传递给TCP模块。

TCP模块在收发数据时分为好几个阶段,并且每个阶段都设计了实现相应功能的网络包,但IP的包接收操作都是相同的,IP模块会将TCP头部和数据看作一整块二进制数据,在执行操作时并不关心其中的内容。总之,IP的指责就是将委托的东西打包送到对方手里,或者是将对方送来的包接收下来,仅此而已。

3、生成包含接收方IP地址的IP头部:

IP头部包含的内容如下表所示(IP头部为20字节):

字段名称

长度(含义)

含义
版本好 4 IP协议版本号,目前使用的是版本4
头部长度(IHL) 4 IP头部长度。可选字段可导致头部长度发生变化,因此这里需要指定头部长度
服务类型(ToS) 8 表示包传输优先级。最初的协议规格里对这个参数的规格很模糊,最近DiffServ规格重新定义了这个字段的用法
总长度 16 表示IP消息的总长度
ID号 16 用于识别包的编号,一般为包的序列号。如果一个包被IP分片,则所有分片都拥有相同的ID号
标志(Flag) 3 该字段有3个比特,其中2个比特有效,分别代表是否允许分片,以及当前包是否为分片包
分片偏移量 13 表示当前包的内容为整个IP消息的第几个字节开始的内容
生存时间(TTL) 8 表示包的生存时间,这是为了避免网络出现回环时一个包永远在网络中打转。每经过一个路由器,这个值就会减1,减到0时这个包就会被丢弃
协议号 8

协议号表示协议的类型(以下均为十六进制)。

TCP:06

UDP:17

ICMP:01

头部校验和 16 用于检查错误,现在已不使用
发送方IP地址 32 网络包发送方的IP地址
接收方IP地址 32 网络包接收方的IP地址
可选字段 可变长度 除了上面的头部字段之外,还可以添加可选字段用于记录其他控制信息,但可选字段很少使用

其中最重要的内容就是IP地址,它表示这个包应该发到哪里去。这个地址是由TCP模块告知到,TCP模块是在执行连接操作时从应用程序那里获得这个地址的。这里需要关注的一点就是IP地址不是相对于计算机的,而是与网卡相关。当一个计算机有多块网卡时,每块网卡都会有自己的IP地址,也就是一个计算机存在多个IP地址。当判断应该使用哪块网卡发送IP包时,也就相当于判断应该把包发往哪个路由器。

在判断应该把包交给哪个网卡时,就类似于之前路由器使用IP表判断下一个路由器位置的操作是一样的。因为协议栈的IP模块与路由器中负责包收发的部分都是根据IP协议规则来进行包收发操作的,所以它们也都有相同的方法来判断把包发给谁。

前面所讲到的“IP表”也叫做路由表。我们可以通过route print命令来显示路由表,如下:

路由表中的Network Destination列表示的是目标IP地址,例如TCP模块告知的目标IP地址为192.168.124.21,那么就对应192.168.124.0这一行,因为它与192.168.124的部分相匹配。路由表中的Interface列表示的是网卡等网络接口,这些网络接口可以将包发送给通信对象。路由表中的Gateway(网关,在TCP/IP中就是路由器的意思)列表示下一个路由器的IP地址,将包发给这个IP地址,该地址对应的路由器就会将包转发给目标地址(如果Interface列与Gateway列中的IP地址相同,就表示不需要路由器进行转发,直接将包发给接收方的IP地址)。路由表第一行中的目标地址和子网掩码都是0.0.0.0,这表示默认网关,也就当其他所有目标地址都无法匹配时,就自动匹配这一行。

在确定完哪个网卡发送包时就可以将该网卡的IP地址填入IP头部中的发送方IP地址。然后需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是TCP模块委托的内容,则设置为06(十六进制)。其他字段也需要填写相应的值,但对大局没什么影响。

4、生成以太网用的MAC头部:

在生成IP头部之后,还需要在IP头部前面加上MAC头部。在以太网的世界中,TCP/IP这个思路是行不通的。MAC头部包含了接收方和发送方的MAC地址等信息,如下(MAC头部为14字节):

字段名称 长度(比特) 含义
接收方MAC地址 48 网络包接收方的MAC地址,在局域网中使用这一地址来传输网络包
发送方MAC地址 48 网络包发送方的MAC地址,接收方通过它来判断是谁发送了这个包
以太类型 16

使用的协议类型。下面是一些常见的类型,一般在TCP/IP通信中使用0800和0806这两种。

0000-05DC:IEEE802.3

0800:IP协议

0806:ARP协议

86DD:IPv6

MAC地址是网卡生产时写入ROM里的,只要将这个值读取出来写入MAC头部就可以了(读取MAC地址一般是在操作系统启动的时候就读取到内存了,读取的操作由网卡驱动程序来完成,当然驱动程序也可以不从ROM中读取MAC地址,而是使用配置文件中设定的MAC地址,又或者通过命令输入MAC地址等等)。在多块网卡的情况下,选择IP地址的同时也就选择了对应的MAC地址。

发送方MAC地址还比较简单,而接收方MAC地址就有点复杂了。这时候,我们就需要执行根据IP地址查询MAC地址的操作。

5、通过ARP查询目标路由器的MAC地址:

在以太网中,有一种叫做广播的方法,可以把包发给连接在同一以太网中的所有设备。ARP(Address Resolution Protocol,地址解析协议)就是利用广播对所有设备提问,获取对应IP地址的MAC地址。如果对方与自己处于同一子网中,那么通过这种操作就可以得到对方的MAC地址。

当然了,如果每次发送包都要这样查询一次,网络中就会有很多ARP包,所以我们会将查询结果放到一块叫做ARP缓存的内存空间中留着以后用。也就是说,在发送包时,先查询ARP缓存,如果已经存在对方的MAC地址则不需要发送ARP查询,否则则需要发送ARP查询。

查询ARP缓存的方法如下(左侧为IP地址,右侧为IP地址对应的MAC地址;arp -d 10.10.1.43表示删除ARP缓存中的对应条目):

ARP缓存也并不总是一层不变的,通常ARP缓存中的值在经过一段时间后会被删除,一般这个时间为几分钟左右。

6、以太网的基本知识:

以太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术,它的原型如下图所示(10BASE5 以太网原型,信号通过网线流过整个网络,这过程中涉及到信号碰撞的问题暂且忽略):

从图中不难看出,这种网络的本质其实就是一根网线。其中,收发器可以将不同网线之间的信号连接起来。在这种网络中任何一台设备发送的信号所有设备都能接收到。为了判断一个信号到底是发给谁的,我们就需要使用MAC头部。然后通过以太类型就能够知道包里面装了什么类型的内容。

后来以太网原型变成了如下结构(采用中继式集线器的变体 10BASE-T,信号通过中继式集线器扩散到整个网络):

可以发现这个结构是将主干网线替换成了一个中继式集线器,将收发网线替换成了双绞线。

再后来又将中继式集线器替换成了交换式集线器(也就是我们通常叫的交换机),现在我们说的以太网指的就是这样的结构。交换式集线器与中继式集线器的最大区别在于,信号只会流到根据MAC地址指定的设备,而不再是发送给所有设备。

以太网的这些性质也是用于无线局域网。无线局域网没有以太类型,但有另一个具备同样功能的参数,可以认为它就是以太类型。

7、将IP包转换成电或光信号发送出去:

IP生成的网络包只是存放在内存中的一串数字信息,没有办法直接发送给对方。因此,需要将数字信息转换为电或光信号才能在网线上传输。从某种意义上讲,这才是真正的数据发送过程。

负责执行这一操作的是网卡,但网卡也无法单独工作,要控制网卡还需要网卡的驱动。所以网卡的内部结构可以看作是下面这张图:

在使用网卡之前,和其他硬件一样需要进行初始化。在打开操作系统的时候,网卡驱动程序会对硬件进行初始化操作,然后硬件才进入可以使用的状态(其中就包括设置MAC地址)。

网卡的ROM中保存着全世界唯一的MAC(Media Access Control)地址,在生产网卡的时候写入的。当然也有一些特殊的情况,例如从命令或者配置文件中读取MAC地址等等。

8、给网络包再加3个控制数据:

接下来看一看网卡是如何将包转换成电信号并发送到网线中的。网卡驱动从IP模块获取包之后,会将其复制到网卡内到缓存区中,然后向MAC模块发送发送包的命令。

MAC模块将包从缓存区中取出,并在开头加上报头和起始帧分界符(SFD),在末尾加上用于检测错误的FCS(帧校验序列)。

报头是一串像10101010...这样1和0交替出现的比特序列,长度为56比特,它的作用是确定包的读取时机。因为网卡将这些比特序列转换为电信号后会形成如下的波纹:

正常情况下,在判断一串数据信号的规律时候还需要一组用来区分比特间隔的时钟信号,来告诉网卡什么时候读取电压和电流的值。但是当距离较远,网线较长时,就会发生时钟偏移的问题。以太网为了解决这个问题将这两组信号结合在了一起,然后根据时钟信号就能够推算出数据信号(这里的信号是10BASE-T规格,通过测量这一波形的时间,可以判断出每个比特信号的中间位置),这就是报头的作用。

报头后面的起始帧分界符在末尾有少许变化。接收方以这一变化作为标记开始提取网络包数据。

末尾的FCS用来检查包传输过程中因噪音导致的波形紊乱、数据错误,它是一串32比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的(一般使用的是CRC错误校验码——Cyclic Redundancy Check 循环冗余校验)。

9、向集线器发送网络包:

发送信号的操作主要分为两种,一种是使用集线器的半双工模式(某一时刻只能进行发送或接收其中一种操作),另一种是使用交换机的全双工模式(发送和接收同时并行的方式)。

在半双工模式中,为了避免信号碰撞,首先要判断网线中是否存在其他设备发送的信号。首先,MAC模块将数字信息按照每个比特转换为电信号,然后再由PHY(Physical Layer Device 物理层装置,速率在100Mbit/s以上的以太网)或者叫MAU(Medium Attachment Unit 介质连接单元)的信号收发模块发送出去。我们常说的网络传输速率就是这里数字信息转换为电信号的速率。

以太网规格中对不同的网线类型和速率以及其对应的信号格式进行了规定,MAC模块将能够转换为任意格式的通用信号发给PHY(MAU)模块,然后PHY(MAU)模块再将其转换为可在网线上传输的格式,类似于对MAC模块产生的信号进行格式转换。当然PHY(MAU)还需要监控接收线路中有没有信号进来。

根据以太网的规格,两台设备之间的网线不能超过100米(这是对于双绞线而言的,对于光纤可以更长),因此发生错误的几率非常低,所以以太网是不会确认对方是否收到发送的信号。在使用集线器的半双工模式中,假如发生了信号碰撞,为了通知其他设备则会发送一段时间的阻塞信号,然后所有发送操作都会停止。等待一段时间之后,网络中的设备又会重新尝试发送信号,这里需要注意的是等待时间,一般等待时间是根据MAC地址生成一个随机数计算出来的,所以几乎不会发生等待时间一样的情况。如果重新发送的时候又与其他设备发送操作冲突,这时会将等待时间延长一倍,以此类推,最多重试10次然后会报告通信错误。

10、接收返回包:

在使用集线器的半双工模式以太网中,一台设备发送的信号会到达连接在集线器上的所有设备。也就意味着无论是不是发给自己的信号都会通过接收线路传进来。

信号的开头是报头,通过报头的波纹同步时钟,然后遇到起始帧分界符时开始将后面的信号转换为数字信息。即先是PHY(MAU)模块将信号转换为通用格式并发送给MAC模块,然后MAC模块再从头开始将信号转换为数字信息,并存放到缓冲区中,最后到达信息的末尾时还需检查FCS。如果FCS校验没有问题,接下来就要看MAC头部中的接收方MAC地址与网卡在初始化时分配给自己的MAC地址是否一致,用于判断这个包是不是发送给自己的。如果是自己的,MAC模块的工作就完成了,接下来网卡会通知计算机收到一个包。

通知计算机的操作会使用一个叫做中断的机制。首先网卡向扩展总线中的中断信号线发送信号,该信号通过计算机中的中断控制器连接到CPU。当产生中断信号时,CPU会暂时挂起正在处理的任务,切换到操作系统中的中断处理程序。然后中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。

中断是有编号的,网卡在安装的时候就在硬件中设置了中断号,在中断处理程序中则将硬件的中断号和相应的驱动程序绑定。现在的硬件设备都遵循即插即用(PnP Plug and Play,是一种自动对扩展卡和周边设备进行配置的功能)的规范自动设置中断号,我们没有必要去关心中断号了。

网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包,并通过MAC头部中的以太类型字段判断协议的类型。如 0800(十六进制)代表IP协议;809B则表示ApplyTalk协议。假如操作系统内部不存在相应的协议栈,则会视为错误,直接丢弃这个包。

11、将服务器的响应包从IP传递给TCP:

假设从服务器返回的包的以太类型是0800,因此网卡驱动会将其交给TCP/IP协议栈来进行处理。首先是IP模块开始工作,第一步是检查IP头部,确认格式是否正确,然后下一步是查看接收方IP地址是否正确。当发现接收方IP地址不是自己时,IP模块会通过ICMP消息将错误告知发送方。ICMP规定了各种类型的消息,如下表:

消息 类型 含义
Echo reply 0 响应 Echo 消息
Destination unreachable 3 出于某些原因包没有达到目的地而是被丢弃,则通过此消息通知发送方。可能的原因包括目标IP地址在路由表中不存在;目标端口号不存在对应的套接字;需要分片,但分片被禁用
Source quench 4 当发送的包数量超过路由器的转发功能力时,超过的部分会被丢弃,这时会通过这一消息通知发送方。但是,并不是说遇到这种情况一定会发送这一消息。当路由器的性能不足时,可能连这条消息都不发送,就直接把多余的包丢弃了。当发送方收到这条消息时,必须降低发送频率
Redirect 5 当查询路由表后判断该包的入口和出口为同一个网络接口时,则表示这个包不需要该路由器转发,可以由发送方直接发送给下一个路由器。遇到这种情况时,路由器会发送这条消息,给出下一个路由器的IP地址,指示发送方直接发送过去
Echo 8 ping 命令发送的消息。收到这条消息的设备需要返回一个 Echo reply 消息,以便确认通信对象是否存在
Time exceeded 11 由于超过了IP头部中的TTL字段表示的存活时间而被路由器丢弃,此时路由器会向发送方发送这条消息
Parameter problem 12 由于IP头部字段存在错误而被丢弃,此时会向发送方发送这条消息

所以像前面的情况就会将表中的Destination unreachable消息通知对方。

如果接收方IP地址正确,则这个包会被接收下来,这时还需要完成另一项工作,如果接收到的包是经过分片的,那么IP模块会将它们还原成原始的包。分片的包会在IP头部的标志字段中进行标记,当收到分片的包时,IP模块会将其暂存在内存空间中,然后等待IP头部中具有相同ID的包全部到达,这是因为同一个包的所有分片都具有相同的ID,此外,IP头部还有一个分片偏移量(fragment offset)字段用于表示当前分片在整个包中所处的位置。在所有分片全部收到之后,就可以将它们还原成原始的包,这个操作叫做分片重组。

接下来IP模块会将包交给TCP模块,TCP模块会根据IP头部中的接收方和发送方IP地址,以及TCP头部中的接收方和发送方端口号来查找对应的套接字。找到对应的套接字之后,就可以根据套接字中记录的通信状态,执行相应的操作。

猜你喜欢

转载自blog.csdn.net/qq_38386085/article/details/93654962