IEC61850 MMS协议深度解析:从原理到Wireshark抓包实战
2026/9/21 20:35:14 网站建设 项目流程

干变电站调试这几年,我有一半的时间都耗在规约沟通上。早期做Modbus点表,双方对着Excel来回核对,改一个点要重新下装;后来开始接触IEC61850,第一次抓MMS报文时对着16进制愣了半天——一个读取遥测的动作,封包居然能嵌套五层。等真正把MMS协议跑通、把Wireshark里的字节和标准文档逐条对上,我才意识到,规约这层窗户纸捅破之后,后面的路会顺很多。

这篇博文要做的,就是把这层窗户纸也帮你捅破。我会用开源库libIEC61850搭一套能真实运行的MMS通信环境,用Wireshark从物理字节层面把一次读数据、写数据、主动上报的完整过程拆开看,还会把自己调试过程中踩过的一些坑和排查思路整理出来。适合正在做电力自动化、继保装置调试、规约开发的朋友,也适合刚开始接触IEC61850、对MMS一脸懵的同学——只要会敲基本命令,就能跟着把流程跑起来。

1. 把IEC61850里的MMS位置搞清楚:三层映射与通信栈

1.1 为什么61850偏偏选了MMS

IEC61850标准族里,通信服务映射部分(Part 8-1)把MMS定义为应用层协议。很多初学者第一反应是:既然都定义了一套完整的信息模型和抽象服务(ACSI),为什么不直接从零写一套协议,非要套用MMS?

原因在于,对于变电站自动化这种对可靠性、实时性、互操作性要求都极高的场景,完全不必要重造轮子。MMS(Manufacturing Message Specification,制造报文规范)是ISO 9506定义的工业自动化应用层协议,早在IEC61850出现之前就已经在工业现场跑了多年,对象模型成熟、大量厂商支持、工具链完善。IEC61850真正聪明的地方在于:它自己定义了一套抽象通信服务接口(ACSI)和对象模型,然后再通过SCSM(特定通信服务映射)把这套抽象模型映射到MMS上。换句话说,IEC61850负责“讲什么”,MMS负责“怎么讲”。

这个分层最大的价值在工程实践上:无论站控层后台是南瑞的还是国电南自的,只要双方都按IEC61850的SCL文件描述模型、用MMS完成映射,就能互认。你不用关心对方后台内部怎么存储数据,只需要关心MMS链路上报文的规范和语义。这也是为什么验收抓包时,测控装置和监控后台联调,抓到的往往就是MMS报文。

1.2 通信栈的完整路径:MMS怎么一层层踩到TCP上

再看底层传输,MMS并不是直接裸跑在TCP上的。完整的IEC61850-8-1通信栈从下到上依次是:

  • TCP(传输层,端口102)
  • TPKT(RFC 1006,在TCP上承载OSI传输层PDU)
  • COTP(ISO 8073,OSI传输层协议)
  • MMS(ISO 9506,应用层协议)

很多人抓包时看到Wireshark里先是三条TCP握手,然后出现一个COTP的Connect Request包,再往后才是MMS的Initiate-Request,就有点懵。这其实是标准的OSI over TCP封装过程。现场工程中,站控层与IED之间的MMS通信几乎都不走完整的OSI七层协议栈,而是采用RFC 1006的方式,把OSI传输层PDU封装到TCP段里,再通过TCP的102端口传输。

这个链路有一个非常实用的小知识:由于底层是TCP,MMS通信天然具备可靠传输、流量控制和拥塞控制。数据不丢包、不乱序,应用层可以相对专注于服务语义。但代价是实时性不如GOOSE那种直接映射到以太网链路层的机制,所以IEC61850里跳闸类、闭锁类信息走GOOSE,遥测、遥信、定值、文件等对实时性要求没那么极端但对完整性要求高的服务走MMS。你要是拿MMS去传跳闸命令,响应时间和网络拥塞问题会让你想哭。

1.3 IEC61850对象模型到MMS对象的翻译对照

真正要上手编码和抓包前,必须先建立一张“翻译表”。IEC61850里那些逻辑设备、逻辑节点、数据对象,到了MMS世界里全部变成一个个MMS对象。我整理了一份最常用的对照关系:

