SNMP 驱动
dc3-driver-snmp 把支持 SNMP 的网络与机房设备接入 IoT DC3:以设备 MIB 树上的 OID 为目标,周期性发 SNMP GET 采数,并支持向 OID 发 SNMP SET 写值。读完你能配好一台路由器、交换机或 UPS,把它的端口状态、流量、温湿度等指标采成位号值。
协议背景
SNMP(Simple Network Management Protocol,简单网络管理协议)是网络与数据中心设备最通用的管理协议,跑在 UDP 上、默认端口 161 。它属于物联网四层架构里的网络层——和工业现场的 Modbus、OPC 一样解决" 在某个寻址空间里读/写一个值"的问题,只是它的设备不是 PLC 和电表,而是路由器、交换机、UPS、机房 PDU、打印机、服务器网卡这类 IP 网络元件。
每台被管设备内置一棵 MIB(Management Information Base)树,树上每个可读写的数据点都有唯一的对象标识符 OID(Object Identifier,形如 1.3.6.1.2.1.1.1.0)。管理端(manager)通过 OID 来定位"采哪个值":
- 标量对象末尾带实例标识
.0,例如系统描述sysDescr是1.3.6.1.2.1.1.1.0; - 表项对象(如各端口的流量、状态)末尾带行索引,例如
...10.1、...10.2分别对应 1 号、2 号端口。
SNMP 有 v1 / v2c / v3 三个版本。v1 与 v2c 用明文的 community(团体名)做口令,配置简单、现场最常见;v3 引入 USM(User-based Security Model)支持认证与加密。本驱动基于 SNMP4J 库,作为 SNMP 管理端主动连接设备:读位号对其 OID 发 GET,写位号对 OID 发 SET,每台设备复用一个常驻的 SNMP 会话。
典型用途是机房与网络监控——带宽、端口 up/down、CPU/内存利用率、机房温湿度、UPS 电量等,凡是支持 SNMP 的设备,配好 OID 即可纳管。
属性配置
SNMP 的连接参数和采集目标在两个层面填写:连接一台设备靠驱动属性(device 级),定位每个数据点靠位号属性(point 级)。属性的名称、类型与默认值都来自驱动 application.yml 的 driver-attribute / point-attribute / command-attribute 定义。
驱动属性(设备级 driver-attribute)
接入一台 SNMP 设备时,在设备上填这些属性。 host / port 决定连哪台设备的哪个 UDP 端口,version + community 是 v1/v2c 的身份口令,timeout / retries 控制请求的容错。
| 属性 | code | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| Host | host | STRING | 127.0.0.1 | SNMP 设备 IP |
| Port | port | INT | 161 | SNMP 端口(标准 161) |
| Version | version | STRING | v2c | SNMP 版本(v1 / v2c) |
| Community | community | STRING | public | 团体名(相当于只读/读写口令) |
| USM Username | usmUsername | STRING | (空) | SNMPv3 USM 用户名(v1/v2c 下不使用) |
| USM Auth Protocol | usmAuthProtocol | STRING | MD5 | SNMPv3 认证算法(MD5/SHA) |
| USM Auth Password | usmAuthPassword | STRING | (空) | SNMPv3 认证密码 |
| Timeout | timeout | INT | 5000 | 请求超时(毫秒) |
| Retries | retries | INT | 1 | 请求重试次数 |
USM 三项仅为 SNMPv3 预留,当前不生效
usmUsername / usmAuthProtocol / usmAuthPassword 是 SNMPv3 的 USM 安全字段,application.yml 里已预留但驱动 buildTarget() 只构造 CommunityTarget,按 version 设置 version1 或 version2c。当前实现只支持 v1 与 v2c,这三项填了也不会被读取,version 请填 v1 或 v2c。
validate() 把 host / port / version / community 列为必填——缺任一项设备校验不通过。
位号属性(point-attribute)
每个采集位号上填 oid 指定采哪个数据点;snmpType 标注该值的 SNMP 数据类型。
| 属性 | code | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| OID | oid | STRING | (空) | SNMP 对象标识符(如 1.3.6.1.2.1.1.1.0) |
| SNMP Type | snmpType | STRING | OCTET_STRING | SNMP 数据类型(INTEGER/GAUGE32/COUNTER32/OCTET_STRING/TIMETICKS/IPADDRESS/OID) |
OID 决定采哪个数据点,snmpType 主要供写入用
读取时驱动对配置的 oid 发 GET,把返回 VariableBinding 的变量值用 variable.toString() 原样作为位号值上报——此时 snmpType 不参与取值。snmpType 的真正作用在写入: createVariable() 据它把字符串转成正确的 SNMP 变量类型。validatePoint() 把 oid 列为必填,读位号缺 oid 校验不通过。
写入复用位号属性,无需另配写命令
写入和读取共用位号上的同一份 oid / snmpType:write()(SET)从 pointConfig(point-attribute)读取 oid 与 snmpType ,对该 OID 发 SET、按 snmpType 构造写入值。可写位号只要在 point 上配好 oid 与 snmpType 即可,不需要在写命令上重复填。
createVariable() 支持的 snmpType 取值:INTEGER/INTEGER32、GAUGE32/COUNTER32/UNSIGNED_INTEGER32、COUNTER64、 TIMETICKS、OID、IPADDRESS、NULL,其余一律按 OCTET_STRING 处理。
command-attribute 当前不被写入路径读取
application.yml 里声明了 command-attribute(oid / snmpType),但 write() 的签名只接收 driverConfig 与 pointConfig,并不传入命令属性,本驱动也未覆写 execute(),因此这组 command-attribute 目前是占位声明、写入时不会被读取。请把可写位号的 oid / snmpType 配在位号(point)上,而非写命令上——否则写入会落到位号缺省值(oid 为空、snmpType=OCTET_STRING)。
采集与健康
- 采集周期:默认 cron
0/30 * * * * ?(每 30 秒采一轮,对应schedule.read.cron)。 - 健康/在线:设备健康检查默认 cron
0/15 * * * * ?,租约超时45 秒。health()以"设备是否已建立 SNMP 会话"判定在线——clientMap里有该设备则视为 online,没有则尝试建会话,建成即 online。在线状态机制见设备。
SNMP 会话的建立不等于设备可达
getConnector() 建的是本地 UDP transport(DefaultUdpTransportMapping),listen() 成功就缓存为"online" ,并不向设备发探测包。也就是说设备掉线后,健康检查仍可能短暂报 online,真正的失败要等下一次 read() 超时——此时驱动会 clientMap.remove(deviceId) 销毁会话,下一轮健康检查才转 offline。
故障排查
OID 末尾常带 .0,别漏
标量(单值)对象的 OID 末尾要带实例标识 .0,例如 sysDescr 是 1.3.6.1.2.1.1.1.0 而不是 1.3.6.1.2.1.1.1 。表项(如各端口流量)则用行索引结尾(如 ...10.1、...10.2)。OID 写错时设备返回 noSuchObject/noSuchInstance, variable.toString() 会把它当普通字符串上报成位号值,看起来"采到了"却是无效数据,排查时容易被误导。
community 写错会静默超时
SNMP 用 community 团体名做口令。团体名不匹配、或设备未对该团体开放访问时,设备通常不回包,snmp.send() 返回的 response.getResponse() 为 null,驱动抛 ReadPointException("SNMP response is null...")。这表现为请求超时而非明确的" 鉴权失败"。接入前先在命令行用 snmpget -v2c -c public <host> 1.3.6.1.2.1.1.1.0 确认 host、port、community、oid 这一组能取到值。
防火墙拦 UDP 161 / 设备未启用 SNMP
SNMP 走 UDP 而非 TCP,许多防火墙默认放行 TCP 却拦 UDP;交换机/服务器也常默认关闭 SNMP agent。现象同样是超时。先确认目标设备已启用 SNMP 服务、并放行了管理端到设备 UDP 161 的入站流量。
接入前,建议先在命令行用 net-snmp 工具验证这条链路是否通——驱动用的是同一套 SNMP4J 语义,命令行取不到值就先别在 DC3 里建设备:
# 最小连通性验证
snmpget -v2c -c public 192.168.1.20:161 1.3.6.1.2.1.1.1.0
# 取不到值时逐项排除:host 可 ping 通?UDP 161 放行?community 对?SNMP 已启用?
snmpwalk -v2c -c public 192.168.1.20:161 1.3.6.1.2.1.1 # 遍历 system 子树看设备是否应答version 只能填 v1 / v2c,填 v3 会被当 v2c
驱动 buildTarget() 只识别 v1(不区分大小写),其余任何值——包括 v3——都走 version2c 分支。若设备只允许 SNMPv3,用本驱动连不上,且不会有"版本不支持"的明确报错,只会表现为 community 校验失败导致的超时。请确认设备允许 v1/v2c 访问。
写入返回 true 不代表设备已接受
write() 只要拿到非空 response 就返回 true,并未校验响应 PDU 的 errorStatus。某些设备对只读 OID 或越权写会返回带错误码的响应而非不回包,此时驱动仍判成功。写关键参数后建议回读该 OID 确认实际生效。
在 IoT DC3 中如何落地
dc3.driver.code:SnmpDriver(驱动名SNMP Driver,类型DRIVER_CLIENT,主动连设备)。这是稳定的路由标识,不可随意改。- 读 / 写 / 订阅能力:读 ✓、写 ✓、订阅 —,与驱动能力矩阵一致。驱动作为 SNMP 管理端主动轮询,不监听设备上报,因此没有订阅方向。
实现状态:可用
SnmpDriverCustomServiceImpl 的 read()(GET)、write()(SET)、getConnector()(会话管理)、health()、event() (设备更新/删除时销毁会话)均已实现,基于 SNMP4J 的 v1/v2c 收发链路完整可用。已知边界:SNMPv3/USM 未接(见上文 USM 三项说明)、写入未校验响应 errorStatus、健康检查为本地会话存活判定而非端到端探测。这些是当前实现的取舍,不影响 v1/v2c 的常规采集与写值。
最小接入示例——把 IP 192.168.1.20:161、团体名 public 的一台交换机接进来,采集它的系统描述(sysDescr,OID 1.3.6.1.2.1.1.1.0):
- 选
SNMP Driver创建设备,driver 属性填host=192.168.1.20、port=161、version=v2c、community=public。 - 给设备绑定的模板加一个描述位号(
pointTypeFlag=STRING、READ_ONLY),point 属性填oid=1.3.6.1.2.1.1.1.0。 - 启动驱动,30 秒内就能在位号值里看到设备的系统描述字符串。
完整接入流程见设备接入。