简介:一套面向网络管理与系统集成人员的SNMP代理实现包,侧重演示SNMP协议中的GET、SET与TRAP三类操作。实现基于C语言与MIB管理信息库,覆盖对象查询、远程配置修改和异常主动上报场景,适合需要理解SNMP协议栈、进行网络设备管理开发或实验调试的技术人员参考学习。包体信息:压缩包约9.75MB,共480个文件,以124个h头文件、106个c源码和154个html帮助文档为主体,附带编译生成的obj、exe、日志与工程配置等文件,源码与构建产物并存,便于对照阅读和运行验证。已有402人学习下载,属于轻量实用的协议学习资料。通过完整工程目录与MIB、GET/SET处理、TRAP上报等模块划分,读者可以从零搭建SNMP代理,快速理解请求处理流程、OID寻址机制与主动上报逻辑;同时,多处示例代码清晰展示关键API调用方式,为后续二次开发或网络管理平台集成提供直接参考。 今天被一个报错折腾了半天:The agent execution provider did not respond in time。我下意识以为是网络设备上的SNMP Agent出了问题,查来查去发现是某台数据库服务器的调度任务超时。虽然方向错了,但这事提醒我一个关键点——很多人排查网络监控时,根本分不清SNMP Agent和系统里的各种Agent代理。正好借这个机会,把SNMP Agent这摊事从头到尾捋一遍。
本文的核心是SNMP Agent,也就是网络设备、服务器上那个负责被网管系统轮询和上报状态的小代理程序。我会把它是什么、怎么在CentOS 7.6上配好SNMP v3、弱口令怎么防、以及如何自己开发一个Agent,一次性讲透。适合网络运维、监控平台开发、以及所有被监控折腾过的人。
1. SNMP Agent到底是个啥角色:先分清管理者与被管理者
1.1 从一次误判说起
先说那个报错。The agent execution provider did not respond in time是SQL Server Agent Job执行时,后台执行宿主没在规定时间内响应——跟网络SNMP八竿子打不着。但我当时第一反应确实是"某台交换机的agent挂了",因为日常监控告警里,SNMP Agent不响应是最常见的故障之一。
这个误判本身说明一个事实:SNMP Agent在网络运维里的存在感太强、太基础了,基础到出了问题会下意识往它身上想。实际上,SNMP Agent是网络管理架构里的"被管理端"角色,它跑在路由器、交换机、服务器、打印机这些设备上,通过UDP 161端口接收网管系统的查询请求,再通过UDP 162端口主动上报告警(Trap)。你只有先把这个角色关系搞清楚,后面配置、调优、开发才不会乱。
1.2 Agent、NMS与MIB:三者少一个都玩不转
一套能用的SNMP监控体系,至少有三个角色:
- NMS(Network Management Station):网管系统,想方设法去"问"设备的那个。
- SNMP Agent:设备上的代理进程,负责回答NMS的"问"。
- MIB(Management Information Base):一个逻辑上的树状数据库,NMS问什么、Agent答什么,都由MIB定义。
这里有个容易绕晕的点:MIB不是Agent里存的一个个文件,而是一套"逻辑规则"。Agent真正维护的是各种实时数据,比如CPU占用、接口流量、设备温度。MIB定义了这些数据在OID树上的位置和类型。NMS拿到一串OID,比如1.3.6.1.2.1.1.1.0,Agent按这个OID去取对应数据返回。
我习惯把Agent比作前台服务员,MIB是菜单,OID是菜品的编号。你报编号(OID),服务员(Agent)去后厨(系统内核、硬件驱动)取菜,再按固定格式端上来。SNMPv1和v2c版本时,菜单还分"公开菜单"和"私有菜单",公开菜单用public团体字串(community string),部分设备还能用private改配置——这就是后面要聊的弱口令问题的根源。
1.3 常用OID速查表
刚开始接触的人最头疼的是记OID。不要求全背,但下面这几组必须眼熟,因为配好Agent之后第一件事就是用它们验证:
| OID | 名称 | 说明 |
|---|---|---|
| 1.3.6.1.2.1.1.1.0 | sysDescr | 设备型号、系统描述 |
| 1.3.6.1.2.1.1.5.0 | sysName | 设备主机名 |
| 1.3.6.1.2.1.1.3.0 | sysUpTime | 设备运行时长 |
| 1.3.6.1.2.1.2.2.1.10 | ifInOctets | 接口入方向流量字节数 |
| 1.3.6.1.2.1.2.2.1.16 | ifOutOctets | 接口出方向流量字节数 |
| 1.3.6.1.4.1.2021.11.9.0 | memAvailReal | net-snmp扩展:可用内存 |
厂商私有MIB的OID通常在1.3.6.1.4.1下面,根据不同企业编号区分。比如中某厂商的设备流量OID就可能在私有节点下面,这时候你光用标准OID查不到,得去厂商官网下MIB文件导入监控系统。后文排查部分我会再提。
2. CentOS 7.6配置SNMP v3:从安装到snmpwalk验证一条龙
2.1 安装和初始配置
CentOS 7.6上配置SNMP Agent,用的基本是net-snmp套件。先装两个包:
yum install -y net-snmp net-snmp-utilsnet-snmp是Agent守护进程,net-snmp-utils是客户端工具集,里面的snmpwalk、snmpget、snmptrap都是后面验证要用的。装完先别急着启动,我先改配置。初始配置文件在/etc/snmp/snmpd.conf,你打开看一下会发现里面全是注释,其实只要几行就能跑起来:
# 允许本机使用public只读访问 rocommunity public 127.0.0.1 # 允许监控网段使用monitor作为只读团体字串 rocommunity monitor 192.168.1.0/24 # 指定Agent监听地址和端口 agentaddress udp:161这里有个习惯问题。很多人图省事只写rocommunity public,等于对所有来源IP开放了只读权限,这是典型的安全隐患。我建议无论什么环境,rocommunity后面一定跟一个网段或者IP限制。想改配置还不想开放写权限?默认就不开写,你只有显式配置rwcommunity才会有写权限,尽量别配。
2.2 创建SNMP v3用户
v2c时代用团体字串当"密码",明文在网络里传输,安全性很差。SNMP v3引入了用户、认证和加密,能解决这个问题。创建v3用户,net-snmp提供了现成脚本,比手改配置省事得多:
systemctl stop snmpd net-snmp-create-v3-user -ro -a SHA -A AuthPass123 -x AES -X PrivPass123 snmpmon systemctl start snmpd简单解释下参数:-ro是只读权限,-a SHA表示认证协议用SHA,-x AES表示加密协议用AES,大写的-A和-X分别指定认证密码和加密密码。执行完,脚本会把用户信息追加到/var/lib/net-snmp/snmpd.conf里,所以要先停服务再创建,否则可能被运行中的进程覆盖。密码别用我这里的示例,至少要12位以上混合字符。
为什么不推荐直接在/etc/snmp/snmpd.conf里写createUser?因为那个文件权限通常不够严,而且容易在重启时被系统重写。用官方脚本生成的用户信息单独存放,出问题时排查也方便。
2.3 验证是否跑通
配置完一定要亲自验证,别信"看起来没问题"。先确认服务状态和端口:
systemctl status snmpd netstat -tulnp | grep 161然后先用v2c验证基础通信:
snmpwalk -v2c -c monitor 127.0.0.1 1.3.6.1.2.1.1.1.0正常会返回类似SNMPv2-MIB::sysDescr.0 = STRING: Linux centos76 3.10.0-957.el7.x86_64这样的信息。再验证v3:
snmpwalk -v3 -u snmpmon -l authPriv -a SHA -A 'AuthPass123' -x AES -X 'PrivPass123' 127.0.0.1 1.3.6.1.2.1.1.5.0这里-l authPriv表示使用认证加加密模式,这是v3最安全的组合。如果这条能返回sysName,说明Agent配置成功。注意,如果服务器开了防火墙,记得放行UDP 161端口:
firewall-cmd --permanent --add-port=161/udp firewall-cmd --reload3. 银河麒麟、Windows的SNMP配置差异与坑
3.1 银河麒麟安装SNMP Agent
这两年国产化环境越来越多,银河麒麟系统上装SNMP Agent的需求也起来了。麒麟系统基于Linux内核,整体思路和CentOS一致,就是包管理有差异——银河麒麟V10的桌面版和服务器版,有的用yum,有的用apt。建议先执行cat /etc/os-release看清版本再动手。
如果是yum系,直接:
yum install -y net-snmp net-snmp-utils如果是apt系,则是:
apt install -y snmpd snmp麒麟上配置文件的路径同样是/etc/snmp/snmpd.conf,v3用户创建方式也一致。我实际遇到过的一个坑是麒麟系统默认把snmpd服务做成了socket激活,也就是说你不访问它,它不一定常驻,导致外部监控平台频繁"超时"。解决办法是禁用socket激活,改成传统常驻服务:
systemctl stop snmpd.socket snmpd.service systemctl mask snmpd.socket systemctl enable --now snmpd.service3.2 Windows上启用或关闭SNMP服务
Windows作为被监控端时,SNMP的配置路径和Linux完全不同,而且从Windows 10 1809开始,微软把SNMP列为了弃用功能,新系统里默认根本不装。
如果想启用,在"设置-应用-可选功能-添加功能"里找到"简单网络管理协议(SNMP)",安装后服务列表里会出现"SIMPLE-TCP"里的SNMP Service。右键服务属性,可以配置"团体"名称和"接受来自这些主机的SNMP数据包"——这里只填监控服务器的IP,别填任何格式的public。Windows的注册表里也能配置,但说实话,同一网段内用界面配置更快。
至于"Win10关闭SNMP"这个需求,本质上是卸载可选功能或停用SNMP服务。在服务管理器里把SNMP Service设为禁用,通常就能挡住大部分轮询请求。如果还不放心,就在Windows防火墙里禁止入站UDP 161端口,再从公网角度检查,确认端口不可达。关闭时要注意,有些监控软件依赖SNMP做资产上报,关了可能误报,操作前先确认监控平台里没有绑它的终端。
4. SNMP弱口令到底有多危险:一次内部排查复盘
4.1 public/private是怎么被盯上的
热词里出现了"SNMP弱口令连接",这个词让我想起一次内部安全巡检。当时我们用扫描脚本扫了整个机房网段,结果吓一跳——有十几台设备还开着SNMP v1,community用的是出厂默认的public。
原理其实很简单,SNMPv1和v2c本身不支持加密,也没有用户概念,它靠一个明文传输的团体字串(community string)做身份识别。设备默认public为只读字串,部分老设备甚至默认private可写。如果你的设备还保持着出厂默认,那任何能到达这台设备UDP 161端口的人,都能用snmpwalk把系统信息、接口流量、路由表、ARP表捞一遍,等于把网络拓扑打印出来送人。
更严重的是private可写。配合snmpget和snmpset,攻击者可以改设备名、改接口描述、重启某些模块。虽然不能直接拿到设备shell,但破坏性已经很可观。很多设备的SNMP服务还运行在管理VLAN里,问题就出在管理VLAN被弱口令直接暴露了。
4.2 加固手段对照表
那次巡检之后,我把加固动作沉淀成了一张清单,每次新设备上线都过一遍:
| 隐患 | 加固手段 | 备注 |
|---|---|---|
| 使用public/private默认串 | 改为随机生成的强community串 | 至少16位,含大小写、数字、符号 |
| 全局开放UDP 161 | 限制管理网段源IP | 在设备ACL或防火墙层做 |
| 允许写权限 | 移除rwcommunity配置 | 日常监控只给只读权限 |
| 使用SNMPv1/v2c明文 | 升级到SNMP v3 authPriv | 核心设备必须上v3 |
| 开放了大量子节点 | 使用view限制可见OID | 只暴露sysName、ifTable等必要节点 |
| 暴露到公网 | 在边界封锁UDP 161/162 | 内网设备不应对公网可见 |
有一次我甚至遇到某设备开启SNMP后,管理员为了让"监控方便",把所有MIB节点都开放了,结果连snmpCommunityTable都能远程读出来。所以光改community还不算完,建议在配置里用view限定可见范围:
view systemonly included .1.3.6.1.2.1.1 view systemonly included .1.3.6.1.2.1.2这样Agent只对这两个子树下被请求的OID做响应,其他一律返回noSuchObject,数据暴露面就小多了。
5. 自己开发一个SNMP Agent:选型与最小实现思路
5.1 为什么不要从零搓协议
聊完配置,再说说开发。不少人一听到"Agent开发",第一反应是拿起Python从零实现SNMP报文解析,这就掉坑里了。SNMP消息基于ASN.1 BER编码,虽然规范不算复杂,但自己撸编解码,光处理各种数据类型的TLV就能耗掉大量精力,还容易出错。
正确的做法是站在现有库的肩膀上。Linux服务端的开发框架,推荐直接用net-snmp的pass_persist指令做脚本化扩展,或者用pysnmp、agentx协议写子代理。如果你是给设备写嵌入式Agent,那才需要考虑轻量级实现,比如用lwip或一些c版本的小型SNMP栈。
有一个判断标准我常用:如果你需要监控的是"Linux服务器上的自定义程序状态",直接写pass_persist脚本最靠谱,改起来快,不重新编译;如果你要把Agent嵌到自研硬件或路由器里,那才值得引入完整的C语言SNMP框架。
5.2 通过pass_persist挂到snmpd实现自定义OID
pass_persist是一种让外部程序接管某一段OID子树的机制。你把自己开发节点的命令写到snmpd.conf里,然后snmpd收到针对这段OID的请求时,会把请求转发给你的脚本处理。
举个例子,假设你的程序搞了一个自定义OID1.3.6.1.4.1.12345.1.0来表示"队列长度",你可以这样写:
pass_persist .1.3.6.1.4.1.12345 /opt/custom_agent/my_agent.py对应的Python脚本核心逻辑是:
#!/usr/bin/env python3 import sys BASE_OID = "1.3.6.1.4.1.12345.1.0" def process_request(cmd, oid): if cmd == "PING": out = ["PONG"] elif cmd in ("GET", "GETNEXT"): # 这里实际应该根据真实队列长度取值 out = [BASE_OID, "integer", "42"] elif cmd == "SET": out = ["not-writable"] else: out = [] print("\n".join(out)) sys.stdout.flush() for line in sys.stdin: parts = line.strip().split() if not parts: continue process_request(parts[0], parts[1] if len(parts) > 1 else "")脚本交互的大致流程是:snmpd启动后向脚本发PING,脚本回PONG;收到GET或GETNEXT请求时,脚本按"OID、类型、值"三行格式返回。注意,脚本必须在标准输出里显式刷新缓冲区,否则snmpd等不到结果会超时。这个模式改起来方便,多个自定义指标就往BASE_OID下面加子节点,但要注意性能,别在脚本里做重型计算或数据库查询,snmpd轮询频率高时容易拖垮Agent。
5.3 实际开发中的几个注意点
开发SNMP Agent过程中,我踩过的坑主要有三个。
第一,OID节点的返回类型必须和MIB定义严格一致。比如你MIB里定义的是Counter64,返回Integer在部分NMS上会解析失败。最简单的方式是尽量用现有类型,integer、string能覆盖大部分场景。
第二,一定要考虑并发和超时。snmpd默认超时时间很短,脚本处理时间稍长,上层轮询就会报Timeout。我可以把耗时操作异步化,返回一个最近结果缓存值,保证脚本响应在100ms以内。
第三,安全别忘。自定义Agent暴露在网络上,至少要支持SNMP v3,这样监控平台才能以认证用户连接你。不要为了调试方便,在脚本里留下一个连v2c都能访问的口子。
6. snmpwalk排查实录与常见报错对照
6.1 排查思路
最后分享一些实战排查经验。碰到监控平台"无数据"时,我的排查路线基本固定:
第一步,在NMS机器上直接snmpwalk目标设备,排除监控平台自身问题。第二步,查网络连通性,telnet 主机 161连不上不代表UDP不通,用nc -vnzu 主机 161配合Agent端tcpdump看有没有包到达。第三步,确认Agent本身在跑,systemctl status snmpd加netstat -tulnp | grep 161。第四步,确认OID是否正确,有些设备私有OID要加载MIB文件才能返回正确数据。
有一个排查原则:永远先确认Agent是好的,再去怀疑监控平台或NMS配置。你在被监控设备上用snmpwalk都能拿到数据,再回监控平台调模板、调OID,思路就清晰多了。
6.2 常见报错速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| Timeout: No Response from 主机 | Agent未启动、UDP端口被封、community错误 | 检查snmpd状态、防火墙放行、核对community名称 |
| No Such Object available | 请求的OID在当前MIB子树下不存在 | 确认设备是否支持该节点,导入厂商MIB后重试 |
| Bad value for community | 团体字串错误 | 检查snmpd.conf的rocommunity配置 |
| 接口流量OID返回0 | 接口索引ifIndex不匹配或设备型号私有规则 | 先walkifDescr拿到接口索引,再查对应流量OID |
| 端口占用,无法启动snmpd | 161端口被其他程序占用 | netstat查占用进程,或改用agentaddress指定其他端口 |
| Unknown user name | SNMP v3用户尚未创建或密码串错 | 用net-snmp-create-v3-user重建用户,核对认证加密参数 |
那个开头提到的The agent execution provider did not respond in time,如果你遇到时排查的是数据库定时任务,先别怀疑网络设备。它是SQL Server Agent Job的执行宿主超时,通常和SQL Agent服务异常、任务负载过大、WMI服务挂起有关。这类报错千万记得:先看报错主体的完整信息,别被"agent"两个字带偏。
最后再分享一个我的个人习惯:新装任何一台要接入监控的设备,第一件事就是snmpwalk把整棵OID树拉一遍,确认Agent本身是好的,才去折腾监控平台那边的模板和MIB。这个'先验证Agent,再排查上层'的顺序,能省下大量无意义的排查时间。
本文还有配套的精品资源,点击获取