IEC61850概念MMS概念说明
Logical Device(逻辑设备)Domain(域)一个逻辑设备映射为一个MMS Domain,通常以LDName命名
Logical Node(逻辑节点)Domain内的一组命名变量逻辑节点实例用LDName/LNName作为前缀,数据对象按分层的规则拼接成变量名
Data Object / Data AttributeNamed Variable(命名变量)例如MMXU1.A.phsA.cVal.mag.f对应MMS里的一个浮点变量
DataSetNamed Variable List(命名变量列表)数据集就是预先定义好的变量集合,供报告和采样使用
Report Control BlockMMS域内的RCB对象 + InformationReport服务报告上送使用MMS的InformationReport(信息报告)
Control Block(如跳闸控制)MMS域内的DO / SBO相关对象遥控操作在MMS侧体现为对控制块内各个属性变量的读、写操作

这个翻译规则直接决定了你在Wireshark里看到的MMS对象路径。比如后台读某个间隔的有功功率,MMS Read请求里出现的对象名是DistalLD/MMXU1.TotW.mag.f这样的形式,本质就是IEC61850对象引用在MMS命名空间里的投影。编码时,libIEC61850会在API里直接使用IEC61850风格的对象路径,底层自动帮你映射成MMS命名,但抓包分析时你要能两头对上。

2. 环境搭建:没有真实IED也能学的完整模拟方案

2.1 硬件与仿真环境的两种选择

学习MMS通信最大的门槛其实是设备。一台真实的IED(智能电子设备)动辄几万块,不是人人都能随时摸到。就算有设备,你也不方便反复折腾它的通信参数。所以我把实验方案分成两条路:

方案A:真实IED + 笔记本直连。这个适合有现场资源的工程师。把笔记本网口和IED接到同一个交换机或直连,配好同网段IP,然后用Wireshark抓MMS报文。优点是报文完全真实,缺点是IED的配置文件生成、下装、重启这一套流程比较繁琐,每次修改模型都要重新下装。

方案B:纯软件仿真。在电脑上跑libIEC61850开源库,一台机器既是IED又是客户端,甚至可以把IED服务和客户端都跑在同一台电脑上,用回环接口抓包。这个方案零成本、可重复、能随时改代码,特别适合学习阶段。这篇博文采用方案B,因为它能让你在半小时内把完整链路跑起来,之后再上真机就只是替换环境的问题。

2.2 libIEC61850快速上手

libIEC61850是目前用得最多的开源IEC61850实现,由慕尼黑工业大学的MZ Automation团队维护,支持MMS、GOOSE、SV等核心服务,代码是用C语言写的,提供了Server和Client两套API。它在GitHub上可以直接下载,也提供了Windows和Linux的编译方式。

我这里以Linux环境为例。克隆源码后,进入examples/server_example_basic目录,里面已经有一个现成的模拟IED:

git clone https://github.com/mz-automation/libiec61850.git cd libiec61850 mkdir build && cd build cmake .. make -j4

编译完成后,在examples/server_example_basic目录下会生成一个可执行文件,直接运行,默认监听TCP 102端口,IED的IP是0.0.0.0(本机所有网卡),模型是simpleIOGenericIO这个逻辑设备。终端打印输出类似:

IED server started on port 102

可以在另一个终端用nmap -p 102 127.0.0.1验证端口已经监听,或者用telnet 127.0.0.1 102(不输入内容)也能看到TCP连接建立。这一步就相当于你已经有了一台“模拟IED”。

2.3 Wireshark的MMS协议解析配置

Wireshark本身内置了MMS解析器,但有个前提条件:它需要正确识别底层是COTP/TPKT封装。默认情况下,如果是标准的102端口,Wireshark会自动识别;但如果某些特殊环境里端口不是102,或者你在LibIEC61850里改了监听端口,就需要手动指定解码规则。

具体操作:在Wireshark里抓到TCP包后,选中一个TCP报文,右键 -> 解码为(Decode As)-> 传输层/应用层,把TCP端口102指定为COTP。设置完成后,Wireshark会按COTP协议去解析TCP负载,然后自动识别上层MMS。如果你用的是自定义端口,也可以在这里选RFC 1006COTP,关键让Wireshark能进入OSI over TCP的解码路径。

