无垠之码

深度剖析代码之道


简单网络管理协议SNMP

SNMP是一种用于监控诸如,路由器,交换机,打印机,服务器,防火墙等设备的管理协议。它运行在UDP协议之上,常用端口是161,162。其中161端口用于管理请求,162端口用于trap告警通知。

作为IETF定义的一种标准协议(RFC:3411-3418),存在很多实现版本(每种实现针对不同场景和需求进行了优化或扩展),比如最常用的开源版本net-snmp,使用纯JAVA语言实现的SNMP4J,PYTHON编写的PySNMP,运用在嵌入式设备、高性能网络设备Agent的SNMP++版本,甚至很多厂商都有自己的私有实现,下文的所有操作都基于net-snmp版本

sudo apt install snmp snmpd snmptrapd snmp-mibs-downloader 
sudo download-mibs   // 下载mibs数据库

SNMP的全称简单网络管理协议,简单的含义是仅包含少数核心操作,但其实现可能相当复杂。下表列出协议规定需要实现的基本操作

操作 解释
GET 获取某个值,例如设备名、系统运行时间
GETNEXT 获取下一个OID的值(用于遍历)
SET 修改某个值,例如设置位置、启用端口
WALK 连续GETNEXT操作,相当于扫描整个MIB树
TRAP 设备主动发送事件通知(如链路断开)
INFORM 主动发送事件通知的同时,需接收方ACK确认,可靠的消息投递

1.核心概念


术语 解释
Manager(管理者) 发起查询、控制请求的设备(比如 NMS 网络管理系统)
Agent(代理) 被管理设备上运行的进程,响应请求并发送trap
MIB(管理信息库) 存储管理数据的数据库,结构是树形
OID(对象标识符) 唯一标识一个MIB对象,例如系统名、CPU 使用率等
Community String 类似密码,控制访问(public只读,private可写)

对象标识符

用于唯一标识网络设备中可管理对象的标识符。它们在MIB(管理信息库)中定义,并遵循树状的结构。OID主要有三种表示形式,便于人类阅读的模块::名字形式(SNMPv2-MIB::sysDescr.0),适合机器解析的纯数字形式(.1.3.6.1.2.1.1.1.0),混合形式(iso.3.6.1.2.1.1.1.0),详细定义可移步至参考文献1

示例:1.3.6.1.2.1.1.1.0

数字部分 名称 含义
1 iso 国际标准化组织
3 org 组织(Organization)
6 dod 国防部(Department of Defense)
1 internet Internet相关
2 mgmt 管理
1 mib-2 管理信息库版本2
1 system 系统信息
1 sysDescr 系统描述
0 实例编号 表示这是一个具体实例

资源视图

在SNMP中,“资源视图”(View)是SNMP中的重要概念,属于访问控制模型(VACM View-based Access Control Model)的一部分。它用于定义一个用户可以访问或不能访问哪些OID子树

# 定义systemonly资源视图,该视图仅能访问
view   systemonly  included   .1.3.6.1.2.1.1
view   systemonly  included   .1.3.6.1.2.1.25.1

权限管理

SNMPv1和SNMPv2c并没有用户的概念,它们通过community字符串来进行身份验证和访问控制,数据使用明文传输的协议

# rocommunity是标准的配置命令,public相当于通信密码,default表示任意访问源(限制访问源)
rocommunity  public default -V systemonly
rocommunity6 public default -V systemonly

SNMPv3中不再使用rocommunity,取而代之需要使用createuser创建用户,认证算法使用SHA-512,加密算法使用AES,加密算法用于数据传输

# authpriv表示认证(auth)+加密(priv),用户也可以设置成
createuser authPrivUser SHA-512 myauthphrase AES myprivphrase
rouser authPrivUser authpriv -V systemonly
安全级别 认证(auth) 加密 配置中对应的写法
noAuthNoPriv 无认证 无加密 noauth
authNoPriv 有认证 无加密 auth
authPriv 有认证 有加密 authpriv

上述配置是典型的RFC3414标准的USM配置方案(User-based Security Model),其实关于SNMP协议模块化的实现方案RFCS-3410-3419中描述运行用户选择安全方案。

image

  • USM目前工业界事实标准
  • TSM(Transport Security Model),将认证/加密从SNMP消息层剥离到传输层,支持多种安全传输协议(如DTLS、SSH、TLS),单个安全通道可承载多个SNMP会话,未全部支持
  • Kerberos安全模型,大多数实现版本不支持该安全模型

2.基本操作


  1. snmptranslate,OID的数字形式与文本形式的相互转换
# 数字形式
snmptranslate -On .iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0
.1.3.6.1.2.1.1.1.0

# 全路径名称
snmptranslate -Of .1.3.6.1.2.1.1.1.0
.iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0

# 模块::名字
snmptranslate .1.3.6.1.2.1.1.1.0
SNMPv2-MIB::sysDescr.0
snmptranslate -IR sysUpTime.0
SNMPv2-MIB::sysUpTime.0

