2. 网络协议基础回顾:OSI七层模型、TCP/IP协议栈、以太网帧结构、IP包结构、TCP/UDP头部
做网络加速器硬件设计,说白了就是跟各种协议打交道。你想想看,数据从网线进来,到最终被应用层处理,中间要经过多少层封装和解封装?我刚开始接触这个领域时,总觉得协议栈是个黑盒子,直到亲手调过几次硬件加速模块,才真正理解每一层存在的意义。
这一节,咱们把网络协议的基础知识过一遍。别嫌啰嗦,这些内容在后续的FPGA实现中会反复用到。嗯,咱们开始吧。
2.1 OSI七层模型:理论上的完美分层
OSI七层模型,教科书上的标准答案。从上到下分别是:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
我个人习惯把七层模型当作一个「思维框架」。实际工程中,没人会严格按照七层去实现。但它的分层思想非常有用——每一层只关心自己的事,下层为上层提供服务,上层不需要知道下层的实现细节。
举个例子,你在浏览器里输入网址,应用层的HTTP协议开始工作。它不需要知道数据是通过光纤还是双绞线传输的,那是物理层的事。这种解耦,让网络协议栈变得可扩展、可维护。
核心要点:OSI七层模型是「概念模型」,TCP/IP协议栈是「事实标准」。做硬件加速时,我们主要关注TCP/IP协议栈的下面四层。
2.2 TCP/IP协议栈:工程中的实际标准
TCP/IP协议栈只有四层:应用层、传输层、网络层、网络接口层。它把OSI的上三层合并为应用层,下两层合并为网络接口层。
为什么TCP/IP能成为事实标准?说白了,它更务实。OSI七层模型太理想化了,有些层的边界在实际实现中很难划分。我在项目中遇到过,有些硬件加速方案直接把传输层和网络层的处理逻辑合并到一个模块里,性能反而更好。
TCP/IP协议栈的核心协议包括:
- 应用层:HTTP、FTP、SMTP、DNS等
- 传输层:TCP(可靠传输)、UDP(不可靠但快速)
- 网络层:IP(路由和寻址)、ICMP(差错报告)、ARP(地址解析)
- 网络接口层:以太网协议、PPP等
我的经验:做FPGA网络加速时,传输层和网络层的处理是性能瓶颈。我曾经在一个40Gbps的项目中,因为TCP校验和计算模块没优化好,导致整体吞吐量上不去。后来改用流水线并行计算,才解决问题。
2.3 以太网帧结构:数据链路层的封装
以太网帧是数据在物理介质上传输的基本单位。它的结构如下:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 前导码 | 7 | 同步时钟,用于接收端锁定比特流 |
| 帧起始定界符 | 1 | 标识帧的开始 |
| 目的MAC地址 | 6 | 接收方的物理地址 |
| 源MAC地址 | 6 | 发送方的物理地址 |
| 类型/长度 | 2 | 0x0800表示IPv4,0x0806表示ARP |
| 数据载荷 | 46-1500 | 上层协议的数据包(IP包) |
| 帧校验序列 | 4 | CRC32校验,检测传输错误 |
这里有个关键点:最小帧长度是64字节(从目的MAC到FCS)。如果数据载荷不足46字节,需要填充。为什么?这是为了检测碰撞。我记得在调试10G以太网MAC时,就因为填充逻辑没处理好,导致某些短包被丢弃,查了好几天才找到原因。
避坑指南:我曾经在FPGA中实现以太网MAC时,忽略了前导码的处理。前导码在硬件接收时会被剥离,但如果你要做原始数据捕获,必须保留前导码的时序信息。否则,数据对齐会出问题。
2.4 IP包结构:网络层的核心
IP包(IPv4)是网络层的数据单元。它的头部结构如下:
| 字段 | 长度(位) | 说明 |
|---|---|---|
| 版本 | 4 | IPv4为4,IPv6为6 |
| 头部长度 | 4 | 以4字节为单位,最小5(20字节) |
| 服务类型 | 8 | 用于QoS,实际很少用 |
| 总长度 | 16 | IP包总字节数,最大65535 |
| 标识 | 16 | 用于分片重组 |
| 标志 | 3 | DF(不分片)、MF(更多分片) |
| 片偏移 | 13 | 分片在原包中的偏移量 |
| 生存时间 | 8 | 每经过一个路由器减1,为0时丢弃 |
| 协议 | 8 | 6表示TCP,17表示UDP |
| 头部校验和 | 16 | 仅校验头部,不校验数据 |
| 源IP地址 | 32 | 发送方IP |
| 目的IP地址 | 32 | 接收方IP |
| 选项 | 可变 | 很少使用,通常为0 |
IP头部校验和的计算,是硬件加速中常见的优化点。你想想看,每个IP包都要计算校验和,如果软件来做,CPU开销很大。FPGA可以用并行加法器,一个时钟周期就算完。我做过一个设计,用三级流水线计算校验和,延迟只有3个时钟周期。
注意:IP头部校验和只校验头部本身,不校验数据载荷。数据的完整性由上层协议(TCP/UDP)保证。这是分层设计的一个体现。
2.5 TCP/UDP头部:传输层的两种选择
TCP和UDP是传输层的两个主要协议。一个可靠但慢,一个快但不可靠。做硬件加速时,选择哪个协议,直接影响设计复杂度。
TCP头部结构
| 字段 | 长度(位) | 说明 |
|---|---|---|
| 源端口 | 16 | 发送方端口号 |
| 目的端口 | 16 | 接收方端口号 |
| 序列号 | 32 | 数据字节流的编号 |
| 确认号 | 32 | 期望收到的下一个序列号 |
| 数据偏移 | 4 | 头部长度,以4字节为单位 |
| 保留 | 6 | 保留位,必须为0 |
| 标志位 | 6 | URG、ACK、PSH、RST、SYN、FIN |
| 窗口大小 | 16 | 接收窗口,用于流量控制 |
| 校验和 | 16 | 校验头部+数据+伪头部 |
| 紧急指针 | 16 | 仅URG标志位有效时使用 |
TCP的校验和计算比较特殊,它包含一个「伪头部」。伪头部由源IP、目的IP、协议号、TCP长度组成。这意味着TCP校验和依赖IP层的信息。在硬件实现时,需要跨层传递这些数据。
我曾经在调试TCP卸载引擎时,发现校验和总是算不对。查了半天,原来是伪头部中的IP地址字节序搞反了。嗯,这种细节问题,软件调试很容易发现,硬件仿真却要跑很久才能暴露。
UDP头部结构
| 字段 | 长度(位) | 说明 |
|---|---|---|
| 源端口 | 16 | 发送方端口号 |
| 目的端口 | 16 | 接收方端口号 |
| 长度 | 16 | UDP头部+数据的总长度 |
| 校验和 | 16 | 可选,为0表示不校验 |
UDP头部只有8个字节,简单得令人感动。它的校验和也是可选的——如果设置为0,接收端就不做校验。这在某些高性能场景下很有用,比如视频流传输,丢几个包不影响观看体验。
我的建议:如果做硬件加速,优先考虑UDP。TCP的状态机太复杂了,光是三次握手、四次挥手、滑动窗口、拥塞控制,就够你写几千行Verilog。UDP无状态,处理起来简单得多。很多网络加速器方案,都是UDP加速先做,TCP加速后做。
2.6 协议栈的硬件视角:数据流怎么走?
下面这张图展示了数据从网线进入FPGA后,各层协议的处理流程。我用SVG画了一个简化的框架图,方便你理解。
你看,数据从下往上走,每一层都在做「拆包」的工作。物理层去掉前导码,数据链路层去掉帧头和帧尾,网络层解析IP头部,传输层解析TCP/UDP头部,最后应用层拿到原始数据。
反过来,数据从上往下走,每一层都在做「封包」的工作。应用层数据加上TCP/UDP头部,再加上IP头部,再加上以太网帧头,最后通过物理层发送出去。
在FPGA中实现这些功能,核心就是流水线处理。每个模块只处理自己那一层的数据,通过FIFO或AXI-Stream接口传递。我习惯把每个协议层做成独立的IP核,这样便于复用和调试。
重要提醒:硬件处理协议时,必须考虑「字节序」问题。网络字节序是大端模式,而大多数处理器是小端模式。我曾经因为字节序搞反,导致IP地址解析出来是反的,排查了整整两天。记住:在硬件中,htonl和ntohl的转换是必不可少的。
好了,网络协议基础就回顾到这里。这些内容看似简单,但每一个细节都可能在硬件实现中给你挖坑。下一节,我们会把这些知识应用到实际的FPGA设计中,看看如何用硬件加速网络包的处理。
公众号:蓝海资料掘金营,微信deep3321