SNMP协议实践
SNMP协议实践
一、背景
物联网时代,软件系统的建设越来越多的需要与设备、设备采集通信协议进行打交道。
工作中,遇到对华为桌面云实现统一监控运维的项目需求。基本功能要求是,通过对华为桌面云FA、FC平台北向接口的数据采集,实现对华为桌面云服务器运行状态的监测,包括CPU使用率、内存、网络、硬盘等使用情况,故障告警的监听处理等内容。技术预研阶段涉及对SNMP协议的预研了解,以下是对SNMP协议内容的一些总结,涉及内容主要如下:
- Snmp基本概念
- linux下安装和配置SNMP
- JAVA开源组件snmp4j
二、Snmp基本概念
2.1 什么是SNMP协议
SNMP是英文"Simple Network Management Protocol"的缩写,简单网络管理协议(SNMP) 是专门设计用于在 IP 网络管理网络节点(服务器、工作站、路由器、交换机、HUBS等)的一种标准协议,它属于TCP/IP五层协议中的应用层协议,用于网络管理的协议。SNMP主要用于网络设备的管理。由于SNMP协议简单可靠 ,受到了众多厂商的欢迎,成为了目前最为广泛的网管协议。
SNMP协议由SNMP管理站(manager)和SNMP代理(Agent)两部分构成。SNMP管理站是中心节点,负责收集维护各个SNMP元素的信息,并对这些信息进行处理;SNMP代理运行在各个被管理的网络节点上,负责统计该节点的各项指标信息,并且负责与SNMP管理站交互,接收并执行管理站的命令,上传各种本地的网络信息。
2.2 SNMP组成
一套完整的SNMP系统主要包括管理信息库(MIB)、管理信息结构(SMI)及SNMP报文协议。
- MIB:任何一个被管理的资源都表示成一个对象,称为被管理的对象。MIB是被管理对象的集合。它定义了被管理对象的一系列属性:对象的名称、对象的访问权限和对象的数据类型等。每个SNMP设备(Agent)都有自己的MIB。MIB也可以看作是NMS和Agent之间的沟通桥梁。
- SMI:SMI定义了SNMP框架所用信息的组织、组成和标识,它还为描述MIB对象和描述协议怎样交换信息奠定了基础。
- SNMP报文:SNMP协议定义了数据包的格式,一条SNMP消息由"版本号"、"SNMP共同体名"和"协议数据单元(PDU)"构成,数据包的长度不是固定的。
2.3 SNMP协议工作方式
SNMP主要采用UDP协议在manager和agent之间传输信息。 SNMP采用UDP 161端口接收和发送请求,162端口接收trap,执行SNMP的设备缺省都必须采用这些端口。SNMP消息全部通过UDP端口161接收,只有Trap信息采用UDP端口162。
简单讲,SNMP管理站通过UDP协议向SNMP代理发送各种命令,当SNMP代理收到命令后,返回SNMP管理站需要的参数。当SNMP代理检测到网络元素异常的时候,也可以主动向SNMP管理站发送消息,通告当前异常状况。SNMP协议提供了三种用于控制MIB对象的基本操作命令:
- Get:用于读取代理者处对象的值。它是SNMP协议中使用率最高的一个命令,该命令是从网络设备中获得管理信息的基本方式。
- Set:manager需要向设备执行设置操作,SNMP提供了Set操作,可以通过它来改动设备的配置或控制设备的运转状态。它可以设置设备的名称、关掉一个端口或清除一个地址解析表中的项等。
- Trap: agent主动向manager通报重要事件。它的主要用途是在网络管理系统没有明确要求的前提下,由管理代理通知网络管理系统有一些特别的情况或问题 发生了。如果发生意外情况,客户会向服务器的162端口发送一个消息,告知服务器指定的变量值发生了变化。通常由服务器请求而获得的数据由服务器的161 端口接收。Trap 消息可以用来通知管理站线路的故障、连接的终端和恢复、认证失败等消息。管理站可相应的作出处理。
2.4 SNMP 协议版本
SNMP协议在1988年被制定,1992年发布了SNMPv2版本,以增强SNMPv1的安全性和功能。目前SNMP协议共有v1,v2,v3三个版本:
- SNMP v1是SNMP协议的最初版本,不过依然是众多厂家实现SNMP基本方式。
- SNMP v2通常被指是基于community的SNMP V2。Community实质上就是**。
- SNMP v3 是最新版本的SNMP。它对网络管理最大的贡献在于其安全性。增加了对认证和密文传输的支持。
三、linux下安装和配置SNMP
3.1安装snmp
# 安装SNMP
yum install -y net-snmp*
# 备份SNMP配置
/etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.bak
# 添加修改配置
vim /etc/snmp/snmpd.conf
rocommunity SZ-SNMP-PRIVATE
group notConfigGroup v1 notConfigUser
group notConfigGroup v2c notConfigUser
# 重启SNMP
service snmpd restart
# 查看主机
snmpwalk -v 2c -c SZ-SNMP-PRIVATE 192.168.1.112
3.2 常用OID
系统参数(1.3.6.1.2.1.1) | |||
---|---|---|---|
OID | 描述 | 备注 | 请求方式 |
.1.3.6.1.2.1.1.1.0 | 获取系统基本信息 | SysDesc | GET |
.1.3.6.1.2.1.1.3.0 | 监控时间 | sysUptime | GET |
.1.3.6.1.2.1.1.4.0 | 系统联系人 | sysContact | GET |
.1.3.6.1.2.1.1.5.0 | 获取机器名 | SysName | GET |
.1.3.6.1.2.1.1.6.0 | 机器坐在位置 | SysLocation | GET |
.1.3.6.1.2.1.1.7.0 | 机器提供的服务 | SysService | GET |
.1.3.6.1.2.1.25.4.2.1.2 | 系统运行的进程列表 | hrSWRunName | WALK |
.1.3.6.1.2.1.25.6.3.1.2 | 系统安装的软件列表 | hrSWInstalledName | WALK |
网络接口(1.3.6.1.2.1.2) | |||
---|---|---|---|
OID | 描述 | 备注 | 请求方式 |
.1.3.6.1.2.1.2.1.0 | 网络接口的数目 | IfNumber | GET |
.1.3.6.1.2.1.2.2.1.2 | 网络接口信息描述 | IfDescr | WALK |
.1.3.6.1.2.1.2.2.1.3 | 网络接口类型 | IfType | WALK |
.1.3.6.1.2.1.2.2.1.4 | 接口发送和接收的最大IP数据报[BYTE] | IfMTU | WALK |
.1.3.6.1.2.1.2.2.1.5 | 接口当前带宽[bps] | IfSpeed | WALK |
.1.3.6.1.2.1.2.2.1.6 | 接口的物理地址 | IfPhysAddress | WALK |
.1.3.6.1.2.1.2.2.1.8 | 接口当前操作状态[up|down] | IfOperStatus | WALK |
.1.3.6.1.2.1.2.2.1.10 | 接口收到的字节数 | IfInOctet | WALK |
.1.3.6.1.2.1.2.2.1.16 | 接口发送的字节数 | IfOutOctet | WALK |
.1.3.6.1.2.1.2.2.1.11 | 接口收到的数据包个数 | IfInUcastPkts | WALK |
.1.3.6.1.2.1.2.2.1.17 | 接口发送的数据包个数 | IfOutUcastPkts | WALK |
CPU及负载 | |||
---|---|---|---|
OID | 描述 | 备注 | 请求方式 |
. 1.3.6.1.4.1.2021.11.9.0 | 用户CPU百分比 | ssCpuUser | GET |
. 1.3.6.1.4.1.2021.11.10.0 | 系统CPU百分比 | ssCpuSystem | GET |
. 1.3.6.1.4.1.2021.11.11.0 | 空闲CPU百分比 | ssCpuIdle | GET |
. 1.3.6.1.4.1.2021.11.50.0 | 原始用户CPU使用时间 | ssCpuRawUser | GET |
.1.3.6.1.4.1.2021.11.51.0 | 原始nice占用时间 | ssCpuRawNice | GET |
. 1.3.6.1.4.1.2021.11.52.0 | 原始系统CPU使用时间 | ssCpuRawSystem. | GET |
. 1.3.6.1.4.1.2021.11.53.0 | 原始CPU空闲时间 | ssCpuRawIdle | GET |
. 1.3.6.1.2.1.25.3.3.1.2 | CPU的当前负载,N个核就有N个负载 | hrProcessorLoad | WALK |
. 1.3.6.1.4.1.2021.11.3.0 | ssSwapIn | GET | |
. 1.3.6.1.4.1.2021.11.4.0 | SsSwapOut | GET | |
. 1.3.6.1.4.1.2021.11.5.0 | ssIOSent | GET | |
. 1.3.6.1.4.1.2021.11.6.0 | ssIOReceive | GET | |
. 1.3.6.1.4.1.2021.11.7.0 | ssSysInterrupts | GET | |
. 1.3.6.1.4.1.2021.11.8.0 | ssSysContext | GET | |
. 1.3.6.1.4.1.2021.11.54.0 | ssCpuRawWait | GET | |
. 1.3.6.1.4.1.2021.11.56.0 | ssCpuRawInterrupt | GET | |
. 1.3.6.1.4.1.2021.11.57.0 | ssIORawSent | GET | |
. 1.3.6.1.4.1.2021.11.58.0 | ssIORawReceived | GET | |
. 1.3.6.1.4.1.2021.11.59.0 | ssRawInterrupts | GET | |
. 1.3.6.1.4.1.2021.11.60.0 | ssRawContexts | GET | |
. 1.3.6.1.4.1.2021.11.61.0 | ssCpuRawSoftIRQ | GET | |
. 1.3.6.1.4.1.2021.11.62.0 | ssRawSwapIn. | GET | |
. 1.3.6.1.4.1.2021.11.63.0 | ssRawSwapOut | GET | |
.1.3.6.1.4.1.2021.10.1.3.1 | Load5 | GET | |
.1.3.6.1.4.1.2021.10.1.3.2 | Load10 | GET | |
.1.3.6.1.4.1.2021.10.1.3.3 | Load15 | GET |
内存及磁盘(1.3.6.1.2.1.25) | |||
---|---|---|---|
OID | 描述 | 备注 | 请求方式 |
.1.3.6.1.2.1.25.2.2.0 | 获取内存大小 | hrMemorySize | GET |
.1.3.6.1.2.1.25.2.3.1.1 | 存储设备编号 | hrStorageIndex | WALK |
.1.3.6.1.2.1.25.2.3.1.2 | 存储设备类型 | hrStorageType[OID] | WALK |
.1.3.6.1.2.1.25.2.3.1.3 | 存储设备描述 | hrStorageDescr | WALK |
.1.3.6.1.2.1.25.2.3.1.4 | 簇的大小 | hrStorageAllocationUnits | WALK |
.1.3.6.1.2.1.25.2.3.1.5 | 簇的的数目 | hrStorageSize | WALK |
.1.3.6.1.2.1.25.2.3.1.6 | 使用多少,跟总容量相除就是占用率 | hrStorageUsed | WALK |
.1.3.6.1.4.1.2021.4.3.0 | Total Swap Size(虚拟内存) | memTotalSwap | GET |
.1.3.6.1.4.1.2021.4.4.0 | Available Swap Space | memAvailSwap | GET |
.1.3.6.1.4.1.2021.4.5.0 | Total RAM in machine | memTotalReal | GET |
.1.3.6.1.4.1.2021.4.6.0 | Total RAM used | memAvailReal | GET |
.1.3.6.1.4.1.2021.4.11.0 | Total RAM Free | memTotalFree | GET |
.1.3.6.1.4.1.2021.4.13.0 | Total RAM Shared | memShared | GET |
.1.3.6.1.4.1.2021.4.14.0 | Total RAM Buffered | memBuffer | GET |
.1.3.6.1.4.1.2021.4.15.0 | Total Cached Memory | memCached | GET |
.1.3.6.1.4.1.2021.9.1.2 | Path where the disk is mounted | dskPath | WALK |
.1.3.6.1.4.1.2021.9.1.3 | Path of the device for the partition | dskDevice | WALK |
.1.3.6.1.4.1.2021.9.1.6 | Total size of the disk/partion (kBytes) | dskTotal | WALK |
.1.3.6.1.4.1.2021.9.1.7 | Available space on the disk | dskAvail | WALK |
.1.3.6.1.4.1.2021.9.1.8 | Used space on the disk | dskUsed | WALK |
.1.3.6.1.4.1.2021.9.1.9 | Percentage of space used on disk | dskPercent | WALK |
.1.3.6.1.4.1.2021.9.1.10 | Percentage of inodes used on disk | dskPercentNode | WALK |
四、JAVA开源组件snmp4j
4.1 snmp4j简介
SNMP4J是一个用Java来实现SNMP协议的开源项目.它支持以命令行的形式进行管理与响应。SNMP4J是纯面向对象设计与SNMP++(用C++实现SNMPv1/v2c/v3)相类似。
SNMP4J API 提供以下下特性:
- 支持MD5和SHA验证,DES,3DES,AES128、AES192和AES256加密的SNMPv3。
- 支持MPv1,MPv2C和MPv3,带执行的可阻塞的信息处理模块。
- 全部PDU格式。
- 可阻塞的传输拓扑。支持UPD、TCP、TLS 。
- 可阻塞的超时模块。
- 同步和异步请求。
- 命令发生器以及命令应答器的支持。
- 基于Apache license的开源免费。
- JAVA 1.4.1或更高版本(2.0或更高版本需要jdk1.6及以上的支持)。
- 基于LOG4J记录日志。
- 使用GETBULK实现Row-based的有效的异步表格获取。
- 支持多线程。
4.2 snmp4j示例
jar包引入
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.7.0</version>
</dependency>
示例代码
package com.wf.example.snmp;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;
import java.util.Vector;
/**
* @author wf
* @ClassName:SnmpUtil
* @Description: SNMP协议
* @date 2021/11/16 14:36
*/
public class SnmpDemo {
private Snmp snmp = null;
private Address targetAddress = null;
//SNMP 代理地址
private String agentAddress="";
//SNMP trap上报监听地址
private String trapAddress="";
//[身份认证]组织团体名称
private String agentCommunity="";
/**
* ========================
* 测试SNMP协议
* 1、get方法获取信息(只演示了同步方式)
* 2、set方法修改信息
* 3、trap监听告警上报
* ========================
*/
public static void main(String[] args) {
try {
//本机(windows 10)测试:GET获取信息【成功】、SET修改信息【成功】
System.out.println("======本机测试开始======");
getPDUTest("udp:127.0.0.1/161","public");
setPDUTest("udp:127.0.0.1/161","public");
//配合模拟发送程序进行接收:SnmpUtilSendTrap
SnmpDemo util = new SnmpDemo("udp:127.0.0.1/161","192.168.215.160/162","public");
util.listen();
System.out.println("======本机测试结束======");
//121.37.0.11(linux)服务器测试:GET获取信息【成功】、SET修改信息【成功】
System.out.println("======121.37.0.11服务器测试开始======");
getPDUTest("udp:121.37.0.11/161","SZ-SNMP-PRIVATE");
//setPDUTest("udp:121.37.0.11/161","SZ-SNMP-PRIVATE");
System.out.println("======121.37.0.11服务器测试结束======");
//192.168.21.110 华为云桌面测试服务器:trap报警【成功】、GET获取信息【成功】
System.out.println("======192.168.22.110服务器测试开始======");
//getPDUTest("udp:192.168.22.110/161","talkweb");
SnmpDemo util1 = new SnmpDemo("udp:192.168.22.110/161","192.168.215.160/162","talkweb");
util1.listen();
System.out.println("======192.168.22.110服务器测试结束======");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* GET测试,需要预装配置SNMP协议,并进行团体名称,读写权限配置
* 1、测试通过:读取计算机名称
*/
public static void getPDUTest(String agentAddress,String agentCommunity)throws IOException{
//本机测试==测试通过
SnmpDemo util = new SnmpDemo(agentAddress,agentCommunity);
// 获取机器名 .1.3.6.1.2.1.1.5.0
// 获取系统基本信息 .1.3.6.1.2.1.1.1.0
OID[] oids=new OID[]{new OID(".1.3.6.1.2.1.1.5.0"),new OID(".1.3.6.1.2.1.1.1.0")};
//获取计算机名称
util.getPDU(oids);
//设置计算机名称 DESKTOP-E109030 |
}
/**
* SET测试,需要预装配置SNMP协议,并进行团体名称,读写权限配置
* 新建团体名称:默认权限是只读,写入权限需要另外分配设置
* 1、测试通过:改写计算机名称(只能改写到MIB库中,不能反馈到计算机的信息面板)
*/
public static void setPDUTest(String agentAddress,String agentCommunity)throws IOException{
//本机测试==测试通过
SnmpDemo util = new SnmpDemo(agentAddress,agentCommunity);
// 机器名 .1.3.6.1.2.1.1.5.0
VariableBinding[] variableBindings=new VariableBinding []{new VariableBinding(new OID(".1.3.6.1.2.1.1.5.0"),new OctetString("ttt"))};
//获取计算机名称
util.setPDU(variableBindings);
}
public SnmpDemo(String agentAddress, String agentCommunity) throws IOException{
this.agentAddress=agentAddress;
this.agentCommunity=agentCommunity;
this.initComm();
}
/**
* 初始化
*/
public void initComm() throws IOException {
// 设置Agent方的IP和端口
targetAddress = GenericAddress.parse(agentAddress);
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
transport.listen();
}
/**
* 通过OID获得一个或多个信息
* @param oids
*/
public void getPDU(OID [] oids) throws IOException {
PDU pdu = new PDU();
for (OID oid:oids){
pdu.add(new VariableBinding(oid));
}
pdu.setType(PDU.GET);
readResponse(sendPDU(pdu));
}
/**
* 设置变更
* @param variableBindings 变更绑定
*/
public void setPDU(VariableBinding [] variableBindings) throws IOException {
PDU pdu = new PDU();
for(VariableBinding variableBinding:variableBindings){
pdu.add(variableBinding);
}
pdu.setType(PDU.SET);
//发送
readResponse(sendPDU(pdu));
}
/**
* 发送信息
*/
public ResponseEvent sendPDU(PDU pdu) throws IOException {
// 设置target
CommunityTarget target = new CommunityTarget();
//设置 团体名称
target.setCommunity(new OctetString(agentCommunity));
//设置地址
target.setAddress(targetAddress);
// 通信不成功时的重试次数
target.setRetries(2);
// 超时时间
target.setTimeout(1500);
//指定协议
target.setVersion(SnmpConstants.version2c);
// 向Agent发送PDU,并返回Response
ResponseEvent event = snmp.send(pdu, target);
//关闭
snmp.close();
return event;
}
/**
* 读取信息
* @param respEvnt
*/
public void readResponse(ResponseEvent respEvnt) {
// 解析Response
if (respEvnt != null && respEvnt.getResponse() != null) {
// System.out.println(respEvnt.getPeerAddress());
System.out.println("【请求】"+respEvnt.getRequest());
System.out.println("【返回】"+respEvnt.getResponse());
Vector<VariableBinding> recVBs = (Vector<VariableBinding>) respEvnt.getResponse().getVariableBindings();
for (int i = 0; i < recVBs.size(); i++) {
VariableBinding recVB = recVBs.elementAt(i);
System.out.println(recVB.getOid() + " : " + recVB.getVariable());
}
}
}
////////////////////////////////////////////////////////////
private TransportMapping transport = null;
public SnmpDemo(String agentAddress, String trapAddress, String agentCommunity)throws IOException {
this.agentAddress=agentAddress;
this.agentCommunity=agentCommunity;
//监听地址
this.trapAddress=trapAddress;
this.initComm1();
}
public void initComm1() throws IOException {
// 设置Agent方的IP和端口
targetAddress = GenericAddress.parse(agentAddress);
// 设置接收trap的IP和端口
transport = new DefaultUdpTransportMapping(new UdpAddress(trapAddress));
snmp = new Snmp(transport);
CommandResponder trapRec = new CommandResponder() {
@Override
public synchronized void processPdu(CommandResponderEvent e) {
// 接收trap
PDU command = e.getPDU();
if (command != null) {
System.out.println(command.toString());
}
VariableBinding value = null;
/*Iterator<VariableBinding> size = command.getVariableBindings().iterator();
while(size.hasNext()){
value = size.next();
System.out.println(value.getOid().toString()+" : "+value.getVariable().toString());
}*/
}
};
snmp.addCommandResponder(trapRec);
transport.listen();
}
public synchronized void listen() {
System.out.println("Waiting for traps..");
try {
this.wait();//Wait for traps to come in
} catch (InterruptedException ex) {
System.out.println("Interrupted while waiting for traps: " + ex);
System.exit(-1);
}
}
}
学习了。