资讯详情

资讯详情

IPMItool源码解析:从接口抽象到BMC诊断命令扩展

简介IPMItool 源码包是一份面向服务器运维工程师、系统管理员及BMC嵌入式开发者的开源工具源码用于通过IPMI协议对BMC进行远程监控、传感器数据读取与电源管理。资源共171个文件压缩包仅752KB以50个头文件和49个C源文件为核心辅以configure脚本、Makefile.in、automake模板及各类平台配置文件目录覆盖命令处理、IPMI协议实现、LAN/串行通信、SDR传感器记录解析等模块结构清晰便于按需研读。已有551人学习下载。读者可从中深入理解IPMI协议栈的实现思路、命令框架、BMC通信机制及安全认证流程既能帮助系统管理员更高效地使用ipmitool排查硬件故障也便于开发者在此基础上新增自定义命令或针对特定服务器硬件进行功能定制与性能优化。1. IPMItool源码用了两年ipmitool你也未必真见过它的全貌用了两年ipmitool的运维多半没见过这个工具长什么样。它在Linux服务器上敲几个参数就能读BMC传感器、改IPMI用户口令、拉起SOL串口重定向但源码细看却不复杂——真正撑起体量的是五类传输接口的适配和各种BMC私有命令的拼装。IPMItool源码解决的核心问题。适合三类人要裁剪固件体积的嵌入式工程师、排查带内带外通道争用的运维以及想搞懂IPMI报文构造的后端开发者。很多人把它当黑匣子用直到BMC死机、SOL断连、写FRU失败时才愿意回头翻源码。这篇就按“读结构→编译→找协议落点→踩坑→扩展”的顺序拆给你看。2. 源码目录与接口抽象从ipmi_main.c到LAN/KCS物理通道想在BMC固件里移植或者裁剪IPMItool第一步不是读代码而是先看懂它的目录和编译骨架。整个项目的模块划分很直白一个命令对应一个ipmi_xxx.c接口定义集中在ipmi_intf.c底层传输封装在各自的接口文件里。明白了这个布局后面看什么功能都能按图索骥。2.1 根目录模块划分哪些子目录才是真正值钱的解开源码包后第一件事是先看顶层结构。不同发行版会有些差异但常见布局基本一致# 在解开的源码包根目录下执行 ls -F输出里值得重点关注的目录和文件有这些configure.ac、Makefile.amautotools 构建脚本交叉编译时改这里src/核心代码ipmitool 全部功能实现都在此include/公共头文件包含 IPMI 协议常量与结构体lib/工具内部自带的辅助库如 MD5、SHA1、AES 等算法实现test/测试脚本与模拟器相关文件src/目录按功能拆得很细ipmi_sel.c系统事件日志、ipmi_sensor.c传感器读数、ipmi_user.c用户管理、ipmi_channel.c通道配置、ipmi_fru.cFRU 读取、ipmi_sol.c串口重定向、ipmi_raw.c裸命令发送、ipmi_mc.cMC 复位与信息查询。新人最容易犯的错是直奔ipmi_main.c逐行读看半小时也找不到命令实现。正确顺序是先看命令行分发再顺藤摸瓜找到对应功能的.c文件。编译骨架走的是 autotools 流程configure.ac定义检查项Makefile.am声明源文件最终由 automake 生成 Makefile。在源码根目录跑一次./bootstrap就能生成 configure 脚本我这里习惯先把生成的文件检查一遍避免后续交叉编译时被旧配置缓存坑到。2.2 接口抽象层LAN/KCS/Serial 三路通道的源码位置IPMItool 最大的设计优点是把“命令”和“传输载体”解耦。命令代码里只看到struct ipmi_intf *intf这个指针真正干活的是藏在接口层下面的传输函数。理解这一点才会明白为什么同一套命令能同时跑在带外局域网和带内系统总线上。接口名源码文件底层通道典型场景LANsrc/ipmi_lan.cUDP 623 端口RMCP 明文旧设备带外管理LANPLUSsrc/ipmi_lanplus.cRMCP 加密会话跨网段远程管理最常用KCSsrc/ipmi_kcs.c系统管理中断寄存器访问本机带内查询SERIALsrc/ipmi_serial.cUART 串口BMC 串口调试OPENIPMIsrc/ipmi_openipmi.c内核 ipmi_si 驱动复用内核通道避免地址冲突KCS 接口值得单独留意。它走的是系统管理总线通过系统管理中断进入 BMC和 LAN 没关系。很多同事在本地敲ipmitool sensor list超时第一反应怀疑网络实际是ipmi_si内核模块或 BIOS 占用了同一个 I/O 端口两者争用导致命令卡死。接口抽象层的核心数据结构是struct ipmi_intf里面绑定了发送、接收、关闭三个函数指针。新增一种物理传输通道只需要填好这个结构体不需要动任何协议命令代码。2.3 从 ipmi_main.c 看一条命令从 argv 到报文命令分发逻辑集中在src/ipmi_main.c的ipmi_cmd数组里。每一条命令注册了名字、帮助文本和执行函数匹配成功后把剩余参数交给对应函数处理。看下面这段精简逻辑就能理解整个分发链路/* src/ipmi_main.c 中的命令分发骨架 */ static struct ipmi_cmd ipmi_cmds[] { { sensor, list, NULL, cmd_sensor }, { sel, list, NULL, cmd_sel }, { raw, raw command, NULL, cmd_raw }, { mc, mc info, NULL, cmd_mc }, { NULL, NULL, NULL, NULL } }; /* 主循环里核心匹配逻辑 */ for (cmd ipmi_cmds; cmd-name ! NULL; cmd) { if (strncmp(argv[0], cmd-name, strlen(argv[0])) 0) { cmd-func(intf, argc, argv); /* 命中后调用对应实现函数 */ break; } }分发完成后具体实现函数负责把请求参数填充到struct ipmi_rq里再调用ipmi_intf_sendrecv()发送到 BMC/* 典型命令实现的报文填充方式 */ struct ipmi_rq req; memset(req, 0, sizeof(req)); /* 必须清零否则会带垃圾字段 */ req.netfn IPMI_NETFN_APP; /* 0x06应用处理器网络功能号 */ req.cmd IPMI_CMD_GET_BMC_GUID; /* 0x48取 BMC GUID */ req.data_len 0; /* 无请求数据长度必须显式设为 0 */ ipmi_intf_sendrecv(intf, req, rsp);ipmi_intf_sendrecv()是统一收发入口。参数intf代表当前激活的通信接口句柄。data_len 0这里常被忽略刚接触源码的人不初始化结构体就直接发送BMC 会收到一段栈上残留的垃圾数据返回 0xC1 未指定错误排查半天才发现是结构体没清零。我一般会顺手检查ipmi_intf.c里的报文发送路径确认裸命令、会话加密、重试机制都走同一条链路再继续往下看具体模块。3. 编译与静态检查从configure到可复现的交叉构建源码拿到手光看不够尤其是嵌入式场景最终要把它编进 BMC 固件或者作为独立二进制部署。编译这一步坑最多的不是语法错误而是依赖库版本和交叉工具链的头文件冲突。先把本地编译跑通再做交叉编译最后用静态分析工具扫一遍隐患。3.1 本地编译的依赖边界openssl 和 ncurses 怎么选默认源码包在 x86 机器上编译很顺一条命令链走完就能得到可执行文件# 生成 configure 脚本并执行配置 ./bootstrap ./configure --enable-static --disable-ipmievd --sysconfdir/etc make -j4--enable-static让编译产物尽量静态链接部署到别的机器时不用背着动态库跑。--disable-ipmievd是去掉事件守护进程纯命令行工具不需要这个常驻进程。--sysconfdir/etc把配置文件固定到 /etc 目录后续改配置不依赖环境变量。本地编译的两个关键依赖是 openssl 和 ncurses。openssl 用于 LANPLUS 的加密认证与 SOL 会话数据加密ncurses 只在某些交互界面使用。如果你只想做带内 KCS 调试./configure --disable-lanplus可以直接绕开加密依赖省掉不少链接问题。检查依赖是否齐备用pkg-config --modversion openssl看一眼版本。openssl 1.1 与 3.x 的 API 差异会直接影响编译报错集中在EVP_*系列函数和HMAC_*接口上。3.2 交叉编译BMC固件场景的configure参数BMC 上的工具链比 x86 苛刻得多。很多 BMC 跑的是 uClibc 或 musl 系统glibc 的动态链接链在板子上经常缺符号。交叉编译时最省心的做法是把校验库全部静态编进去# 以 arm 32 位 BMC 工具链为例 CC/opt/toolchain/bin/arm-linux-gnueabihf-gcc \ CFLAGS-O2 -mcpucortex-a9 \ ./configure --hostarm-linux-gnueabihf --buildx86_64-pc-linux-gnu \ --enable-static --disable-ipmievd make -j4--host声明编译产物的运行平台--build声明当前编译机平台。交叉编译时 configure 会尝试运行一些测试程序判断特性在交叉环境下这些检测程序跑不起来常见做法是给 configure 传--with-cross或手动指定ac_cv_*变量跳过运行测试。静态链接意味着把 openssl 的 AES、MD5、SHA1 代码也打进去二进制体积会比动态版大出 200KB 左右但换来的好处是不依赖 BMC 上残缺的 ldconfig 链。如果你只需要 raw 和 sensor 两大功能可以在 Makefile 里裁剪掉 SOL 和 FRU 模块体积能再少一截。BMC 固件内部的 libc 版本很特殊老系统里常出现__aeabi_*符号缺失那是浮点 ABI 没有对齐。建议先用工具链自带的readelf -A检查目标系统的浮点调用约定再在CFLAGS里补上-mfloat-abihard或 softfp。3.3 用静态分析扫一遍源码里的隐患编译通过只是起点。源码里最容易藏雷的是 SDR 缓存处理传感器原始数据读回来之后要按字节拼成浮点值数组越界几乎是标配。GCC 的-fanalyzer可以做路径敏感分析直接扫出边界问题# 用 gcc 静态分析检查最常用的 LAN 接口代码 gcc -fanalyzer -Wall -Wextra -fsyntax-only src/ipmi_lan.c-fsyntax-only只做语法和静态检查不产出目标文件。-Wall -Wextra把告警级别拉满-fanalyzer负责做数据流分析。告警最常出现在memcpy长度参数和ipmi_flash.c的扇区地址计算上。我自己还习惯补一条 clang-tidyclang-tidy src/ipmi_sdr.c -- -I./include -I./src扫完静态分析后跑真机验证前先strace -e openat ipmitool sensor list看一眼有没有打开/dev/ipmi0失败。返回EBUSY就说明内核驱动占用了通道不用继续折腾协议了。4. 协议核心落点RMCP认证、NetFn映射与SOL配置路径IPMItool 源码真正值钱的部分不在界面交互而是对 IPMI 协议栈的实现封装。读懂了认证流程和消息号分派你就能自己拼一条裸命令也能解释为什么某些参数在真机上莫名其妙不生效。4.1 RMCP认证与密码哈希源码里的 RAKP 流转LANPLUS 接口对应 RMCP 协议它的会话建立过程被封装在ipmi_lanplus.c的lanplus_open_session系列函数里。传统 RMCP 直接明文传密码RMCP 则用 RAKPRemote Authenticated Key-Exchange Protocol做四次握手。实盘抓包时能看到四类 RAKP 消息但这个源码里看不到密码本身只有哈希值参与每一轮消息交换。步骤消息方向关键内容1RAKP Message 1客户端 → BMC客户端随机数、用户名、请求权限级别2RAKP Message 2BMC → 客户端BMC 随机数、BMC GUID、角色 ID3RAKP Message 3客户端 → BMCHMAC-SHA1 计算结果、客户端角色4RAKP Message 4BMC → 客户端最终校验结果与会话 ID在源码里搜索HMAC或hmac_sha1就能定位到具体实现。真正导致认证失败的常见原因是 BMC 侧预设的用户名权限级别和客户端请求的权限不匹配并非密码错误。你可以看到解决思路用ipmitool user list查一下用户的 privilege level再确认会话请求是否超过了该级别。4.2 NetFn 与 BMC 状态机读 SDR 和传感器走的是哪条分支IPMI 协议中每个请求都由NetFn和Command两个字段定位。NetFn 决定功能域Command 决定域内具体操作。从源码的常量定义里能看到有这些数值宏名数值功能域IPMI_NETFN_SENSOR_EVENT0x04传感器事件、温度状态IPMI_NETFN_APP0x06应用处理器状态、BMC GUIDIPMI_NETFN_STORAGE0x0ASDR 存储、FRU 读取IPMI_NETFN_TRANSPORT0x0C传输配置、SOL 参数BMC 内部本质上是一个有限状态机收到合法消息 → 鉴权 → 执行命令 → 返回响应。ipmitool raw 0x06 0x01就是直接向 NetFn 0x06 的 Command 0x01 发起请求查询 BMC 设备 ID。解析响应时返回码0xC1表示未指定错误0xCC是命令非法0xD5则是禁止操作。自己扩展源码时最常见的低级错误就是把 NetFn 和 Command 写反BMC 不会给任何解释性错误直接甩一个0xCC回来。4.3 SOL 配置从 ipmitool sol 到 BMC 串口重定向参数路径SOLSerial Over LAN是把 BMC 下挂串口的数据封装成 IPMI 消息送出去。源码位置在ipmi_sol.c对应的事务包括激活会话、设置参数、以及把 UART 数据打成 IPMI payload。具体操作命令如下# 设置 SOL 使能标志volatile 代表重启 BMC 后失效 ipmitool -I lanplus -H 10.10.1.2 -U admin -P 123456 sol set volatile-enabled true # 激活 SOL 会话 ipmitool -I lanplus -H 10.10.1.2 -U admin -P 123456 sol activate-I lanplus指定 RMCP 接口-H是 BMC 管理网地址-U和-P是用户口令。与 SOL 会话质量直接相关的参数是deq字符累积延迟和baudrate。我一般会这样设置参数含义常用配置volatile-enabled易失生效重启失效truenon-volatile-enabled持久生效truedeq系统串口字符积累延迟20 msdceBMC 到控制台输出延迟50 msdte控制台到 BMC 延迟100 ms这些数值会影响串口回显的实时性设得太小字符会被截断设得太大则出现明显延迟。SOL 断连的排查方法到第五章详细展开这里先记住一条SOL 参数有两套Volatile 和 Non-Volatile只改一边会导致重启后配置漂移。5. 源码实战避坑编译失败、BMC闪存写入与SOL断连的排查记录这章是从实际调试和移植过程里沉淀下来的几个高频问题。每条都按“现象 → 原因 → 解决”讲照着做能省掉大量查文档时间。5.1 configure 阶段找不到 libcrypto现象./configure中途退出报错error: libcrypto not found排查半天但系统里明明装了 openssl。原因大部分情况是只装了 openssl 运行库没装openssl-devel。另外一类是头文件路径不在 GCC 默认搜索路径里比如自定义安装到/opt/openssl。解决先确认库文件位置再通过环境变量补路径# 检查 openssl 头文件与库文件是否齐备 ls /usr/include/openssl/evp.h ldconfig -p | grep libcrypto # 配置时显式指定路径 ./configure LDFLAGS-L/opt/openssl/lib \ CPPFLAGS-I/opt/openssl/include如果是 BMC 老设备glibc 版本太旧导致EVP_*API 缺失那就只能把 openssl 源码一并交叉编译或者降低加密算法等级。这个问题的血泪经验是别急着改代码先确认链接器能不能找到符号90% 的undefined reference都是路径问题不是源码问题。5.2 交叉编译报 struct ipmi_msg 重复定义现象用 BMC SDK 的交叉编译器编译ipmi_dcmi.c时报错redefinition of struct ipmi_msg。原因BMC SDK 自带一套 IPMI 头文件Linux 内核的include/linux/ipmi.h也定义了同名结构体两套定义同时进入编译单元必然冲突。解决在包含 SDK 头文件之前把内核头文件的宏定义隔离起来用宏名做守卫#if defined(LINUX_IPMI_H) #undef ipmi_msg #endif #include linux/ipmi.h /* 内核头文件 */ #include sdk_ipmi.h /* SDK 自带头文件 */工程上的长期做法是直接改用src/ipmi_si.c的私有结构体绕开内核头文件。如果项目允许建议给工程里加一层统一封装头文件控制 include 顺序避免每编译一个文件就改一次宏开关。这个问题我当时花了快一天才定位到纯粹是两个头文件定义字段顺序不一致导致的内存布局错位运行时表现为随机字段乱码。5.3 ipmitool raw 写 BMC Flash 返回超时或 0xD5 禁止现象用ipmitool raw写 System Flash 时命令发送几秒后返回超时某些地址段写进去之后重启又变回原样。原因BMC 闪存有 Firmware Space 与 Data Space 两套地址映射不同厂商地址偏移不同写 Flash 前必须先进入编程模式。0xD5表示当前 BMC 的 Flash 区域处于写保护状态或者 BMC 固件处于运行态并锁定了对应扇区。解决先确认当前 BMC 固件版本再决定地址映射。常见做法是先做扇区擦除再逐块写入每次写入不超过一个块大小块与块之间加 sleep。语句如下# 关闭写保护示例命令使用前确认 BMC 支持该命令 ipmitool raw 0x32 0x71 0x00 # 按块写入块大小为 64KB写入后等待 200ms for offset in $(seq 0 0x10000 0x100000); do ipmitool raw 0x32 0x72 $offset fw_data.bin sleep 0.2 done这里写完后必须做一次读回校验。很多翻车现场都是写完没校验人工误判写入成功下次开机进不了系统。0xD5出现时先查 BMC 里的 Watchdog 是否在运行看门狗会干扰 Flash 写入流程先停掉再操作。5.4 SOL 会话激活后频繁断开字符乱码现象sol activate进去后回显正常几十秒随即断开重连重连后出现重复字符或乱码。原因BMC 串口波特率与被管理主机侧串口设置不一致或者deq延迟参数设置过小。SOL 本质上是在串口数据流上做包封装两端速率不匹配直接体现为断流。解决统一两侧波特率后把易失和持久两套 SOL 参数都配上。先查当前生效值再按推荐参数写# 查看当前 SOL 参数 ipmitool sol info # 写入两组参数保证重启后仍生效 ipmitool sol set volatile-deq 0x14 ipmitool sol set non-volatile-deq 0x14 ipmitool sol set non-volatile-baudrate 115200切记sol activate之前先停掉占用该串口的系统服务如 getty否则两边抢同一串口设备SOL 显示必然乱套。波特率不一致这个坑最迷惑人因为激活时本身不报错断开后看 BMC 日志才看到 parity error。5.5 本地 KCS 通道反复超时sensor list 卡死现象在本机执行ipmitool sensor list超时但用-I lanplus远程连同一个 BMC 却正常。原因带内通道被内核ipmi_si驱动占用或者 BIOS 在引导阶段把 KCS I/O 端口设置成了非标准地址IPMItool 访问的默认地址对不上。解决优先改用openipmi接口让 IPMItool 通过内核驱动收发# 使用 openipmi 接口而不是直接访问 KCS 端口 ipmitool -I openipmi sensor list # 如果超时来自驱动占用执行以下两步排查 dmesg | grep ipmi strace -e openat ipmitool sensor list 21 | grep /dev/ipmi看到/dev/ipmi0: Device or resource busy时需要临时卸载内核对该端口的占用。生产环境不建议卸载模块更适合的做法是改 BIOS 里的 KCS 基地址让 IPMItool 使用同一组端口参数。内核驱动通道和用户态直接访问端口本质上是两个进程争一个物理寄存器这种问题在物理机和虚拟机上的表现还不一样虚拟机里常被虚拟化层吞掉 SMI 消息直接表现为无响应。6. 进阶基于IPMItool源码扩展一条BMC诊断命令的备选路径源码解读到最后一步是把它改造成自己的诊断工具。与其凭空造新命令我更推荐先拿raw加一层壳做定制化封装。以我自己曾经实现的 BMC 固件版本检测命令为例扩展一条子命令只需要三步注册命令表、构造请求报文、解析响应。骨架代码如下/* 新增命令入口ipmitool bmcver */ static int cmd_bmcver(struct ipmi_intf *intf, int argc, char **argv) { struct ipmi_rq req; struct ipmi_rs rsp; uint8_t major, minor; memset(req, 0, sizeof(req)); req.netfn IPMI_NETFN_APP; /* 0x06命令域为应用处理器 */ req.cmd 0x01; /* GET_DEVICE_ID返回设备信息 */ if (ipmi_intf_sendrecv(intf, req, rsp) ! 0) { printf(request failed\\n); return -1; } major (rsp.data[4] 0x7f); /* 高半字节是主版本 */ minor rsp.data[5]; /* 低半字节是修正版本 */ printf(BMC firmware: %u.%u\\n, major, minor); return 0; }把cmd_bmcver注册进src/ipmi_main.c的ipmi_cmds[]数组重新执行make一条新命令就落地了。响应结构rsp.data[4]的偏移位置来自 IPMI 规范里 GET_DEVICE_ID 的数据格式如果你记不住偏移可以先ipmitool raw 0x06 0x01打印一次原始响应再对照。验证扩展命令时我的方式是先真机执行原始mc info比对版本号再用-I openipmi做带内验证最后抓包确认 RMCP 会话没有因为新增命令破坏原有认证状态。BMC 不会给你友好提示只会返回错误码。有一次我把 GET_DEVICE_ID 的 netfn 从 0x06 写成 0x0ABMC 直接回 0xCC 非法命令查了一个多小时才发现是字段错位。从那以后我每次改协议字段都会先查ipmi_intf.c里的本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →