第四章 行情数据链路层协议解析

各位同学,今天我们进入一个非常关键的环节——链路层协议解析。说白了,就是搞清楚数据在网线上是怎么跑的。我在做FPGA行情加速卡的时候,这部分可是踩过不少坑,今天一并分享给你们。

4.1 以太网MAC帧结构

先看最基础的MAC帧。你想想看,数据从网口进来,第一个遇到的就是MAC层。它的结构其实不复杂,但细节很多。

字段 长度(字节) 说明
前导码 7 101010... 用于同步
帧起始定界符 1 10101011 标识帧开始
目的MAC地址 6 接收方地址
源MAC地址 6 发送方地址
类型/长度 2 0x0800为IP,0x0806为ARP
数据 46-1500 上层协议数据
帧校验序列 4 CRC32校验

嗯,这里要注意:前导码和SFD在FPGA接收时通常被硬件过滤掉了。我刚开始做的时候,还傻乎乎地等前导码,结果发现RGMII接口直接就给吃了。所以你在写解析逻辑时,直接从目的MAC开始处理就行。

关键点:MAC帧的最小长度是64字节(含CRC),不足时会自动填充。行情数据包通常很小,这个填充机制要特别留意。

4.2 ARP协议解析与实现

ARP协议,说白了就是"谁有这个IP?告诉我你的MAC"。在FPGA里实现ARP,我建议用状态机来做,清晰又高效。

ARP报文结构是这样的:

// ARP报文格式(28字节)
typedef struct {
    uint16_t htype;      // 硬件类型,以太网为1
    uint16_t ptype;      // 协议类型,IPv4为0x0800
    uint8_t  hlen;       // 硬件地址长度,MAC为6
    uint8_t  plen;       // 协议地址长度,IP为4
    uint16_t oper;       // 操作码:1请求,2应答
    uint8_t  sha[6];     // 发送方MAC
    uint8_t  spa[4];     // 发送方IP
    uint8_t  tha[6];     // 目标MAC
    uint8_t  tpa[4];     // 目标IP
} arp_packet_t;

我在项目中遇到过一个问题:ARP请求来了,FPGA回复了,但PC就是不认。查了半天,发现是MAC地址字节序搞反了。以太网用的是大端序,而我的FPGA代码里直接按小端处理了。这个坑,你们一定要记住。

实战技巧:FPGA实现ARP时,建议维护一个ARP缓存表。用BRAM实现,深度256就够用了。每次收到ARP请求,先查表,有就直接回复,没有才广播。

4.3 IP协议头解析

IP头解析,这是行情数据链路层解析的重头戏。为什么?因为你要从IP头里拿到源IP、目的IP,还要检查校验和。

标准IP头是20字节(不含选项):

// IP头结构
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|版本| 首部长度 |   服务类型   |         总长度(字节)           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        标识(16bit)          |标志|     片偏移(13bit)          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  生存时间(TTL)  |   协议号   |        首部校验和              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        源IP地址                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       目的IP地址                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

我个人习惯在FPGA里这样处理IP头:

  1. 先检查版本号,必须是4,IPv6直接丢弃
  2. 计算首部长度,一般是5(20字节)
  3. 校验和验证,这个必须做
  4. 检查目的IP是否匹配
  5. 检查协议号,UDP是17

注意:IP校验和计算是16位反码求和。很多新手直接用补码加法,结果校验永远过不了。正确的做法是:每16位相加,如果有进位就加回低位,最后取反。

4.4 UDP协议解析

UDP就简单多了。行情数据绝大多数走UDP,因为快啊!TCP那三次握手,对行情来说太慢了。

UDP头只有8字节:

// UDP头结构
typedef struct {
    uint16_t src_port;    // 源端口
    uint16_t dst_port;    // 目的端口
    uint16_t length;      // UDP长度(含头)
    uint16_t checksum;    // UDP校验和(可选)
} udp_header_t;

我曾经踩过一个坑:UDP校验和。有些系统把校验和设为0,表示不校验。但有些交换机或网卡会强制校验。所以我的建议是:FPGA接收时,如果校验和为0就跳过,不为0就验证。发送时,最好把校验和算对,省得麻烦。

核心要点:行情数据链路层解析,本质上就是逐层剥头的过程。MAC头→IP头→UDP头,每剥一层就检查一次,全部通过后才把数据交给上层处理。

4.5 整体解析流程

下面我用SVG画一个完整的解析流程图,你们可以对照着看:

行情数据链路层解析流程 原始以太网数据 MAC帧解析 类型判断 ARP协议处理 IP头解析 UDP协议解析 行情数据提取 ARP请求/应答 MAC地址学习 校验和验证 IP过滤 端口匹配 数据重组

这个流程,说白了就是一条流水线。数据从左边进来,经过MAC、IP、UDP三层过滤,最后才到应用层。我在做FPGA实现时,每一级都用独立的流水线阶段,这样吞吐量能跑到线速。

我的经验:在FPGA里做协议解析,建议用流水线架构。每一级只做一件事,比如第一级剥MAC头,第二级验IP校验和,第三级查UDP端口。这样时钟频率能跑得高,而且逻辑清晰好调试。

好了,链路层解析就讲到这里。记住一句话:协议解析的本质就是"剥洋葱",一层一层来,每层做好自己的事。下一节我们讲传输层,到时候会看到更精彩的内容。


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