引言
SNMP(简单网络管理协议)是一种用于网络设备管理的标准协议。本文将介绍如何使用 Java 和 SNMP4J(一个开源的 SNMP 实现库)进行 SNMP 操作。我们将通过编写 SnmpUtil 类来演示如何进行 SNMP 初始化、创建 PDU、发送 SNMP 请求并处理响应。
环境准备
首先,确保您已经添加了 SNMP4J 依赖。可以通过 Maven 或 Gradle 进行依赖管理。
java
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.8.6</version>
</dependency>
java
implementation 'org.snmp4j:snmp4j:2.8.6'
SnmpUtil 类概述
以下是编写 SnmpUtil 类的整体结构。该类提供了静态方法来初始化 SNMP、创建 SNMP 目标、创建 PDU、发送 SNMP 请求和处理响应。
类的静态初始化
我们在这使用静态块来初始化 SNMP 对象,确保整个应用程序生命周期中只进行一次初始化。
java
public class SnmpUtil {
private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
private static Snmp snmp = null;
static {
try {
initSnmp();
} catch (IOException e) {
log.error("SNMP 初始化失败", e);
}
}
private static synchronized void initSnmp() throws IOException {
if (snmp == null) {
MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
messageDispatcher.addMessageProcessingModel(new MPv1());
messageDispatcher.addMessageProcessingModel(new MPv2c());
OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
PrivAES128.ID, new OctetString("privPassword"));
usm.addUser(user.getSecurityName(), user);
messageDispatcher.addMessageProcessingModel(new MPv3(usm));
TransportMapping<?> transportMapping = new DefaultUdpTransportMapping();
snmp = new Snmp(messageDispatcher, transportMapping);
snmp.listen();
}
}
}
创建目标
编写createTarget 方法用于创建 SNMP 目标,支持 SNMP v1, v2c 和 v3 版本。
java
private static Target createTarget(int version, String community, String ipAddress, int port) {
Target target = null;
if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
log.error("参数version异常");
return target;
}
if (version == SnmpConstants.version3) {
target = new UserTarget();
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target.setSecurityName(new OctetString("SNMPV3"));
} else {
target = new CommunityTarget();
((CommunityTarget) target).setCommunity(new OctetString(community));
if (version == SnmpConstants.version2c) {
target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
}
}
target.setVersion(version);
target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
target.setRetries(5);
target.setTimeout(3000);
return target;
}
创建 PDU
编写createPDU 方法用于创建 Protocol Data Unit (PDU),这是 SNMP 消息的基本构成单元。
java
private static PDU createPDU(int version, int type, String oid) {
PDU pdu = null;
if (version == SnmpConstants.version3) {
pdu = new ScopedPDU();
} else {
pdu = new PDUv1();
}
pdu.setType(type);
pdu.add(new VariableBinding(new OID(oid)));
return pdu;
}
private static PDU createPDU(int version, int type, List<String> oids) {
PDU pdu = null;
if (version == SnmpConstants.version3) {
pdu = new ScopedPDU();
} else {
pdu = new PDU();
}
pdu.setType(type);
for (String oid : oids) {
pdu.add(new VariableBinding(new OID(oid)));
}
return pdu;
}
发送 SNMP 请求
编写snmpWalk 方法用于发送 GETNEXT 请求,并处理响应。
java
public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
try {
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = createPDU(version, PDU.GETNEXT, oid);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
return response;
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
return new PDU();
}
}
public static PDU snmpWalk(String ipAddr, int port, int version, String community, List<String> oids) {
try {
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = createPDU(version, PDU.GETNEXT, oids);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
return response;
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
return new PDU();
}
}
处理 SNMP 响应
编写snmpWalkAll 和 snmpWalkSegment 方法用于处理连续的 SNMP 响应,直到到达 MIB 的末尾。
java
public static List<VariableBinding> snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
List<VariableBinding> results = new ArrayList<>();
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(startOid)));
while (true) {
try {
pdu.setType(PDU.GETNEXT);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
if (response.get(0).getVariable().toString().equals("endOfMibView")) {
break;
}
results.addAll(response.getVariableBindings());
pdu.setRequestID(null); // Reset request ID for the next request
pdu.remove(0); // Remove the old request
pdu.add(response.get(0)); // Add the new request based on the last response
} else {
break;
}
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
break;
}
}
return results;
}
// ...其他代码见完整代码
完整代码
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SnmpUtil {
private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
private static Snmp snmp = null; // 将共享资源的访问控制为静态初始化
static {
// 将 SNMP 对象的初始化放在静态块中,确保只初始化一次
try {
initSnmp();
} catch (IOException e) {
log.error("SNMP 初始化失败", e); // 记录初始化失败的异常信息
}
}
private static synchronized void initSnmp() throws IOException { // 添加 synchronized 关键字保证线程安全
if (snmp == null) {
MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
messageDispatcher.addMessageProcessingModel(new MPv1());
messageDispatcher.addMessageProcessingModel(new MPv2c());
OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
PrivAES128.ID, new OctetString("privPassword"));
usm.addUser(user.getSecurityName(), user);
messageDispatcher.addMessageProcessingModel(new MPv3(usm));
TransportMapping<?> transportMapping = new DefaultUdpTransportMapping();
snmp = new Snmp(messageDispatcher, transportMapping);
snmp.listen();
}
}
private static Target createTarget(int version, String community, String ipAddress, int port) {
Target target = null;
if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
log.error("参数version异常"); // 保持错误日志记录
return target;
}
if (version == SnmpConstants.version3) {
target = new UserTarget();
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target.setSecurityName(new OctetString("SNMPV3"));
} else {
target = new CommunityTarget();
((CommunityTarget) target).setCommunity(new OctetString(community));
if (version == SnmpConstants.version2c) {
target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
}
}
target.setVersion(version);
target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
target.setRetries(5);
target.setTimeout(3000);
return target;
}
/**
* 创建 PDU 对象
* @param version
* @param type
* @param oid
* @return
*/
private static PDU createPDU(int version, int type, String oid) {
PDU pdu = null;
if (version == SnmpConstants.version3) {
pdu = new ScopedPDU();
} else {
pdu = new PDUv1();
}
pdu.setType(type);
pdu.add(new VariableBinding(new OID(oid)));
return pdu;
}
/**
* 创建 PDU 对象
* @param version
* @param type
* @param oids
* @return
*/
private static PDU createPDU(int version, int type, List<String> oids) {
PDU pdu = null;
if (version == SnmpConstants.version3) {
pdu = new ScopedPDU();
} else {
pdu = new PDU();
}
pdu.setType(type);
for (String oid : oids) {
pdu.add(new VariableBinding(new OID(oid)));
}
return pdu;
}
/**
* 获取 SNMP 响应
* @param ipAddr
* @param port
* @param version
* @param community
* @param oid
* @return
*/
public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
try {
// 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = createPDU(version, PDU.GETNEXT, oid); // 保持内部字符串不变
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
return response;
} catch (IOException e) {
log.error("SNMP 发送请求失败", e); // 增加异常日志记录
return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
}
}
/**
* 获取 SNMP 响应
* @param ipAddr
* @param port
* @param version
* @param community
* @param oids
* @return
*/
public static PDU snmpWalk(String ipAddr, int port, int version, String community, List<String> oids) {
try {
// 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = createPDU(version, PDU.GETNEXT, oids); // 保持内部字符串不变
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
return response;
} catch (IOException e) {
log.error("SNMP 发送请求失败", e); // 增加异常日志记录
return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
}
}
/**
* 获取 SNMP 响应
* @param ipAddr
* @param port
* @param version
* @param community
* @param startOid
* @return
*/
public static List<VariableBinding> snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
List<VariableBinding> results = new ArrayList<>();
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(startOid)));
while (true) {
try {
pdu.setType(PDU.GETNEXT);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
if (response.get(0).getVariable().toString().equals("endOfMibView")) {
break;
}
results.addAll(response.getVariableBindings());
pdu.setRequestID(null); // Reset request ID for the next request
pdu.remove(0); // Remove the old request
pdu.add(response.get(0)); // Add the new request based on the last response
} else {
break;
}
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
break;
}
}
return results;
}
/**
* 获取 SNMP 响应
* @param ipAddr
* @param port
* @param version
* @param community
* @param startOid
* @return
*/
public static List<VariableBinding> snmpWalkSegment(String ipAddr, int port, int version, String community, String startOid) {
List<VariableBinding> results = new ArrayList<>();
Target target = createTarget(version, community, ipAddr, port);
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(startOid)));
while (true) {
try {
pdu.setType(PDU.GETNEXT);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
VariableBinding vb = response.get(0);
results.add(vb);
// Check if we have reached the end of the branch
if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
break;
}
pdu.setRequestID(null); // Reset request ID for the next request
pdu.remove(0); // Remove the old request
pdu.add(vb); // Add the new request based on the last response
System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
} else {
break;
}
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
break;
}
}
return results;
}
public static List<VariableBinding> snmpWalkSegment(String ipAddr, int port, int version, String community, List<String> startOids) {
List<VariableBinding> results = new ArrayList<>();
Target target = createTarget(version, community, ipAddr, port);
for(String startOid: startOids){
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(startOid)));
while (true) {
try {
pdu.setType(PDU.GETNEXT);
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
if (response != null && response.size() > 0 && response.getErrorStatus() == 0) {
VariableBinding vb = response.get(0);
results.add(vb);
// Check if we have reached the end of the branch
if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
break;
}
pdu.setRequestID(null); // Reset request ID for the next request
pdu.remove(0); // Remove the old request
pdu.add(vb); // Add the new request based on the last response
System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
} else {
break;
}
} catch (IOException e) {
log.error("SNMP 发送请求失败", e);
break;
}
}
}
return results;
}
public static void main(String[] args) {
PDU response = SnmpUtil.snmpWalk("127.0.0.1", 161, SnmpConstants.version2c, "public", ".1");
if (response != null && response.getErrorStatus() == 0) {
for (VariableBinding vb : response.getVariableBindings()) {
System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
}
} else {
System.out.println("Error in response: " + response.getErrorStatusText());
}
}
}
总结
通过本文的讲解,您应该已经掌握了如何使用 Java 和 SNMP4J 库进行 SNMP 操作。我们介绍了 SnmpUtil 类的设计和实现,包括 SNMP 初始化、创建目标、创建 PDU、发送 SNMP 请求和处理响应等内容。希望这篇教程能够帮助您更好地理解和使用 SNMP4J 进行网络设备管理