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 / snmpdnet-snmp Agent 进程,加载 libsnmp.so 并处理 MIB 对象请求。
MIB / OIDMIB 描述可管理对象,OID 是对象在 MIB 树中的唯一标识。
ASN.1SNMP 报文使用的抽象数据类型,如 INTEGER、OCTET STRING。
映射配置描述 SNMP URI/OID、访问模式和资源协作接口处理流程的 JSON 配置。
进程内路由映射器在 snmpd 进程内加载并执行映射配置的 libroute_mapper 机制。
资源协作接口 / MDSopenUBMC 的资源模型与 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 主键。

调试示例

bash
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 2

2.2 Lua API:match

功能说明

match(uri, method, address, username, reqbody) 是 C 层 handler 与 Lua 层的核心桥接入口。它接收一条 SNMP 请求,经路由映射转换为资源协作接口访问并返回结果。

参数说明

参数类型描述必选
uristring接口 URI,形如 /snmp/<oid>/<接口名>/<模式>。
methodstringGET、PATCH 或 POST。
addressstring客户端 IP。
usernamestringSNMP 用户名;v1/v2c 为 SNMPv1_v2c。
reqbodystring请求体 JSON;GET 可为 nil。

返回值与异常

固定返回 err_code、ret_type、rsp_body 三个值。

返回值类型描述
err_codeinteger0 表示成功,5 表示一般错误,也可返回错误消息中的 SnmpStatusCode。
ret_typestringnumber、string、table 或 userdata。
rsp_bodyany响应值或错误描述字符串。

函数内部使用 pcall 包装,不向调用方抛出异常。映射配置缺失、映射执行异常、响应体为 null 或响应体编码失败时返回非 0 错误。

应用场景

由 net-snmp handler 在收到 MIB Get/Set 请求后调用,业务侧一般不直接调用该函数。

限制条件

方法名由调用方传入,当前映射接口主要使用 GET、PATCH 和 POST;GET 请求的 reqbody 通常为空。

调试示例

match 是 C handler 与 Lua 层的内部接口,不直接面向用户命令行。可通过 2.5 节的 snmpgetsnmpsetsnmpwalk 做端到端验证。

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。

bash
# 遍历本组件注册的 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.50

SNMPv3 需追加 -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}} 占位符引用:

json
{
    "GlobalVariable": {
        "SnmpOemIdentifier": "2011.2.235.1.1"
    }
}

各模块 JSON 含 Resources 数组,每条资源描述一个 SNMP 接口,Uri 中通过 {{SnmpOemIdentifier}} 引用全局变量;Interfaces 描述该接口的 Get、PATCH 或 POST 行为。

以下为标量接口 trapEnable 的节选示例:

json
{
    "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.INFOC 层初始化、脚本加载和接口注册完成。缺失时检查前面是否存在 Lua 加载或接口注册错误。
create the mapper configuration of SNMP interface successfully.INFOLua 层配置和映射器对象初始化完成。结合进程内映射器日志确认配置已加载。
initialize in-process route mapper successfully, generation=nNOTICE进程内路由映射器已编译并加载配置。缺失时检查 libroute_mapper、libmcpp 运行库和配置目录。
load the lua lib failed.ERRORliblua.so 加载失败。检查运行环境中的 Lua 库路径和依赖。
load the main start script failed.ERRORmain.lua 加载或执行失败。检查服务脚本、Lua 模块路径及启动异常。
register the SNMP interface failed.ERRORSNMP 接口注册失败。检查接口 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...ERRORURI、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 invalidERROR表实例数量为 0 或实例结构不合法。检查表数据、主键配置和资源对象。

访问来源记录由 src/lualib-src/handler.c 写入 syslog,使用 LOG_LOCAL7 | LOG_ERR:

text
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 错误码速查表

错误码名称含义可能原因排查建议
0SNMP_ERR_NOERROR成功请求已正常处理。无需处理。
3SNMP_ERR_BADVALUE值非法类型转换或取值范围校验失败。核对请求值、Type 和 Validator。
5SNMP_ERR_GENERR一般性错误映射失败、响应体为空或编码失败。结合路由映射器和资源协作接口日志定位。
6SNMP_ERR_NOACCESS无访问权限对只读接口或只读列执行 Set。核对 URI Mode 和 Sequence Access。
7SNMP_ERR_WRONGTYPE类型错误请求数据类型与配置不符。核对 ReqBody 或 Sequence 的 Type。

5.3 最小化复现与证据收集

  1. 记录客户端 OID、SNMP 版本、访问模式、请求类型和原始返回码。
  2. 保存 snmpd 中模块名为 snmp 的日志及 net stream 来源记录。
  3. 核对 /opt/bmc/apps/snmp/interface_config 的配置根、映射文件和资源对象。
  4. 表接口问题同时记录 Count、Primary 主键和资源协作接口返回。

5.4 调试方法

  • 使用 snmpwalksnmpgetsnmpsetsnmptable 做端到端复现。
  • 使用 busctlmdbctl 检查映射目标资源,区分注册阶段和运行阶段问题。
  • 调整映射配置后重启 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。