# 所有加载的模块
# 1. net-snmp定义的模块
# 2. 自定义的模块
# 3. iana定义的模块
# 4. ietf模块,一般需要访问的oid都位于此目录中
tree /usr/share/snmp/mibs 
.
├── LM-SENSORS-MIB.txt    # lm-sensors工具(Linux下硬件传感器监控)设计的一个MIB模块文件
├── NET-SNMP-AGENT-MIB.txt
├── NET-SNMP-EXAMPLES-MIB.txt
├── NET-SNMP-EXTEND-MIB.txt
├── NET-SNMP-MIB.txt
├── NET-SNMP-PASS-MIB.txt
├── NET-SNMP-TC.txt
├── NET-SNMP-VACM-MIB.txt
├── UCD-DEMO-MIB.txt
├── UCD-DISKIO-MIB.txt
├── UCD-DLMOD-MIB.txt
├── UCD-IPFWACC-MIB.txt
├── UCD-SNMP-MIB.txt
├── iana -> /var/lib/mibs/iana  # IANA MIB模块主要用于存储和管理IANA分配的各种标准化编号、协议、地址族等信息
└── ietf -> /var/lib/mibs/ietf

# 查询功能,使用正则表达式查询所有符合条件的对象
snmptranslate -TB memory
UCD-SNMP-MIB::memory
HOST-RESOURCES-MIB::hrMemorySize

# 打印snmp数结构
snmptranslate -Tp IF-MIB::ifInOctets
+-- -R-- Counter   ifInOctets(10)

# MIB文档的即时查阅​
snmptranslate -Td IF-MIB::ifInOctets
IF-MIB::ifInOctets
ifInOctets OBJECT-TYPE
  -- FROM       IF-MIB
  SYNTAX        Counter32
  MAX-ACCESS    read-only
  STATUS        current
  DESCRIPTION   "The total number of octets received on the interface,
            including framing characters.

            Discontinuities in the value of this counter can occur at
            re-initialization of the management system, and at other
            times as indicated by the value of
            ifCounterDiscontinuityTime."
::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) interfaces(2) ifTable(2) ifEntry(1) 10 }
  1. snmpget,snmpwalk,获取对象

强调:访问的资源一定要包含在访问视图中

snmpwalk -v2c -c public 127.0.0.1 HOST-RESOURCES-MIB::hrSWRunTable
HOST-RESOURCES-MIB::hrSWRunIndex.1 = INTEGER: 1
HOST-RESOURCES-MIB::hrSWRunID.1 = OID: SNMPv2-SMI::zeroDotZero
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "systemd"
HOST-RESOURCES-MIB::hrSWRunPath.1 = STRING: "/sbin/init"
HOST-RESOURCES-MIB::hrSWRunParameters.1 = ""
HOST-RESOURCES-MIB::hrSWRunType.1 = INTEGER: application(4)
HOST-RESOURCES-MIB::hrSWRunStatus.1 = INTEGER: runnable(2)

# 更好展示table类型数据
snmptable -v2c -c public 127.0.0.1 HOST-RESOURCES-MIB::hrSWRunTable

# 获取hrSWRunTable的数据结构,这里的R表示该字段可读,RW表示读写
# Textual Convention表示文本性的约束,国际化的显示字符串
# hrSWRunIndex(1)表示结构中的第一个元素

snmptranslate -Tp HOST-RESOURCES-MIB::hrSWRunTable
+--hrSWRunTable(2)
   |
   +--hrSWRunEntry(1)
      |  Index: hrSWRunIndex
      |
      +-- -R-- Integer32 hrSWRunIndex(1)
      |        Range: 1..2147483647
      +-- -R-- String    hrSWRunName(2)
      |        Textual Convention: InternationalDisplayString
      |        Size: 0..64
      +-- -R-- ObjID     hrSWRunID(3)
      |        Textual Convention: ProductID
      +-- -R-- String    hrSWRunPath(4)
      |        Textual Convention: InternationalDisplayString
      |        Size: 0..128
      +-- -R-- String    hrSWRunParameters(5)
      |        Textual Convention: InternationalDisplayString
      |        Size: 0..128
      +-- -R-- EnumVal   hrSWRunType(6)
      |        Values: unknown(1), operatingSystem(2), deviceDriver(3), application(4)
      +-- -RW- EnumVal   hrSWRunStatus(7)
               Values: running(1), runnable(2), notRunnable(3), invalid(4)

snmpget -v2c -c public 127.0.0.1 HOST-RESOURCES-MIB::hrMemorySize.0
HOST-RESOURCES-MIB::hrMemorySize.0 = INTEGER: 2018684 KBytes
  1. snmpset,设置对象

snmp除了可以查询当前设备的相关运行状态,网络流量,软件包信息,也可以设置一些基本策略,比如开关网卡,防火墙访问控制策略,甚至通过net-snmp的扩展模块,可以远程执行预设操作,当然动作都是在拥有足够的权限的条件下才能完成

snmpset -v2c -c private 127.0.0.1 ifAdminStatus.2 i 2   # 关闭第二个接口

比如下面的例子中使用extend指令扩展net-snmp功能调用脚本,使用pass指令将特定子树前缀的访问特殊处理

