Snmp JavaAPI
本文只对snmp做简单介绍,主要内容为如何使用及易错点提示,,文章最后贴代码。
1.简单介绍
SNMP:“简单网络管理协议”,用于网络管理的协议。SNMP用于网络设备的管理。SNMP的工作方式:管理员需要向设备获取数据,所以SNMP提供了get操作;管理员需要向设备执行设置操作,所以SNMP提供了set操作;设备需要在重要状况改变的时候,向管理员通报事件的发生,所以SNMP提供了Trap操作。
1.1 Snmp相关概念介绍
1)操作命令
SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap。
Get:管理站读取代理者处对象的值。它是SNMP协议中使用率最高的一个命令,因为该命令是从网络设备中获得管理信息的基本方式。
Set:管理站设置代理者处对象的值
Trap: 代理者主动向管理站通报重要事件。Trap 消息可以用来通知管理站线路的故障、连接的终端和恢复、认证失败等消息。管理站可相应的作出处理。使用指南
2)SNMP的消息构成
一条snmp消息由版本识别符、团体名、PDU组成:
1. 版本识别符:用于说明现在使用的是哪个版本的SNMP协议,确保SNMP代理使用相同的协议,每个SNMP代理都直接抛弃与自己协议版本不同的数据报。
2. 团体名:团体是基本的安全机制,用于实现SNMP网络管理员访问SNMP管理代理时的身份验证。类似于密码,默认值为 public。
3. 协议数据单元(PDU):PDU (协议数据单元)是SNMP消息中的数据区, 即Snmp通信时报文数据的载体。PDU指明了SNMP的消息类型及其相关参数
3)MIB与OID
1.MIB(管理信息库)
管理信息(MIB)库可以理解成为agent维护的管理对象数据库, MIB数据对象以一种树状分层结构进行组织,这个树状结构中的每个分枝都有一个专用的名字和一个数字形式的标识符。使用这个树状分层结构,MIB浏览器能够以一种方便而且简洁的方式访问整个MIB数据库。可以通过其数字标识符来查找MIB中的数据对象,这个数字标识符号从结构树的顶部(或根部)开始,直到各个叶 子节点(即数据对象)为止。
每一个节点都有一个对象标识符(OID)来唯一的标识,每个节点用数字和字符两种方式显示,其中对象标识符OID是由句点隔开的一组整数,也就是从根节点 通向它的路径。一个带标号节点可以拥有包含其它带标号节点为它的子树,如果没有子树它就是叶子节点,它包含一个值并被称为对象。比如网络设备名的oid 是.1.3.6.1.2.1.1.5.0,其值为设备名称的字符串。
2.OID(Object Identifier)
每个管理对象都有自己的OID(Object Identifier),管理对象通过树状结构进行组织,OID由树上的一系列整数组成,整数之间用点( . )分隔开,树的叶子节点才是真正能够被管理的对象。
1.2 主要代码分析
这该模块对snmp的get、walk、trap三种方式的业务处理进行介绍。
执行过程概述
驻留在被管设备上的AGENT从UDP端口161接受来自网管站的串行化报文,经解码、团体名验证、分析得到管理变量在MIB树中对应的节点,从相应的模块中得到管理变量的值,再形成响应报文,编码发送回网管站。网管站得到响应报文后,再经同样的处理,最终显示结果。下面根据RFC1157详细介绍Agent接受到报文后采取的动作:首先解码生成用内部数据结构表示的报文,解码依据ASN.1的基本编码规则,如果在此过程中出现错误导致解码失败则丢弃该报文,不做进一步处理。第二步:将报文中的版本号取出,如果与本Agent支持的SNMP版本不一致,则丢弃该报文,不做进一步处理。当前北研的数据通信产品只支持SNMP版本1。第三步:将报文中的团体名取出,此团体名由发出请求的网管站填写。如与本设备认可的团体名不符,则丢弃该报文,不做进一步处理,同时产生一个陷阱报文。SNMPv1只提供了较弱的安全措施,在版本3中这一功能将大大加强。第四步:从通过验证的ASN.1对象中提出协议数据单元PDU,如果失败,丢弃报文,不做进一不处理。否则处理PDU,结果将产生一个报文,该报文的发送目的地址应同收到报文的源地址一致。根据不同的PDU,SNMP协议实体将做不同的处理
1.3 环境准备
在项目中添加maven jar包依赖,如下图所示。
1.4 代码实现
1.4.1 Trap和get方式获取信息
- 初始化snmp,并开启监听
- 初始化get和walk方式请求的目标对象信息
- 根据oid创建请求的报文
- 对返回的消息体进行处理Get方式:
Walk方式:
5. 初始化、创建目标对象、创建报文与get方式一致,此处住在赘述
1.4.2 Trap方式
初始化与get方式一致,此处不在赘述
1.Trap消息处理方法,此方法会接收trap推送的信息
2 常见问题
1.使用get方式请求获取信息,一直返回 noSuchObject说明没有找到该节点,需要确定该oid是否正确。若没有返回说明连接不正常,需要检查各个版本、用户名、密码、协议、团体名等配置信息是否正确。
2. 创建目标对象时,使用v1、v2版本时需要指定团体名,使用v3需要指定安全名。
3.配置v3用户时,最好通过管理口进行配置,web页面配置可能不生效。
3.主要代码demo
3.1get方式
package com.snmp.collection.get_collection.collectionforII;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.snmp4j.*;import org.snmp4j.event.ResponseEvent;import org.snmp4j.mp.MPv1;import org.snmp4j.mp.MPv2c;import org.snmp4j.mp.MPv3;import org.snmp4j.mp.SnmpConstants;import org.snmp4j.security.*;import org.snmp4j.smi.*;import org.snmp4j.transport.DefaultTcpTransportMapping;import org.snmp4j.transport.DefaultUdpTransportMapping;import org.snmp4j.util.MultiThreadedMessageDispatcher;import org.snmp4j.util.ThreadPool;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.net.UnknownHostException;import java.util.*;/**
* @description: get方式获取数据
* @author: yghhz
* @create: 2020-11-15 10:14
**/publicclassMultiThreadedGetDemo{privatestaticfinal Logger LOGGER = LogManager.getLogger(MultiThreadedGetDemo.class);//用户名private String username ="nms2-admin";//鉴权密码private String authPassword ="hello123";//数据加密密码private String privPassword ="hello123";//trap地址private String address ="udp:192.160.0.1/162";//get 地址private String addressGet ="udp:192.160.0.2/161";private MultiThreadedMessageDispatcher dispatcher;private Snmp snmp = null;private Address listenAddress;private ThreadPool threadPool;publicMultiThreadedGetDemo(){}publicvoidinitSnmp()throws IOException {//1、初始化多线程消息转发类
MessageDispatcher messageDispatcher =newMessageDispatcherImpl();//其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping<? extends Address> transportMapping) ,就不需要增加
messageDispatcher.addMessageProcessingModel(newMPv1());
messageDispatcher.addMessageProcessingModel(newMPv2c());//当要支持snmpV3版本时,需要配置user
OctetString localEngineID =newOctetString(MPv3.createLocalEngineID());
USM usm =newUSM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID,0);
OctetString userName1 =newOctetString(username);
OctetString authPass =newOctetString(authPassword);
OctetString privPass =newOctetString(privPassword);
UsmUser user =newUsmUser(userName1, AuthMD5.ID, authPass, PrivAES128.ID, privPass);
usm.addUser(user.getSecurityName(), user);
messageDispatcher.addMessageProcessingModel(newMPv3(usm));//2、创建transportMapping ip为本地ip,可以不设置
TransportMapping<?> transportMapping =newDefaultUdpTransportMapping();//3、正式创建snmp
snmp =newSnmp(messageDispatcher, transportMapping);//开启监听
snmp.listen();}private Target createTarget(String oid){
Target target = null;int version =1;if(!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)){//log.error("参数version异常");return target;}if(version == SnmpConstants.version3){
target =newUserTarget();//snmpV3需要设置安全级别和安全名称,其中安全名称是创建snmp指定user设置的new OctetString("SNMPV3")
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target.setSecurityName(newOctetString(this.username));}else{//snmpV1和snmpV2需要指定团体名名称
target =newCommunityTarget();((CommunityTarget) target).setCommunity(newOctetString(this.username));if(version == SnmpConstants.version2c){
target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);}}
target.setVersion(version);//必须指定,没有设置就会报错。
target.setAddress(GenericAddress.parse(this.addressGet));
target.setRetries(3);
target.setTimeout(2000);return target;}privatestatic PDU createPDU(int version,int type, String oid){
PDU pdu = null;if(version == SnmpConstants.version3){
pdu =newScopedPDU();}else{
pdu =newPDUv1();}
pdu.setType(type);//可以添加多个变量oid/*for(String oid:oids){
pdu.add(new VariableBinding(new OID(oid)));
}*/
pdu.add(newVariableBinding(newOID(oid)));return pdu;}/**
* GET方式请求
* @param oid
*/public List<Map>snmpGet(String oid){try{
LOGGER.info("getfangshi");
List<Map> list =newArrayList<Map>();//1、初始化snmp,并开启监听initSnmp();//2、创建目标对象
Target target =this.createTarget(oid);//3、创建报文
PDU pdu =createPDU(1, PDU.GET, oid);//4、发送报文,并获取返回结果
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();if(response == null){
System.out.println("TimeOut...");}else{if(response.getErrorStatus()== PDU.noError){//get方式获取到的返回值
Vector<?extendsVariableBinding> vbs = response.getVariableBindings();for(VariableBinding vb : vbs){
Map map =newHashMap();
map.put("value",vb.getVariable());
System.out.println("vb.getVariable():"+ vb.getVariable());
System.out.println("OID:"+ vb.getVariable());
LOGGER.info("OIDvALUE"+ vb.getVariable());
list.add(map);}return list;}else{
System.out.println("Error:"+ response.getErrorStatusText());}}}catch(IOException e){
e.printStackTrace();}return null;}//开启监控的main方法。publicstaticvoidmain(String[] args)throws IOException {
MultiThreadedGetDemo multithreadedtrapreceiver =newMultiThreadedGetDemo();
multithreadedtrapreceiver.run();}publicvoidrun(){try{
System.out.println("开始监听get信息!");
LOGGER.info("内存");
String devOid ="1.3.6.1.4.1.25506.2.6.1.1.1.1.8";this.snmpGet(devOid);
LOGGER.info("cpu");
String devOid1 ="1.3.6.1.4.1.25506.2.6.1.1.1.1.6";this.snmpGet(devOid1);
LOGGER.info("运行时间");
String data1 ="1.3.6.1.2.1.1.3.0";this.snmpGet(data1);}catch(Exception ex){
ex.printStackTrace();}}}
3.2Trap方式
/**
* @description: trap监听
* @author: yghhz
* @create: 2020-11-15 10:14
**/publicclassMultiThreadedTrapReceiverDemoimplementsCommandResponder{privatestaticfinal Logger logger = LogManager.getLogger(MultiThreadedTrapReceiverDemo.class);//用户名private String username ="nms2-admin";//鉴权密码private String authPassword ="hello123";//数据加密密码private String privPassword ="hello123";//trap地址private String address ="udp:192.160.0.1/162";//get 地址private String addressGet ="udp:192.160.0.2/161";private MultiThreadedMessageDispatcher dispatcher;private Snmp snmp = null;private Address listenAddress;private ThreadPool threadPool;publicMultiThreadedTrapReceiverDemo(){}privatevoidinit()throws UnknownHostException, IOException {try{//创建接收SnmpTrap的线程池,参数: 线程名称及线程数
threadPool = ThreadPool.create("Trap",2);//创建一个多线程消息分发器,以同时处理传入的消息,该实例将用于分派传入和传出的消息
dispatcher =newMultiThreadedMessageDispatcher(threadPool,newMessageDispatcherImpl());//监听端的 ip地址 和 监听端口号
listenAddress = GenericAddress.parse(address);//在指定的地址上创建UDP传输
TransportMapping<?> transport;if(listenAddress instanceofUdpAddress){//必须是本机地址
transport =newDefaultUdpTransportMapping((UdpAddress) listenAddress);}else{
transport =newDefaultTcpTransportMapping((TcpAddress) listenAddress);}//初始化snmp需要设置messageDispatcher里面的参数和TransportMapping参数
snmp =newSnmp(dispatcher, transport);//消息分发器添加接收的版本信息/* v1和v2都具有基本的读、写MIB功能。*
* v2增加了警报、批量数据获取、管理站和管理站通信能力。*
* v3在v2的基础上增加了USM,使用加密的数据和用户验证技术,提高了安全性*/
snmp.getMessageDispatcher().addMessageProcessingModel(newMPv3());
snmp.getMessageDispatcher().addMessageProcessingModel(newMPv2c());
snmp.getMessageDispatcher().addMessageProcessingModel(newMPv1());//创建具有所提供安全协议支持的USM
USM usm =newUSM(SecurityProtocols.getInstance(),newOctetString(MPv3 //根据本地IP地址和其他四个随机字节创建本地引擎ID.createLocalEngineID()),0);
SecurityModels.getInstance().addSecurityModel(usm);// 添加安全协议,如果没有发过来的消息没有身份认证,可以跳过此段代码
SecurityProtocols.getInstance().addDefaultProtocols();// 创建和添加用户
OctetString userName1 =newOctetString(username);
OctetString authPass =newOctetString(authPassword);
OctetString privPass =newOctetString(privPassword);
UsmUser usmUser1 =newUsmUser(userName1, AuthMD5.ID, authPass, PrivAES128.ID, privPass);//因为接受的Trap可能来自不同的主机,主机的Snmp v3加密认证密码都不一样,所以根据加密的名称,来添加认证信息UsmUser。//添加了加密认证信息的便可以接收来自发送端的信息。
UsmUserEntry userEnty1 =newUsmUserEntry(userName1, usmUser1);
UsmUserTable userTable = snmp.getUSM().getUserTable();// 添加其他用户
userTable.addUser(userEnty1);//开启Snmp监听,可以接收来自Trap端的信息。
snmp.listen();
logger.info("开始监听trap告警");}catch(Exception e){
e.printStackTrace();
logger.info(e.getMessage());}}publicvoidrun(){try{init();
snmp.addCommandResponder(this);
System.out.println("开始监听Trap信息!");}catch(Exception ex){
ex.printStackTrace();}}/**
* 实现CommandResponder的processPdu方法, 用于处理传入的请求、PDU等信息
* 当接收到trap时,会自动进入这个方法
*
* @param respEvnt
*/publicvoidprocessPdu(CommandResponderEvent respEvnt){// 解析Response
logger.info("trap接受到告警消息,开始对消息进行处理");try{if(respEvnt != null && respEvnt.getPDU()!= null){
RtEvent rtEvent =newRtEvent();for(int i =0; i < respEvnt.getPDU().getVariableBindings().size(); i++){
logger.info("消息体oid:"+respEvnt.getPDU().getVariableBindings().elementAt(i).getOid());
logger.info("消息体oid对应值:"+respEvnt.getPDU().getVariableBindings().elementAt(i).getVariable());}//获取对方交换机等设备的ip
String ip = respEvnt.getPeerAddress().toString().split("/")[0].split(":")[0];
rtEvent.setIp(ip);//获取设备以及trap信息
DevAndTrapOid devAndTrapOid = ModelCache.getInstance().getDevAndTrapOid(ip);@SuppressWarnings("unchecked")
Vector<VariableBinding> recVBs =(Vector<VariableBinding>) respEvnt.getPDU().getVariableBindings();for(int i =0; i < recVBs.size(); i++){
VariableBinding recVB = recVBs.elementAt(i);
String value ="点号"+ recVB.getOid()+";内容:"+ recVB.getVariable();
logger.info("二区trap初始snmp,点号:"+ recVB.getOid()+";内容:"+ recVB.getVariable());
System.out.println(value);}}}catch(Exception e){
e.printStackTrace();}}//开启监控的main方法。publicstaticvoidmain(String[] args)throws IOException {
MultiThreadedTrapReceiverDemo multithreadedtrapreceiver =newMultiThreadedTrapReceiverDemo();
multithreadedtrapreceiver.run();}}
3.3walk方式
/**
* @description: walk方式获取数据
* @author: yghhz
* @create: 2020-11-15 10:14
**/publicclassMultiThreadedWalkDemo{privatestaticfinal Logger LOGGER = LogManager.getLogger(MultiThreadedWalkDemo.class);//用户名private String username ="nms2-admin";//鉴权密码private String authPassword ="hello123";//数据加密密码private String privPassword ="hello123";//trap地址private String address ="udp:192.160.0.1/162";//get 地址private String addressGet ="udp:192.160.0.2/161";publicMultiThreadedWalkDemo(){}publicvoidinitSnmp()throws IOException {//1、初始化多线程消息转发类
MessageDispatcher messageDispatcher =newMessageDispatcherImpl();//其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping<? extends Address> transportMapping) ,就不需要增加
messageDispatcher.addMessageProcessingModel(newMPv1());
messageDispatcher.addMessageProcessingModel(newMPv2c());//当要支持snmpV3版本时,需要配置user
OctetString localEngineID =newOctetString(MPv3.createLocalEngineID());
USM usm =newUSM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID,0);
OctetString userName1 =newOctetString(username);
OctetString authPass =newOctetString(authPassword);
OctetString privPass =newOctetString(privPassword);
UsmUser user =newUsmUser(userName1, AuthMD5.ID, authPass, PrivAES128.ID, privPass);
usm.addUser(user.getSecurityName(), user);
messageDispatcher.addMessageProcessingModel(newMPv3(usm));//2、创建transportMapping ip为本地ip,可以不设置//UdpAddress updAddr = (UdpAddress) GenericAddress.parse("udp:192.168.22.100/161");
TransportMapping<?> transportMapping =newDefaultUdpTransportMapping();//3、正式创建snmp
snmp =newSnmp(messageDispatcher, transportMapping);//开启监听
snmp.listen();}private Target createTarget(String oid){
Target target = null;int version =1;if(!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)){//log.error("参数version异常");return target;}if(version == SnmpConstants.version3){
target =newUserTarget();//snmpV3需要设置安全级别和安全名称,其中安全名称是创建snmp指定user设置的new OctetString("SNMPV3")
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target.setSecurityName(newOctetString(this.username));}else{//snmpV1和snmpV2需要指定团体名名称
target =newCommunityTarget();((CommunityTarget) target).setCommunity(newOctetString(this.username));if(version == SnmpConstants.version2c){
target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);}}
target.setVersion(version);//必须指定,没有设置就会报错。
target.setAddress(GenericAddress.parse(this.addressGet));
target.setRetries(3);
target.setTimeout(2000);return target;}privatestatic PDU createPDU(int version,int type, String oid){
PDU pdu = null;if(version == SnmpConstants.version3){
pdu =newScopedPDU();}else{
pdu =newPDUv1();}
pdu.setType(type);//可以添加多个变量oid/*for(String oid:oids){
pdu.add(new VariableBinding(new OID(oid)));
}*/
pdu.add(newVariableBinding(newOID(oid)));return pdu;}/**
* WALK方式请求
* @param oid
*/publicvoidsnmpWalk(String oid){try{
LOGGER.info("walkfangshi");
List<Map> list =newArrayList<Map>();//1、初始化snmp,并开启监听initSnmp();//2、创建目标对象
Target target =createTarget(oid);//3、创建报文
PDU pdu =createPDU(1, PDU.GETNEXT, oid);//4、发送报文,并获取返回结果boolean matched =true;while(matched){
ResponseEvent responseEvent = snmp.send(pdu, target);if(responseEvent == null || responseEvent.getResponse()== null){
System.out.println("TimeOut...");break;}
PDU response = responseEvent.getResponse();
String nextOid = null;
Vector<?extendsVariableBinding> variableBindings = response.getVariableBindings();for(int i =0; i < variableBindings.size(); i++){
Map map =newHashMap();
VariableBinding variableBinding = variableBindings.elementAt(i);
Variable variable = variableBinding.getVariable();
nextOid = variableBinding.getOid().toDottedString();//如果不是这个节点下的oid则终止遍历,否则会输出很多,直到整个遍历完。if(!nextOid.startsWith(oid)){
matched =false;break;}
map.put("oid", nextOid);
map.put("value",variable);
list.add(map);
System.out.println("oid:"+ nextOid +",value:"+ variable);
LOGGER.info("oid:"+ nextOid +",value:"+ variable);}if(!matched){break;}
pdu.clear();
pdu.add(newVariableBinding(newOID(nextOid)));}}catch(IOException e){// TODO Auto-generated catch block
e.printStackTrace();}}//开启监控的main方法。publicstaticvoidmain(String[] args)throws IOException {
MultiThreadedWalkDemo multithreadedtrapreceiver =newMultiThreadedWalkDemo();
multithreadedtrapreceiver.run();}publicvoidrun(){try{
LOGGER.info("开始监听walk信息!");
LOGGER.info("开始获取端口名称!");
String healthOID1 ="1.3.6.1.2.1.2.2.1.2";this.snmpWalk(healthOID1);
LOGGER.info("网口流量");
String healthOID2 ="1.3.6.1.2.1.2.2.1.10";this.snmpWalk(healthOID2);
LOGGER.info("网口状态");
String healthOID3 ="1.3.6.1.2.1.2.2.1.16";this.snmpWalk(healthOID3);
LOGGER.info("网口速率");
String healthOID4 ="1.3.6.1.2.1.31.1.1.1.15";this.snmpWalk(healthOID4);}catch(Exception ex){
ex.printStackTrace();}}}
版权归原作者 一个好孩子啊 所有, 如有侵权,请联系我们删除。