snmp
版本信息
| 项目 | 内容 |
|---|---|
| 组件版本 | 1.120.8 |
| 首发版本 | 1.120.5 |
| 文档作者 | openUBMC 社区 |
| 最后更新 | 2026-09-13 |
| 许可证 | Mulan PSL v2 |
| 组件类型 | library |
1. 组件概述
1.1 组件简介
snmp 是 openUBMC 面向网络管理的 library 类型组件,最终产物为共享库 libsnmp.so(部署于 /usr/lib64/libsnmp.so),由 net-snmp 的 snmpd 进程加载运行。 组件作为 SNMP 协议与资源协作接口之间的适配层,将 SNMP Manager 的 Get/Set 请求转换为资源协作接口读写操作。
组件默认使用进程内路由映射器(libroute_mapper + libmcpp)加载映射配置;运行环境缺少进程内映射器时保留旧 route_mapper 兼容路径。组件本身不提供 D-Bus 服务,也不直接处理 IPMI 命令。
1.2 解决什么问题
- 为 SNMP Manager 提供标准 SNMP Get/Set 入口,屏蔽底层资源协作接口和微组件调用细节。
- 将 MIB/OID 与 openUBMC 资源协作接口解耦,通过映射配置适配不同产品和接口。
- 屏蔽 ASN.1 类型与资源协作接口 JSON 类型之间的转换差异。
- 兼容同步返回和异步任务两类资源访问模型。
- 记录 SNMP 请求来源,为访问审计和问题定界提供依据。
1.3 核心功能
- 接口注册:依据映射配置将 SNMP 接口注册为 net-snmp 的标量或表 MIB 对象,支持 Readwrite、Readonly、Setonly 三种访问模式。
- Get/Set 处理:接收 net-snmp 请求,经进程内路由映射器转换为资源协作接口属性读写或动作调用。
- 类型转换:在 INTEGER、OCTET STRING、IpAddress、OBJECT IDENTIFIER 等 ASN.1 类型与 JSON 之间双向转换。
- 表接口缓存:对表类型 GET 使用 1 秒短缓存,降低资源协作接口重复遍历开销;非 GET 访问会清除对应缓存。
- 异步任务关联:识别 TaskReferenceUri,将 SNMP 请求关联到资源协作接口的异步任务对象。
- 访问来源记录:记录到达 BMC 的 SNMP 报文来源 IP、端口和 OID,用于审计与定界。
- 可定制 OID 前缀:支持通过 config.json 中的 SnmpOemIdentifier 替换 OEM OID 前缀。
1.4 关键术语表
| 术语 | 解释 |
|---|---|
| SNMP Manager | 网络管理系统中的 SNMP 客户端,向 Agent 发起 Get/Set 请求。 |
| Agent / snmpd | net-snmp Agent 进程,加载 libsnmp.so 并处理 MIB 对象请求。 |
| MIB / OID | MIB 描述可管理对象,OID 是对象在 MIB 树中的唯一标识。 |
| ASN.1 | SNMP 报文使用的抽象数据类型,如 INTEGER、OCTET STRING。 |
| 映射配置 | 描述 SNMP URI/OID、访问模式和资源协作接口处理流程的 JSON 配置。 |
| 进程内路由映射器 | 在 snmpd 进程内加载并执行映射配置的 libroute_mapper 机制。 |
| 资源协作接口 / MDS | openUBMC 的资源模型与 D-Bus 协作层。 |
1.5 外部交互边界图
2. API 使用说明与示例
组件对外 API 分为 SNMP 侧 Get/Set 接口、Lua 全局 API 和 C 层入口函数。Lua API 由 src/service/main.lua 导出并供 C 层调用;C 层入口函数编译进 libsnmp.so。
2.1 SNMP Get/Set 接口
功能说明
组件从映射配置读取 SNMP URI,将每条合法接口注册为 net-snmp MIB 对象。标量对象使用 /snmp/<oid>/<接口名>/<模式> 形式的 URI;表对象额外配置 Sequence 列定义。
参数说明
| 项目 | 内容 |
|---|---|
| Get | 标量接口读取 OID 时追加 .0;表接口使用 snmpwalk 或 snmptable 遍历。 |
| Set | 使用 snmpset 写入可写标量或表列。 |
| 通用 OID 前缀 | 1.3.6.1.2.1.1. |
| 默认 OEM OID 前缀 | 1.3.6.1.4.1.2011.2.235.1.1. |
| 访问模式 | Readwrite、Readonly、Setonly。 |
返回值与异常
Get 成功时返回 MIB 对象值;Set 成功时返回 SNMP 成功状态。失败时返回 net-snmp 标准错误码,例如 noAccess(6)、wrongType(7)、badValue(3) 或 genErr(5)。
应用场景
用于通过标准 SNMP 工具或网管平台查询和配置 BMC 事件订阅、网络信息及其他资源协作接口对象。
限制条件
- URI 由 / 拆分后必须为 5 段,接口名不能为空,OID 前缀和访问模式必须合法。
- ReqBody 属性类型当前仅支持 integer 和 string。
- Sequence 列类型支持 integer、string、IpAddress 和 ObjectIdentifier;主键类型仅支持 integer 和 string。
- 表接口必须配置至少一个 Primary 主键。
调试示例
snmpget -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0
snmpset -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0 i 22.2 Lua API:match
功能说明
match(uri, method, address, username, reqbody) 是 C 层 handler 与 Lua 层的核心桥接入口。它接收一条 SNMP 请求,经路由映射转换为资源协作接口访问并返回结果。
参数说明
| 参数 | 类型 | 描述 | 必选 |
|---|---|---|---|
| uri | string | 接口 URI,形如 /snmp/<oid>/<接口名>/<模式>。 | 是 |
| method | string | GET、PATCH 或 POST。 | 是 |
| address | string | 客户端 IP。 | 是 |
| username | string | SNMP 用户名;v1/v2c 为 SNMPv1_v2c。 | 是 |
| reqbody | string | 请求体 JSON;GET 可为 nil。 | 否 |
返回值与异常
固定返回 err_code、ret_type、rsp_body 三个值。
| 返回值 | 类型 | 描述 |
|---|---|---|
| err_code | integer | 0 表示成功,5 表示一般错误,也可返回错误消息中的 SnmpStatusCode。 |
| ret_type | string | number、string、table 或 userdata。 |
| rsp_body | any | 响应值或错误描述字符串。 |
函数内部使用 pcall 包装,不向调用方抛出异常。映射配置缺失、映射执行异常、响应体为 null 或响应体编码失败时返回非 0 错误。
应用场景
由 net-snmp handler 在收到 MIB Get/Set 请求后调用,业务侧一般不直接调用该函数。
限制条件
方法名由调用方传入,当前映射接口主要使用 GET、PATCH 和 POST;GET 请求的 reqbody 通常为空。
调试示例
match 是 C handler 与 Lua 层的内部接口,不直接面向用户命令行。可通过 2.5 节的 snmpget、snmpset 或 snmpwalk 做端到端验证。
2.3 其余 Lua API
| API | 作用 | 返回 | 异常 |
|---|---|---|---|
| get_all_interface_uri_config() | 返回所有 SNMP 接口的 URI、Name、Oid 和 Mode,用于启动注册。 | JSON 字符串数组 | 不满足校验的 URI 会被跳过并打印错误日志。 |
| get_sequence_elements_count(uri) | 返回接口 Sequence 元素个数,用于区分标量和表。 | 整数,标量为 0 | 非法或空 URI 返回 0。 |
| get_interface_reqbody_config(uri) | 返回 Set 请求体配置。 | JSON {Type, ReqBody:[{Name,Type}]} | 空配置返回空字符串。 |
| get_interface_primary_key(uri) | 返回表接口主键名称和类型。 | JSON 数组 [{Name,Type}] | 无 Sequence 时返回 []。 |
| get_interface_instance(uri) | 返回表接口实例信息,带 1 秒缓存。 | JSON {Count, PK, Index} | 失败时返回空字符串。 |
| get_interface_sequence(uri) | 返回表接口 Sequence 列描述。 | JSON 数组 | 无 Sequence 时返回空字符串。 |
2.4 C 层入口函数
| 函数 | 作用 |
|---|---|
| init_snmp_proxy() | 初始化入口,加载 Lua 库和 main.lua,并注册 SNMP 接口。 |
| register_interface() | 根据 get_all_interface_uri_config() 的返回值注册标量或表接口。 |
| interface_handle(...) | net-snmp handler 回调,分发 Get/Set 请求并记录访问来源。 |
2.5 调试示例
本组件是 library,运行载体为 snmpd,调试入口为 net-snmp 客户端工具,而不是 busctl 或 mdbctl。
# 遍历本组件注册的 SNMP 接口(OEM OID 子树)
snmpwalk -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1
# 读取标量接口(OID 后追加 .0;示例 trapEnable,路由样例见 test/unit/mock_routemapper.lua)
snmpget -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0
# 示意响应(需结合实际配置和环境验证):
# iso.3.6.1.4.1.2011.2.235.1.1.4.1.0 = INTEGER: 1
# 设置标量接口
snmpset -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.1.0 i 2
# 遍历表接口(示例 trapInfoDescriptionTable)
snmptable -v2c -c <community> <bmc_ip> 1.3.6.1.4.1.2011.2.235.1.1.4.50SNMPv3 需追加 -v3 -l authPriv -u <user> -a <authproto> -A <authpass> -x <privproto> -X <privpass>。
3. 组件扩展案例
3.1 拓展能力概述
核心扩展能力是基于映射配置新增或定制 SNMP 接口,无需修改 C 代码。映射配置为 JSON 格式,源文件随产品仓提供,可参考 rackmount 仓的 rackmount/interface_config/snmp/。 进程内路由映射器直接使用该目录作为配置根,根目录下包含 mapping_config、script 和 plugins 等配置。
组件从 innerbus 读取 platform_id 和 board_id,按十六进制 %02x_%02x 拼成产品目录名。 若 /opt/bmc/apps/snmp/interface_config/<platform_id>_<board_id> 存在,则使用该目录;否则使用默认根目录 /opt/bmc/apps/snmp/interface_config。
3.2 扩展点说明
| 扩展点 | 位置 | 用途 |
|---|---|---|
| 接口映射配置 | <配置根>/mapping_config 下的 JSON | 定义 SNMP URI、OID、访问模式、请求响应结构及 ProcessingFlow。 |
| 全局配置 | <配置根>/config.json | 定义 SnmpOemIdentifier 等全局变量。 |
| Lua 脚本 | <配置根>/script | 提供映射流程中调用的校验或转换脚本。 |
| Lua 插件 | <配置根>/plugins | 提供映射流程中可复用的插件逻辑。 |
旧 route_mapper 兼容路径会把 mapping_config 子目录作为配置入口;使用进程内路由映射器时,传入的是 mapping_config、script 和 plugins 的公共父目录。
3.3 映射配置示例
config.json 定义 OEM OID 变量,供映射配置中的 {{SnmpOemIdentifier}} 占位符引用:
{
"GlobalVariable": {
"SnmpOemIdentifier": "2011.2.235.1.1"
}
}各模块 JSON 含 Resources 数组,每条资源描述一个 SNMP 接口,Uri 中通过 {{SnmpOemIdentifier}} 引用全局变量;Interfaces 描述该接口的 Get、PATCH 或 POST 行为。
以下为标量接口 trapEnable 的节选示例:
{
"Resources": [
{
"Uri": "/snmp/1.3.6.1.4.1.{{SnmpOemIdentifier}}.4.1/trapEnable/Readwrite",
"Interfaces": [
{
"Type": "Get",
"RspBody": {
"Enabled": "${Statements/Enabled()}"
},
"Statements": {
"Enabled": {
"Input": "${ProcessingFlow[1]/Destination/Enabled}",
"Steps": [
{
"Type": "Switch",
"Formula": [
{
"Case": false,
"To": 1
},
{
"Case": true,
"To": 2
}
]
}
]
}
},
"ProcessingFlow": [
{
"Type": "Property",
"Path": "/bmc/kepler/EventService/Subscriptions/Snmp",
"Interface": "bmc.kepler.EventService.Subscriptions.Snmp",
"Destination": {
"Enabled": "Enabled"
}
}
]
},
{
"Type": "PATCH",
"ReqBody": {
"Type": "object",
"Required": true,
"Properties": {
"Enabled": {
"Required": true,
"Type": "integer",
"Validator": [
{
"Type": "Enum",
"Formula": [
1,
2
]
}
]
}
}
},
"Statements": {
"Enabled": {
"Input": "${ReqBody/Enabled}",
"Steps": [
{
"Type": "Switch",
"Formula": [
{
"Case": 1,
"To": false
},
{
"Case": 2,
"To": true
}
]
}
]
}
},
"ProcessingFlow": [
{
"Type": "Property",
"Path": "/bmc/kepler/EventService/Subscriptions/Snmp",
"Interface": "bmc.kepler.EventService.Subscriptions.Snmp",
"Source": {
"Enabled": "${Statements/Enabled()}"
}
}
]
}
]
}
]
}3.4 URI 校验与注意事项
- URI 格式为 /snmp/<oid>/<接口名>/<模式>,拆分为 5 段。
- OID 前缀须为通用前缀 1.3.6.1.2.1.1. 或 OEM 前缀 1.3.6.1.4.1.{{SnmpOemIdentifier}}.。
- 访问模式须为 Readwrite、Readonly 或 Setonly。
- 表接口必须配置 Sequence 数组,其中至少一个元素的 Primary 为 true;列 Access 为 Readonly 时 Set 会被拒绝。
- 简单接口的 ReqBody 属性个数应为 1。
- 不满足校验规则的 URI 会被跳过,并在模块名为 snmp 的日志中打印错误。
- 完成配置后重启 snmpd,再使用 snmpwalk、snmpget 或 snmpset 验证。
4. 日志说明
4.1 一键日志收集
组件未注册自定义 on_dump 回调,一键日志收集按框架默认逻辑处理。Lua 层和 C 层统一使用模块名 snmp,因此收集结果包含组件输出的标准日志和 net stream 访问来源记录,仓库代码未定义额外的组件专属收集清单。
4.2 关键日志信息
| 日志片段 | 日志级别 | 含义解读 | 建议处理动作 |
|---|---|---|---|
| initialize the Proxy of SNMP interface successfully. | INFO | C 层初始化、脚本加载和接口注册完成。 | 缺失时检查前面是否存在 Lua 加载或接口注册错误。 |
| create the mapper configuration of SNMP interface successfully. | INFO | Lua 层配置和映射器对象初始化完成。 | 结合进程内映射器日志确认配置已加载。 |
| initialize in-process route mapper successfully, generation=n | NOTICE | 进程内路由映射器已编译并加载配置。 | 缺失时检查 libroute_mapper、libmcpp 运行库和配置目录。 |
| load the lua lib failed. | ERROR | liblua.so 加载失败。 | 检查运行环境中的 Lua 库路径和依赖。 |
| load the main start script failed. | ERROR | main.lua 加载或执行失败。 | 检查服务脚本、Lua 模块路径及启动异常。 |
| register the SNMP interface failed. | ERROR | SNMP 接口注册失败。 | 检查接口 URI 校验结果和模块日志。 |
| create the mapper of SNMP interface failed, error is ... | ERROR | 映射器对象创建失败。 | 检查运行库版本、配置根目录和依赖。 |
| The route configuration is missing. | ERROR | 映射配置不存在或路由列表加载失败。 | 检查产品目录、mapping_config 及文件权限。 |
| the prefix of Uri(...) should be ... / the Uri(...) is invalid... | ERROR | URI、OID、接口名或访问模式校验失败,接口被跳过。 | 按 URI 校验规则修正映射配置。 |
| get the instance of interface(...) failed, error is ... | ERROR | 表接口实例获取失败。 | 检查资源协作接口对象、映射路径和底层服务日志。 |
| the response body is nil / encode the response body failed, error is ... | ERROR | 响应体为空或 JSON 编码失败。 | 检查资源返回值和类型处理配置。 |
| the response body of task is null. | NOTICE | 任务接口响应体为空,已使用默认任务信息。 | 检查底层异步任务状态。 |
| the count of sequence is invalid | ERROR | 表实例数量为 0 或实例结构不合法。 | 检查表数据、主键配置和资源对象。 |
访问来源记录由 src/lualib-src/handler.c 写入 syslog,使用 LOG_LOCAL7 | LOG_ERR:
Received SNMP packet(s) from UDP: [<客户端IP>]:<端口>. oid:<OID>未解析字段以 N/A 填充,该记录是定位 SNMP 访问来源的关键信息。
5. 问题定界指南
5.1 典型问题定界
| 问题描述 | 是否为本组件问题 | 判断依据 | 关键证据收集方法 |
|---|---|---|---|
| snmpwalk 或 snmpget 返回 genErr(5)。 | 本组件或资源协作接口 | 映射失败、响应体为空或响应体编码失败会统一返回一般错误。 | 检查 The route configuration is missing、the response body is nil 和编码失败日志。 |
| snmpget 返回 noSuchObject 或找不到 OID。 | 通常是本组件 | 接口未注册时 OID 不存在。 | 检查 URI、OID、接口名和模式校验失败日志。 |
| snmpset 返回 noAccess(6)。 | 是本组件 | 接口 Mode 或 Sequence 列 Access 为 Readonly。 | 核对映射配置中的模式和列访问权限。 |
| snmpset 返回 wrongType(7) 或 badValue(3)。 | 是本组件 | 请求类型与配置 Type 不匹配,或值转换失败。 | 核对 ReqBody、Sequence 类型和 Validator。 |
| 表接口 snmpwalk 无数据。 | 本组件或资源协作接口 | Primary 主键、实例数量或资源对象数据异常。 | 检查 the count of sequence is invalid 和 get the instance of interface failed 日志。 |
| SNMP 访问被拒绝或用户被锁定。 | 本组件或账号服务 | user_login_lock.c 负责登录失败锁定处理,账号状态由账号服务维护。 | 检查 [user login lock] 日志、账号状态和认证配置。 |
| 组件加载或接口注册失败。 | 是本组件 | libsnmp.so 初始化或 Lua 启动阶段失败。 | 检查 load the lua lib failed、load the main start script failed 和 register the SNMP interface failed 日志。 |
调试和复现时,先用 snmpwalk、snmpget 或 snmpset 复现,再查看模块名为 snmp 的日志,区分“注册阶段跳过”和“运行阶段失败”,最后核查映射配置、访问模式、表主键和资源协作接口对象状态。
5.2 错误码速查表
| 错误码 | 名称 | 含义 | 可能原因 | 排查建议 |
|---|---|---|---|---|
| 0 | SNMP_ERR_NOERROR | 成功 | 请求已正常处理。 | 无需处理。 |
| 3 | SNMP_ERR_BADVALUE | 值非法 | 类型转换或取值范围校验失败。 | 核对请求值、Type 和 Validator。 |
| 5 | SNMP_ERR_GENERR | 一般性错误 | 映射失败、响应体为空或编码失败。 | 结合路由映射器和资源协作接口日志定位。 |
| 6 | SNMP_ERR_NOACCESS | 无访问权限 | 对只读接口或只读列执行 Set。 | 核对 URI Mode 和 Sequence Access。 |
| 7 | SNMP_ERR_WRONGTYPE | 类型错误 | 请求数据类型与配置不符。 | 核对 ReqBody 或 Sequence 的 Type。 |
5.3 最小化复现与证据收集
- 记录客户端 OID、SNMP 版本、访问模式、请求类型和原始返回码。
- 保存
snmpd中模块名为snmp的日志及 net stream 来源记录。 - 核对
/opt/bmc/apps/snmp/interface_config的配置根、映射文件和资源对象。 - 表接口问题同时记录 Count、Primary 主键和资源协作接口返回。
5.4 调试方法
- 使用
snmpwalk、snmpget、snmpset和snmptable做端到端复现。 - 使用
busctl或mdbctl检查映射目标资源,区分注册阶段和运行阶段问题。 - 调整映射配置后重启 snmpd,再确认进程内路由映射器初始化成功。
6. 常见问题解答
Q1:snmpget 访问某 OID 返回 genErr(5),如何定位?
- 问题描述:Get 请求未返回目标值,而是返回一般错误。
- 一句话答案:先查看模块名为 snmp 的日志中是否存在映射失败、响应体为空或编码失败错误。
- 根因说明:genErr(5) 是映射失败、响应体为 null、响应体编码失败等情况统一返回的错误码。
- 解决方案:核查映射配置和资源协作接口对象,重点检索 create the mapper of SNMP interface failed、The route configuration is missing 和 the response body is nil 等日志。
- 规避方案:变更映射配置前保存原文件,并在隔离环境验证 OID 和返回类型。
- 适用版本:1.120.6。
Q2:新增 SNMP 接口需要改代码吗?
- 问题描述:产品需要新增或定制 SNMP 可管理对象。
- 一句话答案:不需要修改 C 代码,在映射配置目录新增或修改 URI 配置即可。
- 根因说明:组件经进程内路由映射器加载配置根目录,并从映射配置动态注册接口。
- 解决方案:按第 3 章的示例配置,确认 URI 校验规则,重启 snmpd 后使用 snmpwalk、snmpget 或 snmpset 验证。
- 规避方案:新增接口先使用只读 OID 验证,再开放 Set。
- 适用版本:1.120.6。
Q3:表接口 snmpwalk 遍历不到数据,如何处理?
- 问题描述:表接口 OID 存在,但遍历结果为空或报错。
- 一句话答案:通常是 Sequence 主键配置缺失或非法,也可能是资源协作接口对象无数据。
- 根因说明:表遍历依赖 Primary 主键构建索引;实例获取失败或 Count 为 0 会导致无数据。
- 解决方案:检查 the count of sequence is invalid 和 get the instance of interface failed 日志,核对主键配置和资源协作接口对象。
- 规避方案:表接口上线前验证空表、单实例和多实例场景。
- 适用版本:1.120.6。
Q4:snmpset 被拒绝,如何区分 noAccess、wrongType 和 badValue?
- 问题描述:Set 请求返回 noAccess(6)、wrongType(7) 或 badValue(3)。
- 一句话答案:三者分别对应该写属性被写、请求类型不符和请求值非法。
- 根因说明:noAccess 由 URI Mode 或列 Access 为 Readonly 触发;wrongType 和 badValue 由类型不匹配或转换失败触发。
- 解决方案:核对接口 Mode、列 Access、ReqBody、Sequence 的 Type 和 Validator。
- 规避方案:Set 前先执行 Get 并保存原值,确认失败时资源未变化。
- 适用版本:1.120.6。
Q5:如何定制 OEM OID 前缀?
- 问题描述:产品需要使用自定义 OEM OID 前缀。
- 一句话答案:通过配置根目录下 config.json 的 SnmpOemIdentifier 设置。
- 根因说明:默认 OEM 前缀为 1.3.6.1.4.1.2011.2.235.1.1.,组件会按配置构建实际前缀并校验映射 URI。
- 解决方案:设置 SnmpOemIdentifier,并保证映射配置中的 OID 前缀保持一致。
- 规避方案:切换 OEM 前缀前确认网管系统和 MIB 文件同步更新。
- 适用版本:1.120.6。
Q6:启动日志没有 initialize in-process route mapper successfully,如何排查?
- 问题描述:snmpd 启动后接口未注册,或日志提示路由配置缺失。
- 一句话答案:检查进程内路由映射器运行库、Lua 模块和产品配置根目录是否完整。
- 根因说明:snmp_mapper.lua 依赖 routemapper.sync、error_registry 以及 libroute_mapper、libmcpp 等运行库;依赖加载失败时会记录错误或回退兼容路径。
- 解决方案:确认 libroute_mapper_runtime.so、/usr/lib64/routemapper/core.so、/usr/share/lua/routemapper/ 下的 Lua 模块和 libmcpp 运行库与目标工具链匹配,同时检查 interface_config 产品目录。
- 规避方案:升级组件时同步升级映射器运行库,并在启动后检查初始化成功日志。
- 适用版本:1.120.6。