關于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"));
----------------------pdu--------------------
PDU(協定資料單元),用來表示管理站跟代理站點進行通信的資料。包括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)));
...
----------------------snmp--------------------
snmp類是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協定了。