# 修改snmpd.conf文件增加如下内容,pass_persist使用长连接

extend test /tmp/test.sh
pass .1.3.6.1.4.1.2021.999 /usr/local/bin/mysnmphandler
pass_persist .1.3.6.1.4.1.2021.998 /usr/local/bin/mysnmpd.py

snmptranslate -On NET-SNMP-EXTEND-MIB::nsExtendOutput1Line.\"test\"
snmpget -v2c -c public 127.0.0.1 .1.3.6.1.4.1.8072.1.3.2.3.1.1.4.116.101.115.116
snmpwalk -v2c -c public 127.0.0.1 NET-SNMP-EXTEND-MIB::nsExtendObjects

3.高级案例


  1. 配置trap规则,发送告警信息

注意: ubuntu-snmpd需要修改snmpd进程的启动项,开启mteTrigger和mteTriggerConf功能
mte,Managed Trigger and Event表示管理触发器和事件

使用trapsink或者net-snmp支持的新版指令trapsess,指定trap信息的接受方

# 受控主机配置trapsink
trapsink 127.0.0.1 public   # 定义trap信息的接受目的,trapsess 127.0.0.1 public  
createuser internalUser SHA-512 myauthphrase AES myprivphrase
rouser internalUser noauth -V systemonly
iquerySecName internalQuery # 指定内部查询使用的安全名称
linkUpDownNotifications yes # 当网卡状态发生变化时,发送trap信息
authtrapenable  1           # snmp身份验证失败的信息发送至服务器
defaultMonitors yes         # 启用默认的监控
monitor -r 5 machineTooBusy hrProcessorLoad > 60 # 定义声明一个DisMan监控规则,每5秒检测一次,cpu使用率达到60,触发trap信息

trap告警信息的接受方,使用authCommunity命令配置,运行public密钥的发送端,将接收到的trap信息记录到日志中,运行执行与trap相关联的脚本,允许将trap转发到其它网络位置

authCommunity log,execute,net public
traphandle default /usr/bin/logger -t snmptrap

使用stress工具,在snmptrap主机观测

stress -c 4

Apr 24 14:50:04 zabbix snmptrap: <UNKNOWN>
Apr 24 14:50:04 zabbix snmptrap: UDP: [192.168.5.57]:60176->[192.168.5.59]:162
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::sysUpTimeInstance 0:0:01:05.02
Apr 24 14:50:04 zabbix snmptrap: SNMPv2-MIB::snmpTrapOID.0 DISMAN-EVENT-MIB::mteTriggerFired
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::mteHotTrigger.0 machineTooBusy
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::mteHotTargetName.0 
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::mteHotContextName.0 
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::mteHotOID.0 HOST-RESOURCES-MIB::hrProcessorLoad.196609
Apr 24 14:50:04 zabbix snmptrap: DISMAN-EVENT-MIB::mteHotValue.0 100
Apr 24 14:50:04 zabbix snmptrap: SNMP-COMMUNITY-MIB::snmpTrapAddress.0 192.168.5.57
Apr 24 14:50:04 zabbix snmptrap: SNMP-COMMUNITY-MIB::snmpTrapCommunity.0 "public"
Apr 24 14:50:04 zabbix snmptrap: SNMPv2-MIB::snmpTrapEnterprise.0 DISMAN-EVENT-MIB::dismanEventMIBNotificationPrefix

使用snmpinform命令可以发送,inform消息,在未收到对方应答的情况,发送方会重试再次发送,直到消息真正送达。只有snmpv2以上版本支持该消息

createUser -e 0x0102030405 trap MD5 test AES test # 需要详细阅读man 5 snmptrapd.conf
disableAuthorization yes # 尝试多次,增加身份认证的v3版本,均未成功,直接关闭认证

snmpinform -v 3 -u trap localhost 42 SNMPv2-MIB::coldStart
snmptrap -e 0x0102030405 -v 3 -u trap localhost 42 SNMPv2-MIB::coldStart

2025-04-25 03:28:11 <UNKNOWN> [UDP: [192.168.5.57]:42990->[192.168.5.59]:162]:
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (42) 0:00:00.42        SNMPv2-MIB::snmpTrapOID.0 = OID: SNMPv2-MIB::coldStart

配合agentxtrap,可以由外部脚本/子代理手动触发告警信息

实际部署中,一个系统可能有多个子进程负责不同的监控任务,而这些任务希望在检测到异常时发出Trap。但又不希望每个进程都实现完整的SNMP协议,通过AgentX协议,把这些子进程变成snmpd的"子代理",共享OID空间,通过agentxtrap工具向snmpd报告trap,由snmpd统一处理和发送,下图简单展示zabbix从主机192.168.5.57采集的相关数据

  1. zabbix结合可视化

在zabbix面板中添加主机,比如这里的192.168.5.57该主机运行snmpd服务,选择snmp模板,将其分组至服务器,配置接口为snmp,等待一分钟后,snmp图标绿色表示数据采集成功

image image image

4.参考文献

  1. https://oidref.com/
  2. https://www.net-snmp.org/
comments powered by Disqus