记录基于 SNMP 协议采集设备数据时遇到的各种坑。
SNMP 是基于 UDP 的请求-响应方式的协议,服务端(agent)是常见的各种设备,客户端(network management station)向 agent 请求该设备上的数据。数据中心各设备使用的通信协议中,除 MODBUS 外,SNMP 占比最多。
从网络上看,相比 MODBUS RTU,SNMP 不要求采集器与设备直接连接;从工具上看,相比于仅可运行在 Windows 平台的 Modbus Poll,snmpget 在 linux/mac 可以很方便地安装运行,在 Windows 上也有编译好的二进制版本。因此 SNMP 的调试非常方便。
由于厂商实现参差不齐,通过 SNMP 请求采集器的数据时,也会出现各种问题。
OID 缺失
在请求中,变量绑定(variable-binding) 指定了 10 个 oid: p1, p2, ..., p10,响应中只有 9 个 oid ——p1, p2, ..., p9,不是 p10 为 nosuch* 等错误数据,而是压根没有,整个响应数据中都不存在 p10。
因此,在请求数据时,不能假设响应报文与请求报文中的 oid 一一对应——甚至响应 oid 与请求 oid 的个数都不能保证一致,于是,在解析响应数据时,使用 map 保存中间结果会比较方便。
OID 不存在
响应中的某个 oid 在 agent 中不存在,可能是请求了错误的 oid,也可能是 agent 的配置错误,等等。这些情况下,该 oid 在响应中可能的值包括但不限于:NoSuchObject, NoSuchInstance, Null。
IP 不一致
正常情况下,向 IP A 发送请求,响应报文的源 IP 也是 A,但实际中也会出现向 IP A 请求数据,发送响应的却是 IP B。

程序能否正常收到响应,取决于使用的 snmp 库能否处理这种情况,若使用 github.com/gonsmp/gosnmp,需要指定 UseUnconnectedUDPSocket 为 true 才能兼容这种情况。
另外,如果响应的源 IP 与请求的目的 IP 不一致,k8s 设置的网络策略会对发自 Pod 的 IP 包进行 SNAT,因此在 Pod 中无法收到响应——尽管节点收到了来自 IP B 的响应报文,但由于 IP 不一致,内核无法反向 SNAT,响应报文的目的 IP(节点 IP)无法转换为真正的目的 IP(Pod IP)。
响应超时
agent 性能不足
当请求 oid 个数比较多时,可能出现该情况,需要 agent 优化性能或者降低请求频率与 oid 个数。
agent bug
请求 n-1 和 n+1 个 oid 时都能正常收到响应,但请求 n 个 oid 时无法收到。
这种情况可能的原因是 n 个 oid 的响应报文长度落在了一个特定范围,agent 恰巧无法发送长度在特定范围的报文。