4. IPMI消息格式:请求/响应消息结构、消息头、数据域、校验和
聊到IPMI协议,绕不开的就是它的消息格式。说白了,IPMI就是靠这些消息在管理控制器和各个设备之间来回传递指令的。你想想看,如果没有一套统一的格式,那BMC和传感器之间怎么沟通?
我个人习惯把IPMI消息比作一封信。信封上写着收件人和发件人,信纸上是具体内容,最后还得贴个邮票(校验和)确保没被篡改。嗯,咱们今天就拆开这封信,看看里面到底长什么样。
4.1 消息结构概览
IPMI消息分为两种:请求消息和响应消息。请求是管理软件发给BMC的,响应是BMC回给管理软件的。结构上它们几乎一样,只是某些字段的含义略有不同。
一个标准的IPMI消息,从底层看,包含以下几个部分:
- 消息头:包含目标地址、源地址、命令码等控制信息
- 数据域:携带具体的命令参数或返回数据
- 校验和:保证消息在传输过程中没出错
我在项目中遇到过好几次,因为消息头里的地址写错了,导致BMC死活不响应。排查了半天,最后发现是目标地址少了一位。所以啊,格式这东西,差一点都不行。
4.2 消息头详解
消息头是IPMI消息的灵魂。它决定了这条消息要去哪、从哪来、要干什么。咱们以最常见的IPMB(智能平台管理总线)消息格式为例,来拆解一下。
| 字节偏移 | 字段名称 | 长度(字节) | 说明 |
|---|---|---|---|
| 0 | 目标从机地址 | 1 | 接收方设备的地址,比如BMC的地址通常是0x20 |
| 1 | 网络功能码 + 目标LUN | 1 | 高4位是网络功能码,低2位是逻辑单元号 |
| 2 | 消息头校验和 | 1 | 仅对前两个字节做校验 |
| 3 | 源从机地址 | 1 | 发送方设备的地址 |
| 4 | 序列号 + 源LUN | 1 | 高4位是序列号,低2位是源LUN |
| 5 | 命令码 | 1 | 具体要执行的操作,比如读取传感器读数 |
这里有个关键点:消息头校验和只覆盖前两个字节。为什么?我猜是为了快速校验,毕竟头部的信息最关键。后面的数据域还有自己的校验。
网络功能码决定了这条消息的类型。常见的包括:
- 0x06:应用请求(App Request)
- 0x07:应用响应(App Response)
- 0x0A:存储请求(Storage Request)
- 0x0B:存储响应(Storage Response)
说白了,看到网络功能码的最高位是1,那就是响应消息;是0,那就是请求消息。
4.3 数据域
消息头后面跟着的就是数据域。数据域的长度是可变的,具体取决于命令码。比如读取传感器读数的命令,数据域可能只有1个字节(传感器编号);而设置用户密码的命令,数据域可能长达16个字节。
数据域的格式,在IPMI规范里叫“命令特定数据”。每个命令都有自己的数据格式定义。我建议你手边常备一份IPMI 2.0规范文档,查起来方便。
举个例子,读取传感器读数(命令码0x2D)的请求消息数据域:
字节0:传感器编号(例如 0x01 表示CPU温度传感器)
对应的响应消息数据域:
字节0:完成码(0x00表示成功)
字节1:传感器读数(例如 0x2A 表示42摄氏度)
你看,请求和响应的数据域结构完全不同。请求里只带了传感器编号,响应里却多了完成码和读数。这就是我常说的:请求要精简,响应要完整。
避坑指南:我曾经在调试时发现,响应消息里的完成码明明是0x00,但数据域却全是0xFF。后来查了半天,原来是传感器本身没初始化。所以啊,完成码为0不代表数据一定有效,还得看具体场景。
4.4 校验和
IPMI消息里有两个校验和:
- 消息头校验和:只覆盖消息头的前两个字节
- 消息数据校验和:覆盖从源从机地址开始到数据域结束的所有字节
校验和的计算方法很简单:所有字节相加,取最低8位,然后取反加1。说白了,就是让校验和加上被校验的所有字节,结果的最低8位等于0。
举个例子,假设消息头前两个字节是0x20和0x06:
0x20 + 0x06 = 0x26
0x26 取反 = 0xD9
0xD9 + 1 = 0xDA
所以消息头校验和就是0xDA
验证时,把0x20、0x06、0xDA加起来:0x20 + 0x06 + 0xDA = 0x100,取低8位就是0x00。完美!
注意:消息数据校验和的计算范围是从源从机地址开始,一直到数据域的最后一个字节。消息头校验和只算前两个字节。这两个校验和千万别搞混了,否则BMC会直接丢弃你的消息。
4.5 完整消息示例
咱们来看一个完整的请求消息例子。假设我们要读取传感器编号为0x01的温度值:
字节0: 0x20 (目标从机地址,BMC地址)
字节1: 0x06 (网络功能码=应用请求,目标LUN=0)
字节2: 0xDA (消息头校验和)
字节3: 0x81 (源从机地址,管理软件地址)
字节4: 0x00 (序列号=0,源LUN=0)
字节5: 0x2D (命令码,读取传感器读数)
字节6: 0x01 (数据域,传感器编号)
字节7: 0x?? (消息数据校验和,从字节3到字节6计算)
对应的响应消息可能是:
字节0: 0x81 (目标从机地址,管理软件地址)
字节1: 0x07 (网络功能码=应用响应,目标LUN=0)
字节2: 0x?? (消息头校验和)
字节3: 0x20 (源从机地址,BMC地址)
字节4: 0x00 (序列号=0,源LUN=0)
字节5: 0x2D (命令码,与请求一致)
字节6: 0x00 (完成码,成功)
字节7: 0x2A (数据域,温度读数42°C)
字节8: 0x?? (消息数据校验和)
你发现没有?响应消息里的目标地址和源地址,跟请求消息正好互换。这就是IPMI的地址交换原则。我刚开始学的时候老记反,后来画了个图才彻底搞明白。
4.6 知识体系结构图
下面这张图,是我自己总结的IPMI消息格式核心逻辑。你看一眼就能明白整个结构是怎么串起来的。
这张图把消息头、数据域、校验和的关系画得清清楚楚。你保存下来,以后调试时对照着看,能省不少时间。
4.7 实际调试中的注意事项
最后,分享几个我在实际调试中踩过的坑:
- 校验和算错:这是最常见的错误。我建议你写个脚本自动计算,别手算,容易出错。
- 地址搞反:请求消息的目标地址是BMC,源地址是管理软件。响应消息正好反过来。我刚开始时老搞混,后来在代码里加了注释才解决。
- 序列号不匹配:请求和响应的序列号必须一致。如果你发了请求0,响应里序列号却是1,那这条响应会被直接丢弃。
- 数据域长度不对:每个命令对数据域长度都有严格规定。多一个字节或少一个字节,BMC都会返回错误。
我的习惯:每次写IPMI消息处理代码时,我都会先用抓包工具(比如ipmitool raw命令)抓一条成功的消息,然后逐字节对照。这样能最快发现格式问题。
好了,IPMI消息格式就讲到这里。记住:消息头定方向,数据域定内容,校验和保安全。这三者缺一不可。