交换机协议
这里使用的交换机协议为常见的RCF1213-MIB协议,使用SNMP协议与交换机进行通信。前提记得开启交换机对于SNMP协议的支持
SNMP库
使用SNMP4J库进行开发,maven提供了相应的pom,笔者使用的版本为2.7.0。pom内容如下
<!-- snmp4j依赖包 -->
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.7.0</version>
</dependency>
获取交换机单路状态
首先明确交换机所在的IP地址,交换机提供的公共团体名称以及对应的OID
团体名称简单的来说就是验证码
如果 A 要访问B 的核心内容A 必须有B 的公钥,在和B 通信的时候B 也必须能认证A 持有的公钥
这个过程中的公钥和SNMP的团体名称功能类似
OID(对象标识符),是SNMP代理提供的具有唯一标识的键值。
OID看起来和一个IPv6的地址很像,并且不同的厂商有不同的前缀等信息。具体可以在协议文件中查看。公开的协议OID也可在网络上查询到。
// 配置交换机的SNMP参数
// 交换机的IP地址
String ipAddress = "192.168.1.1";
// 公共团体名称
String community = "public";
// RFC1213-MIB中的ifOperStatus OID,用于获取接口的操作状态
String oidValue = "1.3.6.1.2.1.2.2.1.8";
第二步设定好目标地址以及基础的超时重试信息
其中SNMP协议一共有三个版本,第一代能力欠缺,第三代还处于测试阶段,因此使用能力较为丰富且稳定的第二代SNMP协议。
SNMPv1: SNMPv1是最早的SNMP版本,它提供最基本的网络管理功能。它使用简单的社区字符串(community string)来进行认证,发送的消息以明文形式传输。SNMPv1具有较少的安全功能,并且易受到攻击。
SNMPv2: SNMPv2是对SNMPv1的改进版本。它引入了扩展的管理功能和更复杂的消息格式。SNMPv2分为SNMPv2c(community-basedSNMPv2)和SNMPv2u(user-basedSNMPv2)两种形式。SNMPv2c仍然使用社区字符串进行认证,而SNMPv2u引入了更复杂的用户认证和访问控制机制。然而,SNMPv2的安全性仍然有限,容易受到攻击。
SNMPv3: SNMPv3是最新和最安全的SNMP版本。它提供了更强大的安全性功能,如消息加密、用户身份认证和访问控制。SNMPv3使用基于用户的安全模型(USM)来提供安全性。用户可以使用用户名和密码进行身份认证,并且可以使用加密机制对消息进行保护。SNMPv3还引入了VACM(View-based Access Control Model)来管理对设备的访问控制。
// 创建目标地址。这里使用的是UDP协议,并指定了交换机的IP地址和SNMP端口161。
Address targetAddress = GenericAddress.parse("udp:" + ipAddress + "/161");
// 创建一个社区目标对象,用于存储SNMP的目标信息。
CommunityTarget target = new CommunityTarget();
// 设置目标的社区字符串。这里使用的是"public"。
target.setCommunity(new OctetString(community));
// 设置目标的地址,即前面创建的目标地址。
target.setAddress(targetAddress);
// 设置请求失败时的重试次数,这里设置为2次。
target.setRetries(2);
// 设置请求的超时时间,单位为毫秒,这里设置为1500毫秒(1.5秒)。
target.setTimeout(1500);
// 设置SNMP的版本,这里使用的是SNMP v2c。
target.setVersion(SnmpConstants.version2c);
第三步创建TransportMapping并监听SNMP的返回值
第三步主要通过PDU这个对象单元来进行数据的交换。
一个PDU(Protocol Data Unit)对象,用于封装SNMP请求。
try {
// 创建UDP传输映射,用于SNMP通信。
TransportMapping<?> transport = new DefaultUdpTransportMapping();
// 监听传输映射,即启动SNMP传输。
transport.listen();
// 创建一个Snmp对象,用于发送和接收SNMP消息
Snmp snmp = new Snmp(transport);
// 创建PDU
PDU pdu = new PDU();
// 创建一个PDU(Protocol Data Unit)对象,用于封装SNMP请求。
pdu.add(new VariableBinding(new OID(oidValue)));
// 设置PDU的类型为GETNEXT,用于获取单个变量。
pdu.setType(PDU.GETNEXT);
// 发送请求
boolean finished = false;
while (!finished) {
// 发送SNMP请求,并接收响应事件。
ResponseEvent responseEvent = snmp.send(pdu, target);
// 获取响应PDU。
PDU responsePDU = responseEvent.getResponse();
.............
.............
}
snmp.close();
// 关闭SNMP对象,释放资源。
} catch (Exception e) {
e.printStackTrace();
// 捕获并打印异常。
}
第四步处理我们请求的返回值
if (responsePDU != null) {
for (VariableBinding vb : responsePDU.getVariableBindings()) {
// 检查响应中的OID是否属于请求的OID范围。
if (vb.getOid().toString().startsWith(oidValue)) {
// 打印OID及其对应的变量值。
System.out.println(vb.getOid() + " = " + vb.getVariable());
// 重置请求ID。
pdu.setRequestID(new Integer32(0));
// 将PDU的第一个变量设置为上次收到的OID,以获取下一个变量。
pdu.set(0, vb);
} else {
// 如果OID不再属于请求的范围,则完成循环。
finished = true;
break;
}
}
} else {
finished = true;
// 如果响应PDU为空,则完成循环。
}
完整代码如下
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
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;
public class SnmpGet {
public static void main(String[] args) {
// 配置交换机的SNMP参数
// 交换机的IP地址
String ipAddress = "192.168.1.1";
// 公共团体名称
String community = "public";
// RFC1213-MIB中的ifOperStatus OID,用于获取接口的操作状态
String oidValue = "1.3.6.1.2.1.2.2.1.8";
// 创建目标
Address targetAddress = GenericAddress.parse("udp:" + ipAddress + "/161");
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(community));
target.setAddress(targetAddress);
target.setRetries(2);
target.setTimeout(1500);
target.setVersion(SnmpConstants.version2c);
// 创建TransportMapping并监听
try {
TransportMapping<?> transport = new DefaultUdpTransportMapping();
transport.listen();
Snmp snmp = new Snmp(transport);
// 创建PDU
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(oidValue)));
pdu.setType(PDU.GETNEXT);
// 发送请求
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU responsePDU = responseEvent.getResponse();
if (responsePDU != null) {
for (VariableBinding vb : responsePDU.getVariableBindings()) {
System.out.println(vb.getOid() + " = " + vb.getVariable());
}
} else {
System.out.println("响应PDU为空");
}
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
获取交换机多路状态
关键点就在于修改PDU的属性设置,其他同单路一样。
SNMP的GETBULK操作来获取多个结果,可以提高获取大量信息的效率。
pdu.setType(PDU.GETBULK);
// 每次请求的最大重复次数
pdu.setMaxRepetitions(10);
// 非重复计数器
pdu.setNonRepeaters(0);