2. GOOSE报文结构解析:以太网帧头、APPID、GOOSE PDU、数据集与控制块
各位好,我是老张。今天咱们来聊聊GOOSE报文的结构。说实话,我刚入行那会儿,看着报文抓包里那一长串十六进制数,头都大了。后来摸清了门道,发现其实就那么几个关键部分。今天我就把这套"拆报文"的功夫教给你。
2.1 从以太网帧头说起
GOOSE报文,说白了就是跑在以太网上的特殊数据包。它首先得是个标准的以太网帧,然后才谈得上"特殊"在哪。
以太网帧头长这样:
| 目的MAC (6字节) | 源MAC (6字节) | 以太网类型 (2字节) |
这里有个关键点——目的MAC地址。GOOSE用的是组播MAC,范围是01-0C-CD-01-00-00到01-0C-CD-01-01-FF。我当年在变电站调试时,有次发现某台智能终端死活收不到GOOSE,查了半天,结果是交换机的IGMP Snooping把组播给过滤了。嗯,这种坑踩过一次就记住了。
以太网类型字段固定为0x88B8,这是IEC 61850给GOOSE分配的专属标识。抓包时看到这个值,基本就能确定是GOOSE报文了。
2.2 APPID——报文的"身份证"
紧跟在以太网帧头后面的,是4字节的TPID和2字节的APPID。不过在实际工程中,我们更关注APPID。
APPID的全称是Application Identifier,应用标识。它是个2字节的值,用来唯一标识一个GOOSE控制块。说白了,就是告诉接收方:"嘿,我是谁发出来的,属于哪个应用。"
APPID的取值规则:
- 范围:0x0000 ~ 0xFFFF
- 0x0000:保留,表示未配置
- 0x0001 ~ 0x3FFF:用于GOOSE
- 0x4000 ~ 0x7FFF:用于SMV(采样值)
我在现场调试时,习惯把APPID和装置编号对应起来。比如保护装置1用0x1001,保护装置2用0x1002,这样看报文就能快速定位来源。你想想看,要是所有装置都用默认的0x0001,那排查问题得多痛苦?
2.3 GOOSE PDU——核心数据区
接下来是重头戏——GOOSE PDU(Protocol Data Unit)。这部分采用ASN.1的BER编码,说白了就是TLV结构:Type-Length-Value。
一个完整的GOOSE PDU包含以下字段:
| 字段名 | 标签 | 说明 |
|---|---|---|
| gocbRef | 0x81 | GOOSE控制块引用,格式如"LD1/LLN0$GO$gcb1" |
| timeAllowedtoLive | 0x82 | 允许生存时间,单位毫秒,接收方用这个判断报文是否超时 |
| datSet | 0x83 | 数据集引用,指向具体的数据定义 |
| goID | 0x84 | GOOSE标识,可选字段,用于调试 |
| t | 0x85 | 时间戳,报文生成时刻 |
| stNum | 0x86 | 状态序号,每次数据变化+1 |
| sqNum | 0x87 | 序列序号,每次重发+1 |
| simulation | 0x88 | 仿真标志,TRUE表示测试报文 |
| confRev | 0x89 | 配置版本号,配置变化时递增 |
| ndsCom | 0x8A | 需要后续通信,通常为FALSE |
| numDatSetEntries | 0x8B | 数据集中条目数量 |
| allData | 0xAB | 实际数据,按数据集定义顺序排列 |
这里我特别想强调stNum和sqNum这两个序号。它们配合起来,构成了GOOSE的"心跳+变化"机制:
- 数据不变时:stNum不变,sqNum递增(重发机制)
- 数据变化时:stNum+1,sqNum重置为0
我曾经遇到过一个奇怪的问题:某台装置频繁报"GOOSE断链",但抓包看报文明明在发。后来发现是stNum溢出了——从65535跳回0时,接收方认为序号异常,直接丢弃了报文。这种边界情况,厂家文档里很少提,全靠现场踩坑。
2.4 数据集与控制块——报文背后的"剧本"
报文里传输的allData字段,到底包含哪些数据?这就得看数据集(DataSet)的定义了。
数据集就像一份"数据清单",告诉发送方:"你要把这几条数据打包发出去。"比如:
数据集 "dsGOOSE1" 包含:
1. 位置1:断路器A相位置 (GGIO1.Ind1.stVal)
2. 位置2:断路器B相位置 (GGIO1.Ind2.stVal)
3. 位置3:断路器C相位置 (GGIO1.Ind3.stVal)
4. 位置4:重合闸充电状态 (RBRF1.Ind1.stVal)
而控制块(GSEControl)则是"执行者",它指定了:
- 用哪个数据集(datSet)
- 发往哪个组播地址(destination MAC)
- APPID是多少
- 最小重发间隔、最大重发间隔等参数
说白了,控制块是"怎么发",数据集是"发什么"。两者配合,才构成了一条完整的GOOSE报文。
我的调试小技巧:
拿到SCD文件后,先搜"GSEControl"关键字,找到所有控制块定义。然后对照着看报文里的gocbRef和datSet,就能快速定位这条报文对应哪个IED的哪个功能。这比瞎抓包有效率得多。
2.5 一张图看懂GOOSE报文结构
说了这么多,不如一张图来得直观。下面是我用SVG画的GOOSE报文结构图,你一看就明白:
⚠️ 重要提醒:
报文解析时,千万别把VLAN标签和APPID搞混了。VLAN标签的TPID是0x8100,而APPID是0x88B8后面的内容。我见过有人把VLAN ID当成APPID来配置,结果两台装置怎么都对不上号。
2.6 实战:手撕一条GOOSE报文
光说不练假把式。咱们拿一条真实的GOOSE报文来拆解:
01 0C CD 01 00 01 // 目的MAC:组播地址
00 50 56 C0 00 08 // 源MAC:VMware虚拟网卡
88 B8 // 以太网类型:GOOSE
81 00 00 64 // VLAN标签:VID=100
00 01 // APPID:0x0001
00 64 // 长度:100字节
// 以下是GOOSE PDU内容(ASN.1编码)
61 82 00 60 // 外部TLV:类型0x61,长度0x60
80 1A // gocbRef:标签0x80,长度0x1A
4C 44 31 2F 4C 4C 4E 30 24 47 4F 24 67 63 62 31
// "LD1/LLN0$GO$gcb1"
81 0A // timeAllowedtoLive:标签0x81,长度0x0A
00 00 27 10 // 10000ms = 10s
82 18 // datSet:标签0x82,长度0x18
4C 44 31 2F 4C 4C 4E 30 24 44 53 24 64 73 47 4F 4F 53 45 31
// "LD1/LLN0$DS$dsGOOSE1"
85 08 // t(时间戳):标签0x85,长度0x08
00 00 01 87 5A 3C 00 00 // 2024-01-15 10:30:00
86 01 // stNum:标签0x86,长度0x01
05 // 状态序号=5
87 01 // sqNum:标签0x87,长度0x01
03 // 序列序号=3
AB 82 00 10 // allData:标签0xAB,长度0x10
// 具体数据内容(按数据集定义解析)
83 01 01 // 第1个数据:布尔值TRUE
83 01 00 // 第2个数据:布尔值FALSE
83 01 01 // 第3个数据:布尔值TRUE
83 01 00 // 第4个数据:布尔值FALSE
看到没?报文里每个字段都清清楚楚。我建议你拿到Wireshark抓包后,对照着这个结构逐字节看一遍。看个三五条,你就能闭着眼认出每个字段了。
💡 个人习惯分享:
我调试时喜欢在Wireshark里加两列:一列显示gocbRef,一列显示stNum/sqNum。这样一眼就能看出报文是"心跳"还是"变位"。具体做法是:右键报文 → Protocol Preferences → IEC 61850 GOOSE → 勾选显示字段。
好了,GOOSE报文的结构就讲到这里。记住核心三要素:以太网帧头决定怎么传,APPID决定是谁发的,GOOSE PDU决定发了什么。下次你看到一条GOOSE报文,按这个思路拆解,保证不会乱。
公众号:蓝海资料掘金营,微信deep3321