第三章 802.1AS协议栈:协议层次结构与数据帧格式
说实话,刚接触802.1AS那会儿,我盯着协议栈分层图发了好一阵呆。你想想看,在车载网络或者工业以太网里,时钟同步要求亚微秒级精度,那么底层到底怎么把时间戳精准地嵌进报文里?层次搞不清楚,后面配置测试点、定位丢包问题全得卡壳。所以这一章咱们把协议栈剥开,看看每一层到底干了什么,数据帧又长什么样。
3.1 协议栈的分层哲学
802.1AS本质上是个Layer 2的协议,但它依赖物理层给时间戳打标记。分层结构从下往上依次是:
- PHY层:负责物理介质收发,关键是在帧头帧尾的特定位置捕获精确时间戳。我遇到过某款PHY芯片时间戳点位偏了一个bit,结果同步误差直接窜到微秒级。
- MAC层:负责介质访问控制,封装以太网帧头,接收PHY层上报的时间戳。
- 协议层(广义精确时间协议):运行在MAC之上,处理Sync、Follow_Up、Delay_Req/Resp等报文,完成主从时钟的偏移与延迟计算。
- 应用层:上层调用服务,比如音频视频桥接中的时间感知整形,或控制系统的同步调度。
为什么非要这么分层?说白了,每层只关心一件事。PHY只管抓时间,MAC只管送包,只管算时间差。我曾经在测试中碰到一个问题:某个设备把时间戳捕捉放在了MAC软件层,结果每个包的时间戳偏差几十微秒——因为软件中断响应不可预测。从那以后我习惯先核查物理层时间戳接口的硬件实现。
3.2 协议栈核心结构图
下面这张图是我在项目里经常拿出来跟团队对齐用的,可以直观看出层次关系和关键接口。里面标出了时间戳捕获点、协议层交互,以及最终报文在介质上的样子。
3.3 数据帧格式拆解
802.1AS使用标准的IEEE 802.3以太网帧,以太网类型固定为0x88F7。我刚开始用Wireshark抓包时,看到这个值就知道是了。帧结构包含以下几个关键部分:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 前导码 + SFD | 8 | 物理层添加,MAC层不处理 |
| 目的MAC | 6 | 一般为组播地址(比如01-80-C2-00-00-0E) |
| 源MAC | 6 | 发送端口MAC |
| 以太网类型 | 2 | 0x88F7(标识) |
| PTP头部 | 34 | 包含版本、消息类型、域号、序列ID等 |
| 消息体 | 可变 | 不同类型报文内容不同(Sync 44B, Delay_Req 44B等) |
| TLV(可选) | 可变 | 用于扩展,比如验证时间戳的follow_up信息 |
| FCS | 4 | 帧校验序列 |
PTP头部是理解同步流程的核心。你打开Wireshark看一眼就知道了,里面最关键的是messageType字段(占4bit),它决定了这个报文的作用:
- 0x0 – Sync:主时钟发出,从时钟记录到达时间t2
- 0x1 – Delay_Req:从时钟发出,主时钟记录到达时间t4
- 0x2 – Follow_Up:主时钟将Sync的精确发送时间t1告诉从时钟
- 0x3 – Delay_Resp:主时钟将Delay_Req的到达时间t4告诉从时钟
3.4 报文类型与关键参数速查
为了便于记忆,我整理了常用报文的核心参数对比。你可以在测试时快速查阅:
| 报文类型 | messageType | 发送方向 | 头部特殊字段 | 典型作用 |
|---|---|---|---|---|
| Sync | 0x0 | 主→从 | 无时间戳(由Follow_Up携带) | 触发从时钟本地捕获 |
| Follow_Up | 0x2 | 主→从 | preciseOriginTimestamp | 补偿Sync的精确发送时间t1 |
| Delay_Req | 0x1 | 从→主 | 无时间戳(由Delay_Resp携带) | 要求主时钟记录到达时间t4 |
| Delay_Resp | 0x3 | 主→从 | receiptTimestamp | 返回t4,用于计算链路延迟 |
3.5 实践:用Wireshark抓一个Sync帧
我个人习惯在实际调试时先用Wireshark打开抓取的pcap文件。一个典型的Sync帧(去掉前导码和SFD)看起来大概这样:
Frame 123: 64 bytes on wire (512 bits), 64 bytes captured
Ethernet II, Src: aa:bb:cc:dd:ee:01, Dst: 01:80:c2:00:00:0e
Destination: 01:80:c2:00:00:0e (PTP v2 primary)
Source: aa:bb:cc:dd:ee:01 (Some device)
Type: 802.1AS (0x88f7)
Precision Time Protocol (IEEE 1588v2)
0000 000. .... .... = transportSpecific: 0x0
.... ...0 0000 0000 = messageId: Sync (0x0)
.... .... .... 0000 = version: PTP version 2 (0x00)
Message length: 44
Domain number: 0
Reserved: 00
Flags: 0x0000
Correction field: 0
Reserved: 0x00000000
Source port identity: ...
Sequence id: 0x0001
Control field: 0x00
Log message interval: 0x01 (间隔 2^1 = 2秒)
注意,这里时间戳字段(如preciseOriginTimestamp)是空的——它会在后面的Follow_Up报文里出现。我刚开始学的时候老找不对地方,后来养成习惯:看Sync先看sequenceId,再看对应的Follow_Up,两者对上号才能开始算偏移。