前言
在上一章节中,笔者就IP层的接收代码逻辑做了简单介绍,并对实现代码进行了逻辑梳理以及仿真测试,并且在上一章节中,就IP层的分片包问题,如何确定分片包是否存在已经进行了简单介绍,并在接收模块中,将分片信息进行了输出,因为在设计中,只会对IP报文中包含的数据包中UDP报文和ICMP报文,并且ICMP的报文大小不会产生分包情况,所以可知在判断出当前的IP报文中数据包为UDP数据包后,将UDP数据包输入至IP分片处理模块,进行相应处理
IP分片信息详细介绍
- 标志字段
标志字段是IP头部的一部分,用于控制分片的行为。标志字段由3个位组成:
- 片偏移字段
片偏移字段指示当前分片相对于原始数据包的起始位置。单位是8字节(即64位)。例如,片偏移值为1时,意味着当前分片从原始数据包的第8字节开始。该字段占13位,因此片偏移最大值为 2^13 - 1 = 8191,即偏移量最大可以达到 8191 * 8 = 65528 字节。 - 计算分片的起始位置
对于每个分片,通过标志字段和片偏移字段来计算该分片在原始数据包中的位置和大小。每个分片的大小是根据MTU和IP头部的大小来决定的。假设MTU为1500字节(通常是Ethernet的最大传输单元,不包含MAC字段,即IP报文的最大字段),则每个分片的最大数据部分大小为:
每个分片的数据部分最大大小=MTU−IP头部大小=1500−20=1480字节。在实际分片过程中,IP层会根据每个分片的数据部分长度(通常是8的倍数)来计算片偏移和标志。每个分片除了最后一个,都会设置MF(More Fragments)标志。 - 示例计算
假设我们有一个原始的IP数据包,其总大小为4000字节(包括IP头部),MTU为1500字节。IP头部大小为20字节。因此,每个分片的最大有效载荷为1480字节(MTU - IP头部大小)
第一个分片:
数据部分大小:1480字节
片偏移:0
MF标志:1(表示还有更多分片)
第二个分片:
数据部分大小:1480字节
片偏移:1480 / 8 = 185
MF标志:1(表示还有更多分片)
第三个分片:
数据部分大小:1480字节
片偏移:2960 / 8 = 370
MF标志:1(表示还有更多分片)
第四个分片:
数据部分大小:60字节
片偏移:4440 / 8 = 555
MF标志:0(表示这是最后一个分片) - 注意事项
笔者以IP报文中包含UDP数据包为例,在上面的四个分片中,可以计算出,IP报文去除IP头部的大小后,总共包含3920字节,注意,这3920字节为1个UDP数据包,所以UDP首部只在第一个分片存在,在其他分片不存在,故在存在巨型帧传输的情况下,必需对分片包是否存在做检测,否则会因为数据包缺失UDP首部,而对数据包错误的丢弃或是获取。
代码逻辑梳理
-
MF位起始检测
ip报文分片处理模块,其输入接口包含UDP报文包,以及MF标志,分片大小等信息,在检测UDP报文包到来时,在有效信号的高电平指示下,检测MF标志是否为高,当MF标志位为高时,将当前的数据包存入RAM中,第一次检测电平为高时,写入RAM的起始地址为0,假设第二个以及之后的分片包到来,在上次的RAM地址中进行自加即可。而若MF标志位为低,且在这之前的一个包中,MF标志也为低,则表示这是一个单帧数据包,直接将UDP报文输出即可。只需做相应的延迟处理。 -
MF位结束检测
在检测到MF为高时,需要设置一个变量,表示要进行分片包的接收,设定这个变量为r_fragment_start,注意MF变量在整个数据包有效周期内,都保持高电平或是低电平。当检测到
r_fragment_start为高的情况下,且本包数据包MF标志位为低电平。且在本包数据包最后一个字节接收状态下,再进行r_fragment_start的拉低。 -
分片包的RAM写入以及读出
因为分片包必然是不连续的,所以写入RAM的条件应该是r_fragment_start为高电平,且报文valid信号为高时,进行RAM的写入,并且在每次数据包到来时,在有效信号的上升沿都要进行报文长度的累加,报文总长的清零设置在输出UDP报文的last电平为高时,保证分片数据包总长度的正确。何时进行RAM的读出,当检测到r_fragment_start的下降沿,表示分片包写入完成,之后便可以进行读取,读取的地址从0开始,当读取的RAM地址到达数据包总长时,表示读取完毕,这里的RAM读取还有一个小坑,在仿真测试时,进行说明
仿真测试
- 仿真测试1:直接输入给分片处理模块,三帧数据包,长度都为40字节,前两帧分片标志位为001,第三帧为010。仿真测试,模块能否对三帧数据包进行整合然后输出,写入数据为自增数据。
写入共120字节,三次写入,一次读取,数据为0~119,观察输出数据的最后一个字节,经验证,仿真测试通过
- 仿真测试2,数据包输入给IP层接收模块,经IP层处理后,传输给分片处理模块,两帧数据包,每帧长度为1500字节,包含IP首部。第一帧分片标志位001,第二帧分片标志为000,数据数据为8为自增数。
写入共3960字节,两次写入,一次读取,观察输出数据的最后一个字节,且输出的UDP报文长度也为3960字节,经验证,仿真测试通过。
BUG调试
对于该模块的BUG调试,大多需要通过仿真来进行测试,主要是调整输出有效信号的使能位置,以及需要注意RAM的初始写入因从0开始,而非1,读取同样如此,注意时序逻辑的性质。
其中一个关键问题是,本次RAM的输出端口,存在数据寄存器,其主要是为了保证时序的稳定,这也导致了RAM的输出延迟为2,在进行仿真测试时,注意到一个问题,
如果BRAM的读取使能在107地址之后立刻关闭,则会导致数据106输出持续两个时钟周期,再输出107,给输出UDP报文的完整性造成影响,经过调试,发现,只要读取使能到108地址后,再进行使能关闭,106和107数据就会正常输出,注意0~107地址,存储108个数据,即0-107,图中的是传输了3次长度为36字节的数据包,所以最后一个数据为107.
调整代码逻辑后,仿真如下