抓包工具里刷屏的 16 进制不是"天书"。IEC 61850 的报文有非常规整的层次,本文给一套可以直接照着走的拆解顺序,每个判断点都标注它在标准里的位置。文中只讲标准规定的格式,不涉及任何具体设备的性能或实测结论。
同样是电网报文,IEC 61850 的 MMS 走 ISO 9506 的协议栈,在 TCP 之上还有两层薄壳:TPKT(RFC 1006)和 COTP。所以从 TCP 载荷的第一个字节就能分流:
| 首个字节 | 含义 | 常见于 |
|---|---|---|
03 00 开头 | TPKT:版本 3 + 保留 0,随后两字节是 TPKT 总长度 | MMS over TCP(61850-8-1) |
68 | IEC 60870-5-104 的启动字符,随后一字节是 APDU 长度 | 104 通道 |
| 不是 TCP(以太网直发) | 以太类型 0x88B8 / 0x88BA | GOOSE / SV(61850-8-1、9-2) |
把 104 的 68 当成 61850 报文去解,是最常见的第一次误判。反过来,GOOSE 和 SV 根本不在 TCP 上,用"只过滤 TCP 102 端口"的方式是抓不到它们的。
剥掉 TPKT/COTP 之后就是 MMS 的 PDU。它的 CHOICE 标签(BER 上下文类标记)已经把"这是什么"写进了第一个字节:
| 标签 | PDU | 工程含义 |
|---|---|---|
0x61 | confirmed-RequestPDU | 客户端发起的一次请求(读、写、发现模型……) |
0xa1 | confirmed-ResponsePDU | 对上面那次请求的应答 |
0xa2 | confirmed-ErrorPDU | 应答是错误(对象不存在、参数不合法……) |
0xa3 | unconfirmed-PDU | 无需应答的信息上报 |
0xa8 / 0xa9 | initiate-Request / Response | 连接建立时那次能力协商握手 |
再往里一层才是具体服务。0xa3(unconfirmed-PDU)里的 0xa0 是 informationReport,即服务器主动推上来的报告;0x61 里的 0xa4 是 read、0xa5 是 write、0xa1 是 getNameList(枚举模型)。调试时先看这一层,就能把"设备在报数据"和"上位机在翻模型"两件事分开。
MMS 本身不认识"保护装置"或"遥信",它只认 domainID + itemName 两段字符串(BER 里以 0x1a VisibleString 出现)。61850-8-1 做的映射是:
domainID 是逻辑设备实例名,例如 IED1LD0;itemName 是逻辑节点、功能约束、数据对象的组合,字符串里常见 $ 分隔(如 GGIO1$ST$Ind1$stVal),有的抓包工具显示时会替换成 .,以域里真实的字符串为准;NamedVariableList,报告控制块(RCB)则作为普通数据对象存在。所以"设备说找不到对象"这类错误,八成不是通信断了,而是 itemName 跟 ICD 里导出的名字对不上——大小写、功能约束层、或者少了一段 $。
一帧只能说明"发了什么",对错要看一问一答的顺序。一条典型的 MMS 连接是:initiate 握手 → 用 getNameList、getVariableAccessAttributes 摸清模型 → read 轮询或 informationReport 上送 → 最后 conclude 或 TCP 断开。判断"没响应"之前,先确认三件事:
invokeID 和应答里的 invokeID 是不是同一个——对不上说明你在看两段不同的对话;RptEna),没使能时设备不会主动推。| 现象 | 实际原因 |
|---|---|
把 68 开头的报文当 61850 解 | 那是 104 的 APCI,与 MMS 无关 |
| 用 TPKT 长度去切 MMS 报文 | TPKT 长度是整段(含 COTP),MMS 的长度要再往里读一层 |
| 只抓一帧就判"设备不回" | 应答可能在另一条 TCP 流里,或者 invokeID 对不上 |
把 informationReport 当"设备主动告警" | 它是报告控制块触发的,触发条件由 TrgOps 决定 |
把这四步固化成习惯,报文分析就从"猜"变成"对表":先用首字节分流,再用 PDU 标签定位服务,再把字符串翻成模型引用,最后拿时序验证因果关系。真正难的不是解一帧,而是知道该看哪一帧。