关于Snmp4j包,最重要三个概念,也是三个类:Snmp、Target、PDU。 Target,表示被管理的设备。对于一个被管理的设备,我们关注它的Address,它使用的协议版本version,访问它的用户名和密码、以及对这个设备我们设置跟它打交道的一些策略,比如超时时间、重传次数等。所以有如下方法: 1)获取或者设置ip地址 void setAddress(Address address) Address getAddress() 2)设置或者获取版本号(支持V1,V2c,V3) void setVersion(int ver) int getVersion() 3)超时时间 ( 单位:milliseconds ) void setTimeout(long out) long getTimeout() 4)重传次数 void setRetries(int retries) int getRetries() 5)最大PDU大小值 void setMaxSizeRequestPDU(int max) int getMaxSizeRequestPDU()
上面这些对于Snmp三个版本都是相同的,不同的只是在于安全方面,具体来说,Snmpv1,v2c采用共同体(community)方式,安全性很差;SnmpV3采用基于用户的安全(USM)方式,安全性能得到很大提高。 (1) 对于Snmpv1,v2c,使用Target派生出一个子类CommunityTarget,增加了对Community的方法: void setCommunity(OctetString community) OctetString getCommunity()
(2)对于SnmpV3,使用其子类UserTarget,增加了一些有关USM安全方面的设置,比如采用的安全模型、安全级别、访问该设备的用户名以及权威引擎ID(authoritative engine ID): i)安全级别(支持三种安全级别) void setSecurityLevel(int level) int getSecurityLevel() ii)访问用户名(该访问用户名称必须在设置USM的时候添加其相应的UsmUser) void setSecurityName(OctetString name) OctetString getSecurityName() iii)安全模型(支持三种安全模型) void setSecurityModel(int model) int getSecurityModel() V)权威引擎ID(authoritative engine ID) void setAuthoritativeEngineID(byte[] id) byte[] getAuthoritativeEngineID() 对于Target的代码大致如下:(摘自Snmp4j的说明文档)
//Community Target CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString("public")); target.setAddress(targetAddress); target.setVersion(SnmpConstants.version1);
//User Target UserTarget target = new UserTarget(); target.setAddress(targetAddress); target.setRetries(1); // set timeout to 500 milliseconds -> 2*500ms = 1s total timeout target.setTimeout(500); target.setVersion(SnmpConstants.version3); target.setSecurityLevel(SecurityLevel.AUTH_PRIV); target.setSecurityName(new OctetString("MD5DES"));
一、pduPDU(协议数据单元),用来表示管理站跟代理站点进行通信的数据。包括PDU的类型、传输的数据集合、错误说明等。 除Snmpv1 Trap外,其他操作类型的PDU大致相同。 Snmp4j针对Snmp的各个版本,开发了三个有关PDU的类。PDU(针对Snmpv2c)、PDUv1(针对Snmpv1)、ScopedPDU(针对Snmpv3),但三个类除各自特别的一些参数外,都基本相同。所以Snmp4j在设计的时候,将PDU设计成另外两个的超类,使他们能够共享大部分功能。
(1)PDU 1)pdu类型(Snmpv1支持5种,get、set、getnext、trap、getresponse,Snmpv2c增加了getbulk、inform) int getType() void setType(int type) 2)错误(主要有6种错误,请参见RFC) void setErrorStatus(int status) int getErrorStatus() String getErrorStatusText() void setErrorIndex(int index) int getErrorindex()
3)变量 void add(VariableBinding vb) // 对于除get、getnext、getbulk外 void addAll(VariableBinding[] vbs) // 对于除get、getnext、getbulk外的 void addOID(VariableBinding vb) // 对于get、getnext、getbulk void addAllOID(VariableBinding[] vbs) // 对于get、getnext、getbulk VariableBinding get(int i) VariableBinding[] toArray() Vector getVariableBindings() void set(int i,VariableBinding vb) void remove(int i) void clear() int size() (2)PDUv1 PDUv1继承了PDU,除Snmp v1 trap操作外 ,其它都跟PDU相同。因为Snmp v1的Trap设计PDU不同于其他操作的PDU,它包括:对象标识符(OID)、代理ip地址(agent-addr)、时间戳(time-stamp)、通用代理类型(generic-trap)、特定代理类型(specific-trap)。所以相应的增加了对这些字段的get/set操作。具体参见API文档。
(3)ScopedPDU SnmpV3采用了不同安全策略,它的PDU也增加了有关安全方面的几个字段:上下文名称(ContextName)、上下文引擎ID(contextEngineID)等,其它PDU字段属于USM来管理。 所以ScopedPDU在PDU的基础上增加了相应的get/set操作。参见API。
示例代码如下:(摘自API文档)
SNMPv1/v2c GETNEXT PDU import org.snmp4j.PDU; import org.snmp4j.smi.*; ... PDU pdu = new PDU(); pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1"))); // sysDescr pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.1"))); // ifNumber pdu.setType(PDU.GETNEXT); ...SNMPv3 GETBULK PDU import org.snmp4j.ScopedPDU; import org.snmp4j.smi.*; ... ScopedPDU pdu = new ScopedPDU(); pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.1"))); // ifNumber pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.10"))); // ifInOctets pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.16"))); // ifOutOctets pdu.setType(PDU.GETBULK); pdu.setMaxRepetitions(50); // Get ifNumber only once pdu.setNonRepeaters(1); // set context non-default context (default context does not need to be set) pdu.setContextName(new OctetString("subSystemContextA")); // set non-default context engine ID (to use targets authoritative engine ID // use an empty (size == 0) octet string) pdu.setContextEngineID(OctetString.fromHexString("80:00:13:70:c0:a8:01:0d")); ...SNMPv1 TRAP PDU import org.snmp4j.PDUv1; ... PDUv1 pdu = new PDUv1(); pdu.setType(PDU.V1TRAP); pdu.setGenericTrap(PDUv1.COLDSTART); ...SNMPv2c/SNMPv3 INFORM PDU import org.snmp4j.ScopedPDU; ... ScopedPDU pdu = new ScopedPDU(); pdu.setType(PDU.INFORM); // sysUpTime long sysUpTime = (System.currentTimeMillis() - startTime) / 10; pdu.add(new VariableBinding(SnmpConstants.sysUpTime, new TimeTicks(sysUpTime))); pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, SnmpConstants.linkDown)); // payload pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.2.2.1.1"+downIndex), new Integer32(downIndex))); ...
二、snmpsnmp类是SNMP4J的核心,它提供了发送和接收SNMP PDUs的方法,所有的SNMP PDU 类型都可以采用同步或者异步的方式被发送。
Snmp采用独立的传输协议,通过TransportMapping 接口调用addTransportMapping(TransportMapping transportMapping) 方法或者采用默认的构造函数来实现传输映射,以此来实现信息的传输。
下面的代码片段是采用UDP传输方式来实现一个SNMPv3的SNMP session :
Java代码 Address targetAddress = GenericAddress.parse("udp:127.0.0.1/161"); TransportMapping transport = new DefaultUdpTransportMapping(); snmp = new Snmp(transport); USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); SecurityModels.getInstance().addSecurityModel(usm); transport.listen();
Address targetAddress = GenericAddress.parse("udp:127.0.0.1/161"); TransportMapping transport = new DefaultUdpTransportMapping(); snmp = new Snmp(transport); USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); SecurityModels.getInstance().addSecurityModel(usm); transport.listen();
如何实现SNMPv3信息的同步发送,下面举例说明:
Java代码 // add user to the USM snmp.getUSM().addUser(new OctetString("MD5DES"), new UsmUser(new OctetString("MD5DES"), AuthMD5.ID, new OctetString("MD5DESUserAuthPassword"), PrivDES.ID, new OctetString("MD5DESUserPrivPassword"))); // create the target UserTarget target = new UserTarget(); target.setAddress(targetAddress); target.setRetries(1); target.setTimeout(5000); target.setVersion(SnmpConstants.version3); target.setSecurityLevel(SecurityLevel.AUTH_PRIV); target.setSecurityName(new OctetString("MD5DES")); // create the PDU PDU pdu = new ScopedPDU(); pdu.add(new VariableBinding(new OID("1.3.6"))); pdu.setType(PDU.GETNEXT); // send the PDU ResponseEvent response = snmp.send(pdu, target); // extract the response PDU (could be null if timed out) PDU responsePDU = response.getResponse(); // extract the address used by the agent to send the response: Address peerAddress = response.getPeerAddress();
An asynchronous SNMPv1 request is sent by the following code: // setting up target CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString("public")); target.setAddress(targetAddress); target.setRetries(2); target.setTimeout(1500); target.setVersion(SnmpConstants.version1); // creating PDU PDU pdu = new PDU(); pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,1}))); pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2}))); pdu.setType(PDU.GETNEXT); // sending request ResponseListener listener = new ResponseListener() { public void onResponse(ResponseEvent event) { // Always cancel async request when response has been received // otherwise a memory leak is created! Not canceling a request // immediately can be useful when sending a request to a broadcast // address. ((Snmp)event.getSource()).cancel(event.getRequest(), this); System.out.println("Received response PDU is: "+event.getResponse()); } }; snmp.sendPDU(pdu, target, null, listener); //Traps (notifications) and other SNMP PDUs can be received by adding the folling code to the first code snippet above: CommandResponder trapPrinter = new CommandResponder() { public synchronized void processPdu(CommandResponderEvent e) { PDU command = e.getPDU(); if (command != null) { System.out.println(command.toString()); } } }; snmp.addCommandResponder(trapPrinter); Version: 1.8 Author: Frank Fock translate:avery_leo
// add user to the USM snmp.getUSM().addUser(new OctetString("MD5DES"), new UsmUser(new OctetString("MD5DES"), AuthMD5.ID, new OctetString("MD5DESUserAuthPassword"), PrivDES.ID, new OctetString("MD5DESUserPrivPassword"))); // create the target UserTarget target = new UserTarget(); target.setAddress(targetAddress); target.setRetries(1); target.setTimeout(5000); target.setVersion(SnmpConstants.version3); target.setSecurityLevel(SecurityLevel.AUTH_PRIV); target.setSecurityName(new OctetString("MD5DES"));
// create the PDU PDU pdu = new ScopedPDU(); pdu.add(new VariableBinding(new OID("1.3.6"))); pdu.setType(PDU.GETNEXT);
// send the PDU ResponseEvent response = snmp.send(pdu, target); // extract the response PDU (could be null if timed out) PDU responsePDU = response.getResponse(); // extract the address used by the agent to send the response: Address peerAddress = response.getPeerAddress(); An asynchronous SNMPv1 request is sent by the following code:
// setting up target CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString("public")); target.setAddress(targetAddress); target.setRetries(2); target.setTimeout(1500); target.setVersion(SnmpConstants.version1); // creating PDU PDU pdu = new PDU(); pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,1}))); pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2}))); pdu.setType(PDU.GETNEXT); // sending request ResponseListener listener = new ResponseListener() { public void onResponse(ResponseEvent event) { // Always cancel async request when response has been received // otherwise a memory leak is created! Not canceling a request // immediately can be useful when sending a request to a broadcast // address. ((Snmp)event.getSource()).cancel(event.getRequest(), this); System.out.println("Received response PDU is: "+event.getResponse()); } }; snmp.sendPDU(pdu, target, null, listener); //Traps (notifications) and other SNMP PDUs can be received by adding the folling code to the first code snippet above: CommandResponder trapPrinter = new CommandResponder() { public synchronized void processPdu(CommandResponderEvent e) { PDU command = e.getPDU(); if (command != null) { System.out.println(command.toString()); } } }; snmp.addCommandResponder(trapPrinter);
Version: 1.8 Author: Frank Fock translate:avery_leo
Snmp类提供了一套有关Snmp的功能接口。具体来讲,就是发送、接受、创建Snmp消息。 一个Snmp对象是一个Session,而在Snmp4j中,一个Session可以同多个远程设备通信。
(1) Snmp、Target、PDU三者的关系 Target代表远程设备或者远程实体、PDU代表管理端同Target通信的数据,Snmp就代表管理者管理功能(其实就是数据的收发)的具体执行者。 打个比方:Target就是你远方的恋人,PDU就是你们之间传递的情书、而Snmp就是负责帮你寄信收信的邮差。
(2)Snmp收发数据的两种方式 Snmp可以同步、也可异步收发数据。详细见代码示例说明。
(3)Snmp与传输层协议 Snmp可以定制传输层协议,一般选择udp,也可以选择tcp。详细见代码示例说明。
(4)Snmp与Usm 创建Snmp用来发送Snmpv3版本的消息时候,一般还要创建USM,将它添加至安全模型管理器(SecriryModels)中,同时还需要向Usm中添加相应的USM用户(UsmUser)。详细见代码示例说明。
代码示例:(摘自Snmp4j的API文档)
(1)创建Snmp 1)使用UDP传输协议
TransportMapping transport = new DefaultUdpTransportMapping(); snmp = new Snmp(transport); 2)使用TCP传输协议
TransportMapping transport = new DefaultTcpTransportMapping(); snmp = new Snmp(transport); 3)创建用于Snmpv3的Snmp
// 创建Snmp TransportMapping transport = new DefaultUdpTransportMapping(); Snmp snmp = new Snmp(transport); if (version == SnmpConstants.version3) { byte[] localEngineID = ((MPv3)snmp.getMessageProcessingModel(MessageProcessingModel.MPv3)).createLocalEngineID(); // 创建USM USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(localEngineID), 0); // 将USM添加至安全模式管理器中 // 安全模型管理器采用了单例模式,它内部可以维护为3个安全模型,分别对应Snmp三个版本 SecurityModels.getInstance().addSecurityModel(usm); snmp.setLocalEngine(localEngineID, 0, 0); // 添加用户 snmp.getUSM().addUser(securityName,new UsmUser(securityName,authProtocol, authPassphrase,privProtocol,privPassphrase)); }
(2)同步收发消息
import org.snmp4j.*; ... Snmp snmp = new Snmp(new DefaultUdpTransportMapping()); ... ResponseEvent response = snmp.send(requestPDU, target); if (response.getResponse() == null) { // request timed out ... }else { System.out.println("Received response from: "+ response.getPeerAddress()); // dump response PDU System.out.println(response.getResponse().toString()); }
(3)异步收发消息
import org.snmp4j.*; import org.snmp4j.event.*; ... Snmp snmp = new Snmp(new DefaultUdpTransportMapping()); ... // 增加监听器 ResponseListener listener = new ResponseListener() { public void onResponse(ResponseEvent event) { PDU response = event.getResponse(); PDU request = event.getRequest(); if (response == null) { System.out.println("Request "+request+" timed out"); } else { System.out.println("Received response "+response+" on request "+ request); } }; snmp.sendPDU(request, target, null, listener); ...(4)实现trap 实现trap需要三步: 1)创建Snmp; 2)对于listen()使处于网络监听(实际上是同于网络编程中的Socket监听); 3)实现CommandResponder接口的监听器,并且调用Snmp.addCommandResponder(CommandResponder)注册监听器。
import org.snmp4j.*; import org.snmp4j.smi.*; import org.snmp4j.mp.SnmpConstants; ... TransportMapping transport = new DefaultUdpTransportMapping(new UdpAddress("0.0.0.0/161")); Snmp snmp = new Snmp(transport); if (version == SnmpConstants.version3) { byte[] localEngineID = ((MPv3)snmp.getMessageProcessingModel(MessageProcessingModel.MPv3)).createLocalEngineID(); USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(localEngineID), 0); SecurityModels.getInstance().addSecurityModel(usm); snmp.setLocalEngine(localEngineID, 0, 0); // Add the configured user to the USM ... } // 注册命令响应监听器 snmp.addCommandResponder(this); transport.listen(); ... // 实现CommandResponder接口 public synchronized void processPdu(CommandResponderEvent e) { PDU command = e.getPdu(); if (command != null) { ... } } 总结 Snmp内含了一个消息分发器,消息分发器中内含了处理网络的线程,在使用完后最好调用close(),将其资源回归处理。 掌握了上面所说的三个概念,基本上可以使用Snmp4j编写Snmp的程序了。 有关Snmp4j编程最好也最详细的资料:API文档和源代码。关于使用Snmp4j编写Snmp程序的例子,多线程的例子可以参看源代码中:org.snmp4j.test包下的MultiThreadedTrapReceiver.java,完整的例子可以参看 org.snmp4j.tools.console包下的SnmpRequest(一个命令行的Snmp管理器)。 不过,要想快速和深入掌握Snmp编程,最好的办法一定是先弄懂Snmp协议,这方面的资料最权威的就是RFC协议了。