另外一个很实用的小技巧:Wireshark支持按协议名过滤。但在MMS关联建立之前,你只能看到TCP和COTP报文。等MMS关联建立后,过滤表达式mms就会生效,直接只看MMS层。如果你希望过滤某个MMS服务类型,比如只看Read请求响应,可以用mms.confirmedRequestPDU配合mms.confirmedResponsePDU来过滤。这些表达式在后续抓包分析时高频使用。

3. 手把手跑通一个MMS通信实例

3.1 从一份最小模型开始:把SCL文件里的信息“喂”给服务器

先别急着写代码。IEC61850的世界里,一切通信内容都基于信息模型,而信息模型在工程中用SCL(Substation Configuration Language)文件描述。libIEC61850的server_example_basic里已经内置了一份静态生成的模型,直接用就可以,但为了后面抓包看得明白,我建议你动手改一个最小模型试试。

这里用一个极简的逻辑设备模型,包含一个逻辑节点LLN0作为公共节点,再有一个MMXU1作为测量节点:

// 伪代码示意,实际用libIEC61850的模型API创建 LogicalDevice* lDevice = LogicalDevice_create("simpleIOGenericIO", model); LogicalNode* lln0 = LogicalNode_create("LLN0", lDevice); LogicalNode* mmxu1 = LogicalNode_create("MMXU1", lDevice);

其中MMXU1下会有A(电流)、W(有功功率)等数据对象,每个数据对象下面又有phsAcValmagf这些数据属性。这一层层的嵌套,在MMS报文里会变成一个完整的MMS变量路径。你先记住这个规律:IEC61850对象引用路径“/”层的点号层级,在MMS中会转换为用$分隔的MMS标识符。

3.2 服务端实现:让模拟IED真正能应答读写

libIEC61850的Server API核心就几行:创建模型、创建IedServer对象、启动服务。我截取一个关键示例:

#include "iec61850_server.h" #include "hal_time.h" #include <stdio.h> int main(void) { // 创建IED服务器实例,模型使用编译期生成的external model IedServer iedServer = IedServer_create(&iedModel); // 启动服务器,监听所有网卡的102端口 IedServer_start(iedServer, 102); if (!IedServer_isRunning(iedServer)) { printf("Server start failed!\n"); return 1; } printf("IED server running on port 102\n"); while (1) { Thread_sleep(1000); // 模拟遥测值变化,便于测试信息报告上送 IedServer_lockDataModel(iedServer); float value = (float) (50.0 + (rand() % 10) / 5.0); IedServer_setFloatValue(iedServer, (IedServer_getLogicalNode(iedServer, "MMXU1")) ? IED_MODEL_MMXU1_A_phsA_cVal_mag_f : NULL, value); IedServer_unlockDataModel(iedServer); } IedServer_stop(iedServer); IedServer_destroy(iedServer); return 0; }

这个代码的核心是:服务器内部维护了一个数据模型,客户端通过MMS Read来读取当前值,通过MMS Write来写入数据。启动以后,它就挂在102端口等着被调用。为了后面抓包能看到上送信息,我还在循环里改了MMXU1.A.phsA.cVal.mag.f的值,这样一旦使能了报告控制块,客户端就能收到InformationReport。

3.3 客户端实现:连接、读遥测、写遥控值

客户端用libIEC61850的Client API,逻辑也很直白:

#include "iec61850_client.h" #include <stdio.h> int main(void) { IedConnection con = IedConnection_create(); // 用TCP连接服务器 IedConnection_connect(con, "127.0.0.1", 102); if (con->state != IED_CONNECTION_STATE_CONNECTED) { printf("Connection failed\n"); IedConnection_destroy(con); return 1; } printf("Connected to server\n"); // 读取遥测值:MMXU1.A.phsA.cVal.mag.f MmsValue* value = IedConnection_readObject(con, "simpleIOGenericIO", "MMXU1/A.phsA.cVal.mag.f", IEC61850_FC_MX); if (value != NULL) { printf("A magnitude (f): %f\n", MmsValue_toFloat(value)); MmsValue_delete(value); } // 写入一个开关位置:GGIO1.SPCSO1.stVal(示例,需要模型里有这个点) IedConnection_writeObject(con, "simpleIOGenericIO", "GGIO1/SPCSO1.stVal", IEC61850_FC_ST, MmsValue_newBoolean(true)); // 断开连接 IedConnection_close(con); IedConnection_destroy(con); return 0; }

