Telegraf inputs.snmp_trap 插件详解:通过 UDP 接收、翻译并结构化 SNMP Trap 事件
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
本文围绕 Telegraf 的snmp_trap服务输入插件展开:它如何在 UDP 端口上被动监听 SNMP trap/inform 通知,如何用gosmi或netsnmp后端将 MIB 对象翻译为可读名称,以及如何把 trap 变量转换为带标签的snmp_trap指标。读完后你将掌握完整的 TOML 配置(含 SNMPv3 安全选项)、特权端口的最小权限方案、v1 trap 到 v2 的自动转换机制,以及指标输出的标签/字段结构。
插件定位:一个被动的 UDP 监听服务
snmp_trap插件监听 SNMP 通知,包括 trap 和 inform 请求,通知通过纯 UDP 接收,端口可配置(参见 plugins/inputs/snmp_trap/README.md)。它与snmp这类主动轮询插件不同:不发起 Get/Set 请求,而是作为接收端常驻等待设备推送事件。
从源码结构看(snmp_trap.go),核心实现非常薄:
- 插件结构体
SnmpTrap持有一个gosnmp.TrapListener(listener *gosnmp.TrapListener,见 snmp_trap.go#L29-L50); Init()负责解析配置、选择 MIB 翻译器并构造 gosnmp 安全参数;Start()启动 UDP 监听,并把每个收到包的处理委托给s.handler(s.listener.OnNewTrap = s.handler,见 snmp_trap.go#L196-L198);Gather()是空实现(直接返回 nil),因为数据不是按 interval 采集出来的,而是事件驱动的。
Service Input 的语义
该插件属于 service input。与普通插件相比,service 插件有两个关键差异:
- 全局或插件级
interval设置可能不适用; --test、--test-wait和--once等 CLI 选项对该插件可能不产生输出。
完整说明见 service_input 文档。
完整配置与参数说明
以下配置完整继承自 sample.conf,并在注释之外补充了源码可确认的默认值与取值范围:
# Receive SNMP traps [[inputs.snmp_trap]] ## Transport, local address, and port to listen on. Transport must ## be "udp://". Omit local address to listen on all interfaces. ## example: "udp://127.0.0.1:1234" ## ## Special permissions may be required to listen on a port less than ## 1024. See README.md for details ## # service_address = "udp://:162" ## ## Path to mib files ## Used by the gosmi translator. ## To add paths when translating with netsnmp, use the MIBDIRS environment variable # path = ["/usr/share/snmp/mibs"] ## ## Timeout running snmptranslate command ## Used by the netsnmp translator only # timeout = "5s" ## Snmp version; one of "1", "2c" or "3". # version = "2c" ## SNMPv3 authentication and encryption options. ## ## Security Name. # sec_name = "myuser" ## Authentication protocol; one of "MD5", "SHA", "SHA224", "SHA256", "SHA384", "SHA512" or "". # auth_protocol = "MD5" ## Authentication password. # auth_password = "pass" ## Security Level; one of "noAuthNoPriv", "authNoPriv", or "authPriv". # sec_level = "authNoPriv" ## Privacy protocol used for encrypted messages; one of "DES", "AES", "AES192", "AES192C", "AES256", "AES256C" or "". # priv_protocol = "" ## Privacy password used for encrypted messages. # priv_password = ""参数与源码映射(字段定义见 snmp_trap.go#L29-L50,默认值见 init 注册处):
| 参数 | 默认值 | 说明 |
|---|---|---|
service_address | udp://:162 | 必须以udp://开头,Start()中会显式校验 scheme,非 UDP 直接报错(snmp_trap.go#L206-L214)。省略本地地址即监听所有网卡;端口小于 1024 可能需要特殊权限 |
path | ["/usr/share/snmp/mibs"] | MIB 文件路径列表,gosmi翻译器使用;netsnmp翻译器则通过MIBDIRS环境变量追加路径 |
timeout | 5s | snmptranslate命令的执行超时,仅netsnmp翻译器使用(snmp_trap.go#L24 定义defaultTimeout) |
version | 2c | 取值"1"、"2c"或"3";空字符串按2c处理 |
sec_name | — | SNMPv3 安全名,config.Secret类型,支持 secret store |
auth_protocol | — | 取值MD5、SHA、SHA224、SHA256、SHA384、SHA512或"",大小写不敏感 |
auth_password | — | 认证口令,config.Secret类型 |
sec_level | — | 取值noAuthNoPriv、authNoPriv或authPriv,空字符串按noAuthNoPriv |
priv_protocol | — | 取值DES、AES、AES192、AES192C、AES256、AES256C或"" |
priv_password | — | 加密口令,config.Secret类型 |
此外,插件支持全局配置选项(修改 metrics、tags、fields,创建别名、配置插件顺序等),详见 CONFIGURATION.md 的 plugins 章节。
注意:README 特别强调——
path设置在所有 SNMP 插件类型的所有实例之间是共享的。
SNMPv3 安全参数的落地方式
version = "3"时,Init()会依次完成四步(见 snmp_trap.go#L108-L193):
- 设置安全模型为
gosnmp.UserSecurityModel; - 将
sec_level映射到 gosnmp 的MsgFlags(NoAuthNoPriv/AuthNoPriv/AuthPriv),未知取值返回unknown security level错误; - 将
auth_protocol/priv_protocol映射到对应的AuthenticationProtocol/PrivacyProtocol(均忽略大小写),未知取值分别报unknown authentication protocol/unknown privacy protocol; - 通过
config.Secret接口取回sec_name、auth_password、priv_password并填入UsmSecurityParameters,取回后立即调用Destroy()释放秘密。
这三项秘密字段因此支持 Telegraf 的 secret store 机制,用法见 secret store 文档。
值得留意的一点:sec_level为noAuthNoPriv时 gosnmp 仍会构造安全参数结构。测试文件 snmp_trap_test.go 对noAuthNoPriv、authNoPriv(MD5/SHA/SHA224/SHA256/SHA384/SHA512 全覆盖)以及authPriv(SHA+AES、SHA+DES、SHA+AES192、SHA+AES192C、SHA+AES256 等组合)逐一构造了真实 UDP 收发用例(TestReceiveTrapV3,见 snmp_trap_test.go#L330),可以用它们作为各安全组合下指标输出形态的参考。
SNMP 后端:gosmi 与 netsnmp
插件通过translator接口(lookup(oid string) (snmp.MibEntry, error),snmp_trap.go#L52-L54)解耦 MIB 翻译,提供两种实现:
gosmi(推荐)
纯 Go 实现,初始化时一次性把path下所有 MIB 文件加载进内存(gosmi.go):
newGosmiTranslator调用snmp.LoadMibsFromPath(paths, log, &snmp.GosmiMibLoader{});- 每次 OID 查询走共享的
snmp.TrapLookup(oid)(见 plugins/common/snmp/translator_gosmi.go)。
加载失败会在Init()阶段直接返回错误,使插件无法启动——这提供了 MIB 路径配置的快速反馈。
netsnmp(已弃用)
netsnmp翻译器通过调用系统上的snmptranslate命令完成翻译(netsnmp.go#L60-L83):
snmptranslate -Td -Ob -m all <oid>实现细节:
- 每个插件实例持有独立的翻译器缓存(
cache map[string]snmp.MibEntry+sync.Mutex),缓存未命中时才 exec 外部命令;源码注释指出这与snmp插件的全局缓存不同,因为 trap 插件通常只配置一个实例(netsnmp.go#L30-L43); - 输出解析逻辑:取第一行文本,以第一个
::为界拆出 MIB 名与对象名,没有::视为not found; timeout参数控制每次snmptranslate的执行超时(经internal.RunTimeout强制执行)。
如何选择:全局snmp_translator选项
后端选择不是插件级配置,而是[agent]段的snmp_translator选项,作用于 Telegraf 中所有 SNMP 用法(字段定义见 config.go#L276-L278)。配置示例:
[agent] snmp_translator = "gosmi"默认使用netsnmp,但该取值已被弃用:config.go#L683-L690 中明确记录了弃用信息——自 1.25.0 起弃用、计划 1.40.0 移除,提示使用gosmi。README 也鼓励迁移到gosmi,如遇到gosmi有问题而netsnmp没有的情况,应向上游提交 issue。
使用特权端口
在很多操作系统上,监听小于 1024 的端口需要额外权限。默认 SNMP trap 端口 162 正落在这个区间,因此用 Telegraf 接收 trap 可能需要额外授权。各操作系统的做法不同,官方建议不要为了用特权端口而让 telegraf 以超级用户运行,而是遵循最小权限原则,用操作系统特定机制授予该端口能力;另一条路是让 telegraf 监听非特权端口,再用防火墙端口转发规则把特权端口的流量转发过来。
Linux 上可以用setcap给 telegraf 二进制授予CAP_NET_BIND_SERVICE能力:
setcap cap_net_bind_service=+ep /usr/bin/telegrafmacOS 在 10.14 及以后版本对监听特权端口不设限。
Trap 处理流程:从 UDP 包到指标
handler()是数据转换的核心(snmp_trap.go#L246-L360),处理顺序如下:
1. 基础标签与调试日志
每个包先建立version(包版本字符串)与source(发送端 IP)两个基础标签。开启 trace 级别日志时,会把原始报文十六进制编码输出,便于排障。
2. SNMPv1 trap 的 v1→v2 转换
对 v1 包,按 RFC 2576 第 3.1 节的流程合成 trap OID(snmp_trap.go#L264-L289):
GenericTrap在 0~5 之间(冷启动等通用 trap):拼出.1.3.6.1.6.3.1.1.5.<GenericTrap+1>;GenericTrap == 6(enterpriseSpecific):拼出<Enterprise>.0.<SpecificTrap>;- 合成后必须经翻译器解析成功才能设置
oid/name/mib标签,失败则记录错误并丢弃该包。
同时,v1 包会把agent_address写入标签、把时间戳写入sysUpTimeInstance字段。
3. 变量(PDU)遍历与字段生成
对包内每个变量按类型处理:
- ObjectIdentifier 类型:先用翻译器解析其值。特殊地,当变量自身的 OID 是
1.3.6.1.6.3.1.1.4.1.0(即SNMPv2-MIB::snmpTrapOID.0,见 snmp_trap.go#L319-L324)时,它承载的是 trap 名,解析结果写入oid/name/mib标签后跳过,不作为普通字段输出;其余 OID 值解析为可读文本后作为字段值。 - OctetString 类型:内容若不是合法 UTF-8,则转为十六进制字符串输出,否则按原样输出。
- 其他类型:值直接透传。
每个变量随后用翻译器把变量 OID(如.1.3.6.1.2.1.1.3.0)解析成对象名作为字段名。注意源码中的设计选择:不做“解析失败时回退为数字 OID”的兜底,因为数字 OID 对用户价值有限且事后难以清理;任何一次解析失败都会记录Error resolving OID oid=...并放弃整包。
4. 版本相关标签
- v3:
ContextName非空时写context_name标签;ContextEngineID非空时按 SNMP RFC(3411、5343)习惯以十六进制字符串形式写入engine_id标签(snmp_trap.go#L345-L352); - v1/v2c:
Community非空时写community标签。
最后通过acc.AddFields("snmp_trap", fields, tags, tm)输出指标。
指标结构与示例输出
输出指标名为snmp_trap:
- tags:
source(string,trap 源 IP 地址)name(string,来自SNMPv2-MIB::snmpTrapOID.0PDU 的 trap 名)mib(string,来自SNMPv2-MIB::snmpTrapOID.0PDU 的 MIB 名)oid(string,来自SNMPv2-MIB::snmpTrapOID.0PDU 的 OID 字符串)version(string,"1"或"2c"或"3")context_name(string,v3 trap 的值)engine_id(string,v3 trap 的值)community(string,v1 或 2c trap 的值)agent_address(string,仅 v1 trap 携带 AgentAddress 时出现)
- fields:
- 由 trap 中的变量一一映射而来。字段名是变量 OID 经 MIB 查找后的对象名,字段值是 trap 变量值。
真实输出的 Line Protocol 示例(来自 README):
snmp_trap,mib=SNMPv2-MIB,name=coldStart,oid=.1.3.6.1.6.3.1.1.5.1,source=192.168.122.102,version=2c,community=public snmpTrapEnterprise.0="linux",sysUpTimeInstance=1i 1574109187723429814 snmp_trap,mib=NET-SNMP-AGENT-MIB,name=nsNotifyShutdown,oid=.1.3.6.1.4.1.8072.4.0.2,source=192.168.122.102,version=2c,community=public sysUpTimeInstance=5803i,snmpTrapEnterprise.0="netSnmpNotificationPrefix" 1574109186555115459测试验证方式
单元测试用真实 UDP 收发验证全链路:在 snmp_trap_test.go 中,插件在 12399 端口启动TrapListener,再用 gosnmp 客户端SendTrap发送构造好的包,最后断言指标标签与字段。三组测试分别覆盖:
TestReceiveTrapV1(L21):enterprise trap 与 generic(coldStart)trap,含非 UTF-8 OctetString 的十六进制转换(valueHexOID = "07e801040e021900000e02");TestReceiveTrapV2c(L206):标准 v2c coldStart trap,验证snmpTrapOID.0生成oid/name/mib标签、sysUpTimeInstance生成字段;TestReceiveTrapV3(L330):遍历noAuthNoPriv、authNoPriv(六种认证协议)、authPriv(AES/DES/AES192/AES192C/AES256 系列)组合,并验证context_name与十六进制engine_id标签。
测试中用testTranslator替代真实 MIB 查找,说明翻译器是可插拔的接口,这正是 gosmi/netsnmp 双后端设计的可测试性来源。
相关文件索引
- 插件实现:plugins/inputs/snmp_trap/snmp_trap.go
- gosmi 翻译器:plugins/inputs/snmp_trap/gosmi.go
- netsnmp 翻译器:plugins/inputs/snmp_trap/netsnmp.go
- 样本配置:plugins/inputs/snmp_trap/sample.conf
- 测试用例:plugins/inputs/snmp_trap/snmp_trap_test.go
- 全局翻译器选项:config/config.go(
snmp_translator字段与 netsnmp 弃用提示) - 公共 SNMP/MIB 库:plugins/common/snmp
- 插件全局配置说明:docs/CONFIGURATION.md
【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考