
简介IPMItool 是广泛使用的开源命令行管理工具通过 IPMI 协议与服务器 BMC 交互实现带外监控、传感器读取、电源控制等功能。此源码包面向系统运维工程师、嵌入式开发者和学习服务器管理的进阶用户适合深入研读 IPMI 协议实现与 BMC 通信机制。压缩包共包含171个文件以50个头文件和49个C源文件为核心辅以 Makefile.am、configure.in 等构建脚本以及 man 手册和文档整体仅752KB结构紧凑、易于阅读。源码按功能模块划分覆盖命令解析、IPMI 消息构造、LAN/串口通信、SDR 传感器数据解析、电源状态控制及安全认证等关键环节跨平台编译设计支持 Linux、Windows 及 Unix-like 系统。目前已有550人学习下载对于希望定制 IPMItool 命令、适配特定硬件或深入理解服务器带外管理原理的读者是一份难得的参考实现。 IPMItool 这套源码我前前后后翻过好几遍每次看都有新收获。它不只是运维工具箱里那个“查电源、看传感器”的命令行工具更是一套完整、紧凑、可扩展的 IPMI 协议实现范例。不管你是做服务器带外管理、嵌入式 BMC 开发还是单纯想读一份 C 语言网络管理程序的优秀源码IPMItool 都能给你不少启发。这篇内容我会从源码整体架构入手拆解它如何组织协议栈、如何封装接口、核心命令又是怎么走通的然后把我在编译定制和二次开发过程中踩过的坑一并分享出来。读完你可以拿这套思路去阅读自己的源码也可以照着补一版内部专用的管理工具。1. 源码整体架构与设计思路1.1 IPMItool 源码里到底有什么先明确一点IPMItool 的老家是 SourceForge现在代码托管在 GitHub 上继续维护。源码下载后解压目录结构不算复杂最关键的是src目录里面几乎涵盖所有核心实现。典型版本1.8.18 左右的文件大致如下ipmi_main.c主入口命令分发中心也是ipmitool进程启动后第一个执行的文件。ipmi_intf.c接口抽象层定义了struct ipmi_intf这样的核心结构体把不同的连接方式KCS、SSIF、LAN、LANPLUS包装成统一接口。ipmi_lan.c实现基于 RMCP/RMCP 的局域网接口代码量很大也是最值得读的部分。ipmi_sensor.c处理传感器读取、阈值判断对应命令是sensor list、sensor get。ipmi_sel.cSystem Event Log 的读取和清理对应sel elist、sel clear。ipmi_power.c电源控制对应power on/off/status/cycle。ipmi_chassis.c机箱级别的控制包括开机、重启、指示灯等。ipmi_sol.cSerial Over LAN 实现让你的串口控制台通过网络出现。ipmi_hpm.c、ipmi_fwum.c、ipmi_tsol.c等厂商扩展部分主要是惠普、戴尔等服务器的固件更新与配置。主程序逻辑并不复杂解析命令参数、打开连接、调用对应功能的处理函数、打印输出。难的是每一层协议细节的实现。1.2 为什么这个设计值得你读IPMItool 的工程价值在于“分层清晰”。它在ipmi_intf.h里定义了一个接口结构体类似面向对象语言里的抽象基类struct ipmi_intf { int ipmi_intf_type; char *name; void (*shutdown)(struct ipmi_intf *); struct ipmi_rs * (*sendrecv)(struct ipmi_intf *, struct ipmi_rq *); ... };所有上层命令模块比如传感器、电源、SEL拿到这个结构体之后只关心调用sendrecv函数发送一个ipmi_rq请求、拿回一个ipmi_rs响应。至于底层走的是本机 KCS 内核驱动还是网线上的 RMCP 加密包对上层完全是透明的。这种设计直接带来的好处你想新增一种传输层不需要去改电源、改传感器、改日志等一堆业务代码只需要实现一套新的ipmi_intf补上sendrecv行为即可。2. 核心协议原理与实现路径拆解2.1 IPMI 协议和它的分层模型要读懂 IPMItool 源码先要把 IPMI 的抽象层次落下来。IPMI 的全称是 Intelligent Platform Management Interface定位于“带外管理”。所谓带外就是不依赖操作系统、不依赖 CPU、甚至不依赖服务器是否开机通过一颗独立的 BMC 芯片就能管理和监控硬件。IPMI 从底层向上大致分三层传输接口层包括 KCS键盘控制器风格接口走系统总线的 I/O 端口、SMBUS、LAN网线。消息层IPMI 消息是一段命令帧包含 NetFn网络功能码、LUN、命令号、数据段。这是所有上层命令的通用封装。应用层各类具体命令比如Get Sensor Reading、Chassis Control、Get SEL Entry。IPMItool 的代码完美对应了这几层。ipmi_lan.c负责处理传输层和会话层ipmi_cmd.c或主命令模块负责把用户输入转换成具体的 NetFn/LUN/Cmd传感器、电源、用户管理这些模块则组装各自的数据负载。2.2 一条 IPMI 命令从输入到输出的完整链路拿最常用的ipmitool -I lanplus -H IP -U user -P pass power on举例源码执行路径如下ipmi_main.c解析命令行参数识别-I lanplus指的是通过 RMCP 协议走局域网。ipmi_lan.c的ipmi_lanplus_open完成 TCP/UDP 连接初始化发送 RMCP 打开会话请求完成认证和完整性校验的协商。ipmi_power.c中的ipmi_power_on构造一个ipmi_rq结构体NetFn 设为IPMI_NETFN_CHASSISCommand 设为IPMI_CMD_CHASSIS_CONTROL数据字段设为 1表示开机。sendrecv函数把这个ipmi_rq打包成网络数据包先套 IPMI 消息头再套 RMCP 会话头加密、加完整性校验送上网络。BMC 收到后执行真实电源控制操作返回响应数据包。源码拆包、解密、校验把响应填充回ipmi_rs结构体最终ipmi_power.c根据返回码打印Chassis Power Control: Up/On。每一步都有对应的状态机比如 RMCP 的认证就是几个状态之间跳转。代码里最经典的函数就是ipmi_lanplus_do_open_session和ipmi_lanplus_do_rakp前者发送打开会话请求后者处理 RAKP 消息完成身份验证。说是远程硬件控制本质其实就是一套约定的报文交互。2.3 为什么默认端口是 623IPMItool 打开 LAN 连接时默认端口 623。这不是随便定的623 是 IANA 分配给 RMCPRemote Management Control Protocol的端口。BMC 的网卡等管理控制器会持续监听 UDP 623 端口等待管理端的控制报文。你在代码里会看到IPMI_LAN_PORT这个常量它就是 623。UDP 而是不是 TCP这是很多人的疑问。RMCP 早期设计用 UDP 是为了轻量和无状态但后来 RMCP 为了解决可靠性问题增加了很多会话机制包括超时重传、序列号管理等但底层仍然保持 UDP。IPMItool 的发送函数ipmi_lan_send_pkt底层用的是sendto接收用recvfrom这些都是典型的 UDP socket 操作。3. 网络接口源码精读LAN 与 LANPLUS 的差异3.1 直接看代码前先把概念分清-I lan和-I lanplus是 IPMItool 里最常见的两个网络接口选项它们对应协议完全不同lan使用老的 RMCP 协议认证方式主要靠rmcp_authentication_type传输的数据不加密。老设备兼容性可以但安全性很差。lanplus使用 RMCP 协议支持完整的 RMCP 打开会话、RAKP 认证、AES 数据加密和 HMAC 完整性校验。现代服务器都推荐这种。源码里实现这两套逻辑是不同的文件或不同函数分支。lanplus 是代码库中最庞大的一块涉及加密库调用、会话密钥生成、包序号维护、重传机制等复杂度比 lan 高出不少。3.2 lanplus 握手流程在源码中的对应关系RMCP 的建立过程大概是这些消息交互Open Session Request客户端发随机数告诉 BMC 自己支持哪些认证算法、完整性和加密算法。Open Session ResponseBMC 从客户端建议的算法里选一套返回会话 ID、BMC 的随机数和参数。RAKP Message 1客户端再次提供随机数、用户名和摘要数据。RAKP Message 2BMC 验证客户端的身份返回自己的摘要和授权结果。RAKP Message 3客户端确认回复。RAKP Message 4BMC 返回完整握手成功消息。这些步骤对应ipmi_lan.c或ipmi_lanplus.c里的几个关键函数。我初读代码时容易一头雾水后来我是这样梳理的每次发送和接收都看成一个“请求-响应”对把状态变量拉出来对照协议规范文档比如IPMI 2.0 Specification的 13.28 节代码里的字段名和文档里的表能一一对上。你只要完整走通一次握手流程对 IPMI 网络协议的理解会质变。3.3 包结构拆解头部、载荷与认证抓包或读源码的时候IPMItool 构造的 lanplus 数据包在内存里是分段的。ipmi_lan_message结构体大概把这些信息组织在一起RMCP 头版本号、保留位、消息类型。IPMI 会话头认证类型、会话 ID、序列号。IPMI 消息本身NetFn/LUN、命令、数据。完整性校验数据如果是 lanplus通常在消息最后附加 HMAC 结果。从代码层面看ipmi_lan_build_rq这类函数负责把ipmi_rq转化成ipmi_lan_message加密、算摘要最后ipmi_lan_send_pkt丢到 socket。响应侧则是ipmi_lan_parse_rp负责反向拆包。如果想定制网络层行为比如加打印、改超时重传策略核心入口就在这几个函数里。4. 源码编译、定制与二次开发实操4.1 编译环境准备和源码获取IPMItool 不是特别新的项目编译依赖不多但有几个坑要注意。典型依赖包括OpenSSL 开发库编译 lanplus 加密功能必需libncurses5-dev可选用于 HPM 更新界面make、gcc 基础工具链在 Debian/Ubuntu 系统里我会先把依赖装上sudo apt-get install build-essential libssl-dev libncurses-dev在 CentOS/RHEL 系统里对应的是sudo yum groupinstall Development Tools sudo yum install openssl-devel ncurses-devel源码下载目前主流的做法是到 GitHub 仓库ipmitool/ipmitool拉取最新代码git clone https://github.com/ipmitool/ipmitool.git cd ipmitool4.2 开始编译configure 与 make 的细节IPMItool 使用 autotools 体系标准的编译流程如下./bootstrap ./configure --with-internal --enable-static make -j$(nproc)多提一句--with-internal这个选项允许源码内置所需的 libipmi 相关实现适合系统里缺少某些依赖环境的场景。--enable-static会生成静态链接的二进制方便拷贝到目标机器执行很实用。如果用系统自带的工具链直接make报错大多卡在两个地方OpenSSL 头文件路径找不到长这样fatal error: openssl/hmac.h: No such file or directory。configure 阶段提示libssl headers not found。解决办法是把 OpenSSL 开发包装上或者手动指定 CPPFLAGS 和 LDFLAGS。例如./configure CPPFLAGS-I/usr/local/ssl/include LDFLAGS-L/usr/local/ssl/lib4.3 生产环境里的静态编译方案我有一台很老的服务器系统里没有编译器也没有动态库。我的做法是在开发机上静态编译好 IPMItool再把二进制拷贝过去./configure --with-internal --enable-static --with-opensslno make -j4如果这台服务器不用 lanplus只使用 lan 走老协议可以关闭 OpenSSL 依赖生成的二进制甚至会小很多部署时也省事。这个精简思路在带外管理的应急场景里特别实用。要注意的是只开启 lan 模式就失去数据加密保护生产环境务必在隔离的管理网段运行。4.4 给 IPMItool 加一条自定义命令很多做服务器管理平台的同学不满足于官方命令想用自己的 PMBus 数据或厂商私有命令扩展 IPMItool。这个完全可行因为你只是给它增加一个命令处理函数。在ipmi_main.c里找到命令表结构static struct ipmi_cmd commands[] { { power, Chassis power control, NULL, cmd_power }, { sensor, Sensor list, NULL, cmd_sensor }, ... };你在数组里加一行仿照别的命令写一个回调函数函数签名通常长这样static int cmd_my_custom(struct ipmi_intf *intf, int argc, char **argv) { struct ipmi_rq req; uint8_t data[8]; struct ipmi_rs *rsp; memset(req, 0, sizeof(req)); req.msg.netfn IPMI_NETFN_APP; req.msg.cmd 0x4E; // 举例自定义命令号 req.msg.data data; req.msg.data_len sizeof(data); // 填充数据发送请求 rsp intf-sendrecv(intf, req); ... }注意真正向 BMC 发送之前检查intf是否已成功 open否则上层传过来的连接可能是个空指针。命令返回码要遵守工具约定0 表示成功非 0 表示失败方便脚本调用判断。加完后重新编译一个内部专属的 IPMItool 就诞生了。5. 源码阅读经验掉坑记录5.1 非 root 权限导致的设备打开失败直接在你电脑上用ipmitool -I open访问本机 BMC 设备文件时需要访问/dev/ipmi0或/dev/ipmi/device这类设备节点。普通用户默认没有权限你会看到类似Could not open device at /dev/ipmi0 or /dev/ipmi/device: Permission denied这不是 IPMItool 的问题是系统权限配置。做法之一是把当前用户加入对应设备文件所属的组比如很多系统是ipmi组sudo usermod -aG ipmi $USER # 退出重登生效还有一种做法是直接给设备节点加 ACLsudo setfacl -m u:yourname:rw /dev/ipmi0这在 Ubuntu 和 CentOS 上我都实测过后者对单个节点更轻量。真正生产环境带外管理多数走网络接口这类问题不见得会遇到但源码调试时会用到。5.2 远程连接的认证与错误排查不管读源码还是实际使用身份认证是绕不开的。-I lanplus时默认使用密码认证但有些老 BMC 固件有缺陷会出现 RAKP 认证失败。日志里常见RAKP HMAC is invalid! Unable to establish IPMI v2 / RMCP session这通常是 BMC 固件 bug 或者系统时间与客户端差距过大导致会话密钥生命周期校验失败。可以先用明文 lan 方式确认连接和命令本身没问题再回头查认证问题。现场排查工具也很重要。Linux 下用tcpdump抓包过滤主机 IP 和端口时重点看 UDP 623 端口上的报文。先看会话历史包有没有按时收到再看 RAKP2 消息里的状态码很多疑难杂症都是抓包后一眼定位的。5.3 接口超时参数如何调整BMC 网络慢或者远端设备负载高时IPMItool 默认重试次数不够表现为命令偶发超时。源码里提供的参数-N设置单条命令超时秒-R设置重试次数。例如ipmitool -I lanplus -H 192.168.10.20 -U root -P calvin -N 10 -R 5 power status实测在部分老设备上默认-N 2 -R 3不够稳定加大到-N 5 -R 4后成功率显著提升。5.4 只改代码不重新 configure 的坑如果你拉取的是 git 最新源码改过.c文件后直接make编译有时会发现改动完全没有生效。原因是你早期执行过./configure生成了一批 config 文件和头文件它们与 git 工作区代码之间存在时间戳或缓存差异。正确姿势是每次进入一个新环境或拉取新代码后先完整走一遍./bootstrap ./configure make clean make很多第一次接触 autotools 的人在这里懵了看到编译报错却找不到原因白白折腾半天。6. 日常使用中的几个高效技巧6.1 批量查看传感器和健康状态读源码同时日常维护也离不开这个工具。我在维护服务器机柜时最常用的是这一组命令ipmitool -I lanplus -H $IP -U admin -P pass sdr list ipmitool -I lanplus -H $IP -U admin -P pass sensor list ipmitool -I lanplus -H $IP -U admin -P pass sel elistsel elist能看到最近所有事件记录包括温度告警、电压异常、风扇故障、非法登录尝试等。把这些输出采集到监控系统里就能实现基础但有效的硬件健康监测网。6.2 用脚本循环执行电源控制管理多台服务器时写个简单的 shell 循环比手动一个个操作高效得多for ip in $(cat server_list.txt); do echo $ip ipmitool -I lanplus -H $ip -U root -P calvin power status done凡是遇到命令执行失败的情况用echo $?检查退出码。源码层面的约定正常处理返回 0超时或 BMC 无响应返回非 0。脚本判断这个状态比直接过滤 stdout 更可靠。6.3 编译参数和安全加固提醒无论是自己编译还是二次开发安全方面有几个底线不要在公网环境直接用-P明文传密码密码会出现在进程列表和 shell history 里。建议使用环境变量IPMITOOL_PASSWORD或交互式输入。管理网段做实网段隔离不给 BMC 暴露到办公网或互联网。源码里如果有敏感 URL、密钥逻辑发布前务必自查尤其是你基于 IPMItool 做了私有扩展后。升级 OpenSSL 版本时重新编译一次 IPMItool因为它会链接系统 OpenSSL 库旧版本可能有已知 CVE。7. 扩展思路从源码到内部管理平台的信号IPMItool 源码值得读的另一个理由是它教会你一种“嵌入式管理协议客户端”的标准写法。很多公司在做自己的带外管理系统时都是参考它的结构。甚至你可以基于这套源码做一个 HTTP API 包装器把power status、sensor list等命令封装成 REST 接口供上层平台调用。我这边就曾基于这套源码做过一个简单的硬件管理服务用 Go 封装了 IPMItool 二进制调用这样上层 Web 面板就能一键操作所有机器的开关机和硬件健康检查。实际上这也暗合了从命令行工具到管理平台演进的常见路径。如果你正在规划类似系统研究 IPMItool 源码比从头读 IPMI 规范文档要容易得多它能直接给你一个正确、精简、可跑的参考实现。最后再分享一个阅读技巧不要线性逐行读代码。先跑通编译然后跟着一条实际命令的执行路径走一遍打印日志、抓包对照这样用不了多久你就能把 IPMItool 源码里最关键的设计要点全部吃透。本文还有配套的精品资源点击获取