运行这个客户端,终端会打印读到的遥测值。这里有个非常容易误解的地方:IedConnection_readObject里传的第二个参数是逻辑设备名,第三个参数是LNName.DataObject路径,但底层会拼成MMS可解析的完整路径。如果你传错了逻辑设备名,或者把路径层级写错,MMS返回的就是ObjectAccessDenied或ObjectNonExistent,这在现场是非常典型的错误。

3.4 验证通信:三端同步确认

跑起来以后,建议开三个终端:一个跑server,一个跑client,一个开Wireshark在loopback接口上抓包。抓包后先别急着看内容,直接观察报文数量:一次完整的连接+读+写,应该有大约20-30个TCP段,其中MMS应用层报文不足10个。这个比例基本展示了MMS协议的“真实体积”:底层TCP握手、确认、窗口管理消耗了相当一部分报文。

我建议你把抓包保存为pcapng文件,后面每一步分析都基于这份现场报文来对照。我在第四章用的就是这类实测报文的结构和顺序。

4. Wireshark抓包:把MMS报文拆开看

4.1 过滤规则:先抓到对的那几包

抓包时,如果server和client都在本机,选loopback(lo)接口。启动抓包后再运行client,就能看到完整交互。为了聚焦MMS报文,过滤栏用:

tcp.port == 102

先看全部102端口流量。等到MMS关联建立后,可以用:

mms || cotp

把MMS和COTP报文一起显示,这样能直观看到TCP之上COTP和MMS的层次关系。不要只过滤mms,因为会漏掉COTP层的连接建立过程,而COTP连接建立恰恰是很多人看不懂的“额外握手”。

4.2 一次完整会话的报文时序:从TCP握手到InformationReport

我截取一次真实的会话过程,把关键报文列成一张表:

序号源->目的协议关键信息
1Client -> ServerTCPSYN,握手开始
2Server -> ClientTCPSYN+ACK
3Client -> ServerTCPACK
4Client -> ServerCOTPCR(Connect Request),TPKT包
5Server -> ClientCOTPCC(Connect Confirm)
6Client -> ServerMMSInitiate-Request,携带MMS版本、协商参数
7Server -> ClientMMSInitiate-Response
8Client -> ServerMMSConfirmed-Request:Read(读取)
9Server -> ClientMMSConfirmed-Response:Read(返回值)
10Client -> ServerMMSConfirmed-Request:Write(写入)
11Server -> ClientMMSConfirmed-Response:Write(返回成功)
12Client -> ServerMMSConfirmed-Request:GetNameList(可选)
13Client -> ServerTCPFIN/ACK,连接关闭

注意第4、5两步:这是OSI传输层的连接建立(COTP Connect Request/Confirm),它发生在MMS关联之前。很多教程只讲TCP和MMS,忽略了COTP,导致新手看到COTP包就以为MMS出问题了。其实COTP建立成功后才能承载MMS的Initiate。

后面第6、7步才是MMS层的“握手”,用来协商MMS协议版本、最大报文长度、支持的服务等参数。第8-12步是真正的业务请求。信息报告(InformationReport)不会出现在这个会话里,因为它是服务端主动推送的,必须等报告控制块被使能后才会触发。为了看到它,你可以在client连接后,先对数据集使能报告(RptEna置true),然后服务端那个循环改值的代码就会触发InformationReport主动上送。

4.3 字节级拆解:用一次Read请求看穿MMS的TLV编码

Wireshark能把MMS报文解码成结构化的字段,但你要真理解协议,还是要看原始字节。MMS的PDU编码遵循ASN.1的BER(Basic Encoding Rules)规则,基本单元是TLV三元组:Tag(类型)、Length(长度)、Value(值)。Wireshark的“Bytes”面板里能看到完整hex原始数据。

我用一个实际Read请求包的关键部分来说明。抓包里某个MMS Confirmed-Request PDU的hex长这样(此处为演示略作简化):

