报文接收与解析: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剥干净,再识别真正的报文类型。
头部解析流程:庖丁解牛
头部解析,就是把报文头里的字段拆出来,放到一个叫“解析结果缓冲区”的地方。后续的查表、编辑、转发都靠这个缓冲区。
解析流程大致如下:
- 固定偏移解析:MAC DA、MAC SA、EtherType,这些位置是固定的,直接取。
- 条件解析:如果EtherType是0x8100,说明有VLAN Tag,再往后读4字节。
- 协议栈解析:识别出IPv4后,解析IP头:版本、IHL、TOS、总长、标识、标志、片偏移、TTL、协议、头校验和、源IP、目的IP。
- 四层解析:根据IP头的协议字段,决定是否解析TCP/UDP端口号。
这里有个设计难点:解析深度。你想想看,一个报文可能嵌套了VLAN、MPLS、IP、TCP,解析器要能一层层剥下去。但芯片面积有限,不能无限解析。我一般设一个“解析深度计数器”,最多解析8层协议头。超过8层的,直接标记为“解析失败”,走异常路径。
注意:头部解析时,一定要做“字段合法性检查”。比如IPv4的头校验和,如果错了,这个包就不该继续转发。我曾经见过一个bug:芯片没检查IP校验和,结果转发了一个头损坏的包,导致下游设备连续报错。
核心逻辑:一张图说清楚
下面这张图,是我画给团队新人的。它把报文接收与解析的整个流程串起来了。你看一遍,应该就能理解数据是怎么从线上一路走到解析缓冲区的。
这张图里,我特意把异常路径用虚线标出来了。很多芯片设计者只关注正常路径,忽略了异常处理。但实际线上跑的流量,总有千分之几的坏包。如果异常路径没设计好,轻则丢包,重则整机挂死。
避坑指南:我踩过的三个雷
- 雷区一:解析深度不够。我曾经设计了一款芯片,只支持解析两层MPLS标签。结果客户跑了三层MPLS,所有包都解析失败。后来我学乖了,解析深度做成可配置的,至少支持8层。
- 雷区二:CRC计算和接收不同步。有个项目,MAC接收模块用了两级流水线,CRC计算模块只用了单级。结果数据对齐出了问题,每收100个包就有1个CRC误报。查了整整一周。
- 雷区三:EtherType匹配太死。有些厂商会在标准EtherType之外,用自定义的EtherType做隧道封装。如果芯片只认标准类型,这些包就丢了。我现在的做法是:除了标准类型,还支持用户自定义的EtherType匹配表。
个人建议:在设计解析器时,留一个“旁路模式”。调试时可以把解析结果和原始报文一起抓出来,逐字段对比。这个功能在芯片回片验证阶段,能帮你省下至少一半的调试时间。
好了,报文接收与解析这部分,核心就是这些。记住一句话:收进来是本事,收对了是水平。下一层,我们就要拿着解析结果去查转发表了。但那是另一个话题,咱们先消化好今天的内容。
公众号:蓝海资料掘金营,微信deep3321