3. 数据包处理基础:以太网帧结构、IP头、TCP/UDP头解析,校验和计算

做硬件加速,说白了就是跟数据包打交道。你得先搞清楚数据包长什么样,才能在硬件里精准地把它拆开、分析、再组装回去。我刚开始接触硬件包处理时,总觉得软件里看包挺简单的,但一转到硬件视角,很多细节就变得特别关键。

这一节,咱们就扎进数据包的底层。我会结合自己踩过的坑,把以太网帧、IP头、TCP/UDP头这些结构掰开揉碎了讲。嗯,校验和计算这块尤其重要,硬件里算不对,整个包就废了。

3.1 以太网帧结构:最底层的“信封”

数据包在网络上传输,第一层包装就是以太网帧。你可以把它想象成一个信封,上面写着发件人和收件人的地址。

标准以太网帧(DIX v2)的结构是这样的:

字段 长度(字节) 说明
前导码(Preamble) 7 同步时钟用,硬件接收时直接丢弃
帧起始定界符(SFD) 1 标志帧的开始,固定值 0xD5
目的MAC地址 6 接收方网卡地址
源MAC地址 6 发送方网卡地址
类型/长度(EtherType) 2 0x0800 表示IPv4,0x0806 表示ARP
载荷(Payload) 46-1500 上层数据,最小46字节,最大1500字节
帧校验序列(FCS) 4 CRC32校验,硬件自动计算

这里有个关键点:前导码和SFD在硬件接收时就被剥离了,你真正要处理的是从目的MAC开始的14字节头部。我在做FPGA网卡项目时,就曾因为没搞清楚这个偏移量,导致解析出来的MAC地址全是错的。你想想看,地址都错了,后面的处理还有什么意义?

硬件加速要点:

  • MAC地址比较:用硬件比较器并行比对,一个时钟周期就能出结果
  • EtherType提取:固定偏移12字节处,硬件直接截取2字节即可
  • FCS校验:交给硬件CRC模块,别用软件算,太慢了

3.2 IP头解析:网络层的“导航图”

以太网帧拆开后,如果EtherType是0x0800,接下来就是IP头。IPv4头部标准长度是20字节,但可以带选项,最长60字节。我个人习惯在硬件里只处理20字节的标准头,遇到带选项的直接丢给软件——省心。

IPv4头部结构:

字段 长度(位) 说明
版本(Version) 4 IPv4固定为4
首部长度(IHL) 4 以4字节为单位,最小5,最大15
服务类型(ToS) 8 现在常用DSCP字段
总长度(Total Length) 16 整个IP包的长度,包括头部
标识(Identification) 16 分片时用
标志(Flags) 3 DF位、MF位
片偏移(Fragment Offset) 13 分片偏移量
生存时间(TTL) 8 每经过一个路由器减1
协议(Protocol) 8 6=TCP,17=UDP
首部校验和(Header Checksum) 16 只校验IP头部
源IP地址 32 发送方IP
目的IP地址 32 接收方IP

我的经验:硬件解析IP头时,重点关注三个字段:IHL(确定头部长度)、总长度(确定包边界)、协议(决定下一步解析TCP还是UDP)。其他字段像TTL、标识这些,除非你做路由或分片重组,否则可以先放一放。

3.3 TCP/UDP头解析:传输层的“门牌号”

IP头解析完,根据Protocol字段就知道上层是什么协议了。6是TCP,17是UDP。这两个结构差别挺大,咱们分开看。

3.3.1 UDP头:简单粗暴

UDP头只有8字节,结构非常简单:

字段 长度(字节) 说明
源端口 2 发送方端口
目的端口 2 接收方端口
长度 2 UDP头+数据的总长度
校验和 2 可选,为0表示不校验

UDP在硬件里特别好处理。固定8字节,没有状态,没有序号。我在做视频流加速时,UDP包几乎都是直通处理的——解析出端口号,查表转发,完事。

3.3.2 TCP头:复杂但可靠

TCP头就复杂多了。标准头20字节,带选项可以到60字节。硬件里处理TCP,核心是提取五元组(源IP、目的IP、源端口、目的端口、协议)和序列号。