03 00 00 48 02 F0 80 00 01 00 00 00 00 0A A0 25 02 01 01 A1 20 60 1E A0 1C 30 1A 04 0E 73 69 6D 70 6C 65 49 4F 47 65 6E 65 72 69 63 49 4F 04 08 4D 4D 58 55 31 2E 41 2E ...

我逐层拆解给你看:

  • 03 00 00 48:TPKT头。03是常量版本号,00保留,00 48(十进72)表示整个TPKT包长度为72字节。
  • 02 F0 80:COTP头。02表示头长度2字节,F0是DT(Data)TPDU类型,80是EOT标志。
  • 接下来的00 01 00 00 00 00 0A是MMS的关联ID(或一部分包装字段,具体看实现)。
  • A0 25:第一个MMS应用层TLV,TagA0表示Confirmed-Request PDU(上下文标签0),0x25(十进37)表示后面Value区占37字节。
  • 02 01 01:Integer类型TLV,值是01,这是invokeID(调用编号)。
  • A1 20:上下文标签1,表示请求内容。后面跟的60 1E就是ASN.1的ReadRequest结构。

这套逐层解析的方法看着繁琐,但在现场排查时极其有用。因为Wireshark有时会因为解析器版本问题把字段解析错位,你拿原始hex和标准文档核对,才能确认到底是设备发了非标报文,还是Wireshark误判。

4.4 InformationReport:设备主动上报是怎么出现的

InformationReport是MMS中唯一一个设备不经过请求就能主动发给客户端的服务,对应IEC61850里的报告上送机制。线上运行中,遥控操作后的遥信变位、模拟量越限等,都是通过InformationReport即时推送的。

抓包时你会看到服务端发起一个TCP数据段,里面MMS PDU的类型是InformationReport(Tag通常是A6上下文标签6)。注意,它不需要invokeID,也不需要客户端回应确认——实际上MMS协议也不要求对InformationReport做应用层确认,可靠性完全依赖底层TCP。这又验证了一件事:MMS对TCP的依赖不是可有可无的,而是设计上就靠TCP来保证不丢包。

如果要定位某个数据集的报告报文,可以看InformationReport里携带的数据集名称和对象列表。正常情况下,使能报告后,服务器按缓存报告里设定的触发条件(数据变化、品质变化、数据刷新等)在对应时机发出InformationReport。如果收不到,就要回头检查报告控制块的触发条件配置和数据集引用是否一致。

5. 实战中必然踩的坑:从连不上到数据错位的排查链路

5.1 第一层排查:TCP都连不上,先别怪MMS

现场联调最常见的现象是客户端报连接失败。第一步不要看MMS,先看TCP通不通。命令行里telnet <IP> 102能通,说明TCP没问题;如果不通,查IP地址、子网掩码、网关和交换机的VLAN配置。Wireshark里如果连TCP三次握手都看不到或只有SYN没有SYN+ACK,那就说明数据包根本没送到对端,或者被防火墙拦了,别打开MMS解析浪费时间。

另外一个隐蔽坑:多网卡机器上,服务端绑定的是0.0.0.0还能接受任意网卡,但如果服务端绑定了具体IP,客户端连另一个网卡IP就会失败。现场IED可能有多个网口,管理口和过程层口IP不能混用。这类问题你可以在Wireshark里看到TCP重传或RST包。

5.2 第二层排查:TCP通了但MMS关联失败

TCP握手正常,COTP连接的CR/CC也正常,但MMS Initiate-Request发出去后,服务端回了Initiate-Response里的协商结果包含错误,或者直接发了Reject,这就需要看MMS的错误原因。常见的有版本不匹配、不支持的服务类型、超出最大报文长度限制等。

libIEC61850里如果server端模型不支持某个服务,它会在MMS层返回ServiceUnsupported的响应。这时候抓包看响应报文中的reject reason字段,那个错误码可以直接查MMS标准附录。很多时候是因为server端没有使能对应功能的MMS服务映射,比如没有使能GetNameList或者没有开放文件传输。这些在IedServer启动配置里都有开关。

5.3 第三层排查:路径引用和数据模型不一致

