报文接收与解析:MAC层接收、帧校验、报文类型识别、头部解析流程

好,咱们接着聊转发引擎的第一步——报文怎么进来的。

很多人觉得MAC层接收就是“把线上一堆bit收进来”,其实没那么简单。我当年刚做路由芯片时,就吃过这个亏。芯片回来后,发现某些短包老是丢,查了三天,最后发现是MAC层的接收状态机少了一个状态。嗯,从那以后,我对这部分的敬畏心就上来了。

MAC层接收:不只是收bit

MAC层接收,说白了就是物理层把串行数据转成并行后,MAC层要干三件事:

  • 定界:找到帧的起始和结束。靠的是前导码(Preamble)和帧起始定界符(SFD)。
  • 去填充:把物理层加的填充位、前导码全部扔掉。
  • 完整性检查:帧长是否合法?最小64字节,最大1518字节(Jumbo帧另说)。

我个人习惯,在MAC层接收模块里,会同时做两件事:一边收数据,一边算CRC。这样收完最后一bit,CRC结果也出来了,省一个时钟周期。你想想看,在100Gbps的线速下,一个时钟周期能省多少事?

关键点:MAC层接收必须支持“截断”和“上抛”两种模式。截断模式遇到超长帧直接丢弃,上抛模式则把超长帧标记后送给上层处理。我建议转发芯片用截断模式,控制面用上抛模式。

帧校验:CRC不是万能的

以太网用的是CRC32,多项式是0x04C11DB7。这个多项式能检测所有单bit错误、双bit错误、奇数个错误,以及长度不超过32的突发错误。

但我要泼盆冷水——CRC32不是万能的。我在项目中遇到过一种情况:光纤链路受到强电磁干扰,连续几十个bit都翻了,CRC32竟然没检出来。为什么?因为CRC32对超过32bit的突发错误,漏检率是1/2^32。虽然概率低,但不是零。

所以,现在高端路由芯片会在MAC层之外,再加一层FEC(前向纠错)。比如RS-FEC,能纠正最多11个symbol的错误。嗯,这里要注意:FEC是物理层的事,但转发引擎必须知道FEC的状态——如果FEC纠错次数太多,说明链路质量在恶化,应该触发切换。

实战技巧:CRC校验失败后,不要立刻丢弃。我习惯把错误帧统计到寄存器里,同时触发一个中断。这样调试时能知道是哪个端口在丢包,而不是两眼一抹黑。

报文类型识别:一眼看出你是谁

帧校验通过后,接下来就是识别报文类型。这一步决定了后续走哪条处理路径。

识别依据主要是以太网类型字段(EtherType)或者LLC/SNAP头。常见的类型有:

EtherType (十六进制) 协议类型 处理路径
0x0800 IPv4 路由转发
0x86DD IPv6 路由转发
0x0806 ARP 上送CPU
0x8100 VLAN Tag 剥Tag后继续识别
0x8847 MPLS单播 MPLS转发

我曾经踩过一个坑:某款芯片的报文类型识别模块,只查了外层EtherType。结果客户跑了QinQ(双层VLAN Tag),内层EtherType被当成外层处理,所有报文都送CPU了。从那以后,我设计时一定会把VLAN Tag剥干净,再识别真正的报文类型。

头部解析流程:庖丁解牛

头部解析,就是把报文头里的字段拆出来,放到一个叫“解析结果缓冲区”的地方。后续的查表、编辑、转发都靠这个缓冲区。

解析流程大致如下:

  1. 固定偏移解析:MAC DA、MAC SA、EtherType,这些位置是固定的,直接取。
  2. 条件解析:如果EtherType是0x8100,说明有VLAN Tag,再往后读4字节。
  3. 协议栈解析:识别出IPv4后,解析IP头:版本、IHL、TOS、总长、标识、标志、片偏移、TTL、协议、头校验和、源IP、目的IP。
  4. 四层解析:根据IP头的协议字段,决定是否解析TCP/UDP端口号。

这里有个设计难点:解析深度。你想想看,一个报文可能嵌套了VLAN、MPLS、IP、TCP,解析器要能一层层剥下去。但芯片面积有限,不能无限解析。我一般设一个“解析深度计数器”,最多解析8层协议头。超过8层的,直接标记为“解析失败”,走异常路径。

注意:头部解析时,一定要做“字段合法性检查”。比如IPv4的头校验和,如果错了,这个包就不该继续转发。我曾经见过一个bug:芯片没检查IP校验和,结果转发了一个头损坏的包,导致下游设备连续报错。

核心逻辑:一张图说清楚

下面这张图,是我画给团队新人的。它把报文接收与解析的整个流程串起来了。你看一遍,应该就能理解数据是怎么从线上一路走到解析缓冲区的。

报文接收与解析核心流程 MAC层接收 定界 · 去填充 · 完整性检查 帧校验 (CRC32) 校验通过?→ 继续 | 失败?→ 丢弃/统计 报文类型识别 EtherType → 决定解析路径 头部解析 固定偏移解析 → 条件解析 协议栈解析 → 四层解析 深度限制:最多8层协议头 异常处理路径 CRC失败 · 超长帧 · 解析失败 解析结果缓冲区 供后续查表、编辑、转发使用 注:虚线表示异常路径,实线表示正常转发路径 解析结果缓冲区包含: · MAC DA/SA · EtherType · VLAN ID · IP SA/DA · TTL · 协议号 · TCP/UDP端口号

这张图里,我特意把异常路径用虚线标出来了。很多芯片设计者只关注正常路径,忽略了异常处理。但实际线上跑的流量,总有千分之几的坏包。如果异常路径没设计好,轻则丢包,重则整机挂死。

避坑指南:我踩过的三个雷

  • 雷区一:解析深度不够。我曾经设计了一款芯片,只支持解析两层MPLS标签。结果客户跑了三层MPLS,所有包都解析失败。后来我学乖了,解析深度做成可配置的,至少支持8层。
  • 雷区二:CRC计算和接收不同步。有个项目,MAC接收模块用了两级流水线,CRC计算模块只用了单级。结果数据对齐出了问题,每收100个包就有1个CRC误报。查了整整一周。
  • 雷区三:EtherType匹配太死。有些厂商会在标准EtherType之外,用自定义的EtherType做隧道封装。如果芯片只认标准类型,这些包就丢了。我现在的做法是:除了标准类型,还支持用户自定义的EtherType匹配表。

个人建议:在设计解析器时,留一个“旁路模式”。调试时可以把解析结果和原始报文一起抓出来,逐字段对比。这个功能在芯片回片验证阶段,能帮你省下至少一半的调试时间。

好了,报文接收与解析这部分,核心就是这些。记住一句话:收进来是本事,收对了是水平。下一层,我们就要拿着解析结果去查转发表了。但那是另一个话题,咱们先消化好今天的内容。


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