TCP头部关键字段:

字段 长度(字节) 说明
源端口 2 发送方端口
目的端口 2 接收方端口
序列号(SEQ) 4 数据流中的位置
确认号(ACK) 4 期望收到的下一个序列号
数据偏移 4位 TCP头长度,以4字节为单位
标志位 6位 SYN、ACK、FIN、RST等
窗口大小 2 接收窗口
校验和 2 覆盖TCP头+数据+伪头部

注意:TCP校验和计算时,需要包含一个伪头部(Pseudo Header)。伪头部包含源IP、目的IP、协议号、TCP长度。这意味着硬件计算TCP校验和时,必须等IP头解析完才能开始。我曾经在流水线设计时忽略了这一点,导致校验和总是算不对——后来才发现是伪头部里的IP地址还没准备好。

3.4 校验和计算:硬件加速的核心战场

校验和计算是硬件加速最能发挥优势的地方。软件里算一个包的校验和可能要几十个指令周期,硬件里一个时钟周期就能搞定。

3.4.1 IP首部校验和

IP首部校验和的计算方法很简单:

  1. 将校验和字段置为0
  2. 以16位为单位对IP头部进行累加
  3. 如果累加结果超过16位,将高16位加到低16位上(循环进位)
  4. 取反,得到校验和

硬件实现时,可以用一个16位加法树并行计算。我做过一个设计,20字节的IP头,用5个加法器级联,一个时钟周期就出结果。

// 硬件风格的伪代码
// 假设 ip_header[0:9] 是10个16位字
sum = 0;
for (i = 0; i < 10; i++) {
    sum = sum + ip_header[i];
    // 硬件里这是并行加法树
}
// 处理进位
sum = (sum & 0xFFFF) + (sum >> 16);
checksum = ~sum & 0xFFFF;

3.4.2 TCP/UDP校验和

TCP/UDP校验和的计算范围更大,包括:

  • 伪头部(12字节)
  • TCP/UDP头部
  • 数据载荷

伪头部的结构:

字段 长度(字节)
源IP地址 4
目的IP地址 4
保留(全0) 1
协议号 1
TCP/UDP长度 2

硬件加速技巧:

  • 使用增量更新:如果只修改了TTL或端口号,不需要重新计算整个校验和。可以用公式:new_checksum = ~(~old_checksum + ~old_value + new_value)
  • 对于UDP,如果校验和为0,硬件可以直接跳过计算
  • TCP校验和必须计算,不能跳过

3.5 知识体系总览

说了这么多,我画了一张图帮你把整个数据包解析的流程串起来。从网线进来的比特流,到最终提取出应用层数据,每一步都有硬件可以加速的点。

数据包硬件解析流程 物理层接收 前导码剥离 → 时钟同步 → 比特流对齐 以太网帧解析(MAC层) 目的MAC比对 → 源MAC提取 → EtherType判断 FCS校验(CRC32硬件模块) IP头解析(网络层) 版本/IHL检查 → 总长度提取 → 协议号判断 IP校验和验证(16位加法树) TCP解析(Protocol=6) 五元组提取 → SEQ/ACK → 校验和(含伪头部) UDP解析(Protocol=17) 端口提取 → 长度检查 → 可选校验和

这张图把整个流程分成了四层。每一层都有硬件可以加速的关键点。你想想看,从最底层的比特流对齐,到MAC地址比对,再到IP校验和计算,最后到TCP/UDP的五元组提取——每一步如果用硬件流水线来做,延迟可以控制在几十纳秒以内。

我的建议:刚开始做硬件包处理时,别想着一步到位。先把以太网帧解析做稳定,再往上加IP层,最后才是TCP/UDP。我曾经在一个项目里同时上了所有协议层,结果调试时根本分不清问题是出在MAC层还是IP层。后来学乖了,一层一层来,每层验证通过再往下走。

好了,数据包的基础结构就讲到这里。这些内容虽然基础,但却是硬件加速的基石。下一节我们会把这些知识用起来,看看怎么在FPGA里搭一个真正的数据包处理流水线。


公众号:蓝海资料掘金营,微信deep3321