这是我遇到最多的一类问题。客户端连上了,关联也建立成功,但读数据时报ObjectNonExistent或读出来的值是空。根本原因是MMS对象路径和Server实际模型不匹配。IEC61850的对象路径大小写敏感,一点都不能错。比如MMXU1.A.phsA.cVal.mag.fMMXU1/a.phsA.cVal.mag.f(注意大小写和斜杠位置)在MMS里就是完全不同的变量。

排查时有一个高效的办法:在客户端调用IedConnection_getNameList,把Server里某个Domain下的所有变量列表拉出来,看看实际对象名是什么。libIEC61850的客户端例子goose_clientmms_client里都有获取名称列表的调用,可以直接参考。对照实际对象名和代码里读的对象名,很快就能发现是多了还是少了中间某一段,比如把cVal写成了cval

还有一个高频错误:把IEC61850的数据集(DataSet)路径当成普通变量去读。数据集在MMS里是Named Variable List,不是单一的Named Variable,需要用专门的读数据集操作,用IedConnection_readMultipleObjects或者直接读数据集对象的引用。用读单变量的API去读数据集,肯定读不出来。

5.4 Wireshark显示“看不懂”的常见原因

有时你会发现Wireshark明明抓到了102端口的包,却显示为Data而不是COTPMMS。这通常是解码器没识别成功。解决方式前面提过:选中报文,右键“解码为”,手动把TCP端口指定为COTP。如果还不行,检查一下抓包长度设置:有些网卡默认抓包长度(snaplen)不够大,大报文被截断,Wireshark解析NM_MMS的TLV时长度字段超出捕获长度,就会放弃解析。在Wireshark的捕获选项里,把“限制每个包的长度”设成65535字节或留空,基本能解决。

还有一类情况:MMS报文较长时,TCP会分片传输,Wireshark默认会做TCP重组,但如果抓包时丢了一个分片,重组失败,整个MMS报文就变成乱码。此时检查Wireshark底部状态栏有没有显示“TCP segment of a reassembled PDU”或“Unreassembled”字样。如果是服务器或者客户端的发送缓冲区设置不合理导致频繁分片,可以调整栈的MSS或应用层报文长度配置,但这属于网络调优范畴,先确认抓包无误再说。

5.5 信息报告不触发的典型原因

前面提到,服务端主动上报依赖报告控制块(RCB)使能。现场调试时经常遇到:客户端已经把RptEna置为true,但设备数据变化后就是没有InformationReport。原因是RCB里还有两个关键参数,一个是数据集引用(DatSet),一个是触发条件(TrgOp)。数据集引用必须指向一个已经定义好的数据集,如果数据集不存在或引用错位,设备根本不知道该上报什么。触发条件则决定了哪些事件能触发上报,常见配置数据变化触发或品质变化触发,你只置RptEna但TrgOp是0,设备同样不会上报。

另外,MMS的Report是带缓存机制的。如果服务端数据变化频繁,而客户端来不及接收,消息会积压在缓存里。调试时建议把触发条件先设为“数据变化”一种,频率不要太高,确保每条报告能对应到一次明确的数据变化上。我见过很多新手栽在这里:RptEna也置true了,数据集也对,但触发条件没设,数据死活不上送,抓包抓来抓去都是空。

最后分享一个调试习惯

从我自己的经历看,MMS协议调试最大的陷阱不是协议本身,而是“你以为你发的东西和实际发出去的东西是一致的”。所以每次联调,我都习惯性地先开Wireshark留底,哪怕刚开始不分析,等出问题再回看,往往比瞎猜快得多。还有一个更具体的习惯:把抓到的MMS报文里的对象路径复制到文本编辑器里,和SCD文件里的IED模型逐字对照,大多数数据读不到的问题都是这个环节抓出来的。

另外给刚入门的朋友一个建议:如果你已经把libIEC61850的例子跑通,下一步不要急着上复杂模型,先从一个点(比如一个遥测值)开始,自己写client,自己在Wireshark里找到对应的Read和Response报文,再去看InformationReport。把这个最小闭环吃透,再到现场面对真实IED,你会发现除了设备型号变了,通信流程几乎完全一样。剩下的就是经验和耐心了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询