使用Java编程-SNMP4J-SNMPv3-代码实例
SNMP介绍
SNMP是一种用于管理网络设备的协议。它是一种标准化的协议,被用于监控和管理网络设备,包括路由器、交换机、服务器、打印机和其他设备。
SNMP协议的基本组成部分包括:
管理站(Management Station):通常是一个PC或服务器,用于监控和管理网络设备。
管理代理(Management Agent):运行在网络设备上的软件,负责管理和监控网络设备,并提供信息给管理站。
MIB(Management Information Base):一个层次结构的数据库,包含了网络设备的信息。MIB的每个节点都对应着网络设备的一个属性或状态。
SNMP协议:用在管理站和管理代理之间传输信息的协议。SNMP消息包含了操作类型、对象标识符和值。SNMP协议定义了一系列操作,可以通过这些操作获取、设置和监控网络设备的属性和状态。这些操作包括:
1、GET:获取一个属性或状态的值。
2、SET:设置一个属性或状态的值。
3、GETNEXT:获取下一个节点的值。
4、GETBULK:获取一组节点的值。
5、TRAP:向管理站发送一个警告或通知。
SNMP协议的最新版本是SNMPv3,它提供了安全性和访问控制机制,以保护网络设备的安全性和保密性。
为什么要用SNMP
随着网络技术的飞速发展,在网络不断普及的同时也给网络管理带来了一些问题:网络设备数量成几何级数增加,使得网络管理员对设备的管理变得越来越困难;同时,网络作为一个复杂的分布式系统,其覆盖地域不断扩大,也使得对这些设备进行实时监控和故障排查变得极为困难。
网络设备种类多种多样,不同设备厂商提供的管理接口(如命令行接口)各不相同,这使得网络管理变得愈发复杂。
在这种背景下,SNMP应运而生,SNMP是广泛应用于TCP/IP网络的网络管理标准协议,该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。通过"利用网络管理网络"的方式:网络管理员可以利用SNMP平台在网络上的任意节点完成信息查询、信息修改和故障排查等工作,工作效率得以提高。
屏蔽了设备间的物理差异,SNMP仅提供最基本的功能集,使得管理任务与被管理设备的物理特性、网络类型相互独立,因而可以实现对不同设备的统一管理,管理成本低。
设计简单、运行代价低,SNMP采用"尽可能简单"的设计思想,其在设备上添加的软件/硬件、报文的种类和报文的格式都力求简单,因而运行SNMP给设备造成的影响和代价都被最小化。
SNMP的基本组件
SNMP基本组件包括网络管理系统NMS(Network Management System)、代理进程(Agent)、被管对象(Managed Object)和管理信息库(Management Information Base)。
优势
1、简单易用:SNMP协议是一种简单易用的协议,使用方便、易于管理。
2、标准化:SNMP协议是一种标准化的协议,被广泛应用于网络管理领域。
3、实时监控:SNMP协议可以实时监控网络设备的性能和状态,及时发现和解决问题。
4、集中管理:SNMP协议可以通过集中管理工具对网络设备进行统一管理,提高管理效率和管理水平。
5、可扩展性:SNMP协议具有良好的可扩展性,可以适应不同的网络管理需求和应用场景。
应用
网络设备管理:SNMP协议是一种常用的网络设备管理协议,用于监控和管理路由器、交换机、服务器和打印机等网络设备。
网络性能监控:SNMP协议可以实时监控网络设备的性能指标,如CPU利用率、内存使用率、网络流量等,帮助管理员及时发现和解决性能问题。
安全管理:SNMP协议可以用于监控和管理操作系统、数据库、应用程序等系统和软件,保证系统正常运行和高效管理。
云计算管理:SNMP协议可以用于云计算环境中的网络管理和监控,帮助管理员及时发现和解决云环境中的问题。
SNMP版本
SNMP协议的版本包括:SNMPv1、SNMPv2c、SNMPv3。
SNMPv1和SNMPv2c都是使用基于共同体名的认证。NMS通过共同体名列表控制对设备的访问权限,而代理(Agent)并不核实发送者是否使用了授权的共同体名,同时,SNMP消息未采用加密传输,因此在认证和私有性方面缺乏安全保障。
SNMPv2c在SNMPv1的基础上进行了增强,增强的功能包括:支持更多的操作、支持更多的数据类型、提供更丰富的错误处理码和多种传输协议的支持。
SNMPv3定义了包含SNMPv1、SNMPv2所有功能在内的体系框架和包含验证服务和加密服务在内的全新安全机制。
SNMPv3的安全性主要体现在数据安全和访问控制上。
SNMPv3提供消息级的数据安全,它包括以下三种情况:
- 数据完整性:数据不会在未被授权方式下修改,数据顺序的改动也不会超出许可范围。
- 数据来源验证:确认所收到的数据来自哪个用户。SNMPv3定义的安全性是基于用户的,它验证的是生成消息的用户,而不是具体生成消息的应用程序。
- 数据核实性检查:当NMS或Agent接收到消息时,对消息的生成时间进行检查,如果消息时间与系统当前时间的差超出了指定的时间范围,该消息就不被接受。这可以防止消息在网络传输过程中被恶意更改,或收到并处理恶意发送的消息。
SNMPv3的访问控制是基于协议操作的安全性检查,控制对被管理对象的访问。
SNMP协议数据单元
SNMP规定了5种协议数据单元PDU(也就是SNMP报文),用于NMS与Agent的交互。。
SNMP的报文操作示意图
各种报文的操作如下:
- get-request:从代理进程处提取一个或多个参数值。
- get-next-request:从代理进程处提取紧跟当前参数值的下一个参数值。
- set-request:设置代理进程的一个或多个参数值。
- get-response:返回的一个或多个参数值。这个操作是由代理进程发出的,它是对前面3种操作的响应。
- trap:代理进程主动发出的报文,通知管理进程有某些事件发生。
前面3种操作由NMS向Agent发出,后面2种操作由Agent向NMS发出。
SNMP报文处理过程
Agent通过UDP端口161接收来自NMS的Request报文。
Agent接收到报文后,其基本处理过程如下:
- 解码:依据ASN.1基本编码规则,生成用内部数据结构表示的报文。如果此过程出现错误导致解码失败,则丢弃该报文,不做进一步处理。
- 比较SNMP版本号:将报文中的版本号取出,与本Agent支持的SNMP版本号比较。如果不一致,则丢弃该报文,不做进一步处理。
- 团体名验证:将报文中的团体名取出,此团体名由发出请求的网管站填写。如与Agent所在设备认可的团体名不符,则丢弃该报文,不做进一步处理,同时产生一个Trap报文。SNMPv1提供较弱的安全措施,在版本3中这一功能被加强。
- 提取PDU:从通过验证的ASN.1对象中提出协议数据单元PDU。如果失败,丢弃报文,不做进一步处理。
- 处理PDU:根据不同的PDU,SNMP协议实体进行不同的处理。得到管理变量在MIB树中对应的节点,从相应的模块中得到管理变量的值,形成Response报文,编码发回网管站。
- 网管站得到响应报文后,经过同样的处理,最终显示结果。
示例
本次使用snmp v3协议获取数据,需要提前配置好snmp v3账号、认证协议、加密协议
认证协议采用SHA256 ,加密协议采用AES128
使用华为wireless Lan设备进行演示,通过oid获取对应值
Java实现
maven依依赖
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>3.5.1</version>
</dependency>
编写snmp工具类
/**
* snmp工具
*
* @author jay_zhao
* @date 2024/08/20
*/
public class SnmpUtil2{
private static String username = "your username";
private static String authPassword = "your auth Password";
private static String privPassword = "your priv Password";
/**
*设备的ip
*/
private static String addressIp = "udp:127.0.0.1/161";
/**
* v1和v2协议需要的团体名
*/
private static String publicName = "your public Name";
private static Snmp snmp;
private static void initSnmp() throws Exception {
//1、初始化多线程消息转发类
MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
//其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping<? extends Address> transportMapping) ,就不需要增加
messageDispatcher.addMessageProcessingModel(new MPv1());
messageDispatcher.addMessageProcessingModel(new MPv2c());
// 3.x版本之后默认不开启,需要手动开启,否则会报not support Security level,手动加入加密密码和加密协议
SecurityProtocols instance = SecurityProtocols.getInstance();
instance.addAuthenticationProtocol(new AuthSHA());
// instance.addAuthenticationProtocol(new AuthMD5());
instance.addPrivacyProtocol(new PrivAES128());
// instance.addPrivacyProtocol(new PrivAES256());
//当要支持snmpV3版本时,需要配置user
OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
OctetString userName1 = new OctetString(username);
OctetString authPass = new OctetString(authPassword);
OctetString privPass = new OctetString(privPassword);
// UsmUser user = new UsmUser(userName1, AuthMD5.ID, authPass, PrivAES128.ID, privPass);
// 根据自己的加密协议设置需要的认证协议和加密协议
UsmUser user = new UsmUser(userName1,AuthHMAC192SHA256.ID , authPass, PrivAES128.ID, privPass);
usm.addUser(user.getSecurityName(), user);
messageDispatcher.addMessageProcessingModel(new MPv3(usm));
//2、创建transportMapping ip为本地ip,可以不设置
TransportMapping<?> transportMapping = new DefaultUdpTransportMapping();
// UdpAddress updAddr = (UdpAddress) GenericAddress.parse("udp:172.29.12.95/161");
// TransportMapping<?> transportMapping = new DefaultUdpTransportMapping(updAddr);
//3、正式创建snmp
snmp = new Snmp(messageDispatcher, transportMapping);
//开启监听
snmp.listen();
}
private static Target createTarget(int version) {
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 = new UserTarget();
//snmpV3需要设置安全级别和安全名称,其中安全名称是创建snmp指定user设置的new OctetString("SNMPV3")
target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
target.setSecurityName(new OctetString(username));
} else {
//snmpV1和snmpV2需要指定团体名名称
target = new CommunityTarget();
// ((CommunityTarget) target).setCommunity(new OctetString(this.username));
((CommunityTarget) target).setCommunity(new OctetString(publicName));
if (version == SnmpConstants.version2c) {
target.setVersion(SnmpConstants.version2c);
target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
}
}
target.setVersion(version);
//必须指定,没有设置就会报错。
target.setAddress(GenericAddress.parse(addressIp));
target.setRetries(2);
target.setTimeout(10000);
return target;
}
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);
//可以添加多个变量oid
/*for(String oid:oids){
pdu.add(new VariableBinding(new OID(oid)));
}*/
pdu.add(new VariableBinding(new OID(oid)));
return pdu;
}
private static PDU createMulPDU(int version, int type, String[] oids) {
PDU pdu = null;
if (version == SnmpConstants.version3) {
pdu = new ScopedPDU();
} else {
pdu = new PDUv1();
}
pdu.setType(type);
//可以添加多个变量oid
for(String oid:oids){
pdu.add(new VariableBinding(new OID(oid)));
}
return pdu;
}
/**
* WALK方式请求
*
* @param oid
* @throws Exception
*/
public static List<Map<String,Variable>> snmpWalk(String oid, int version) throws Exception {
try {
System.out.println("first way:");
List<Map<String,Variable>> list = new ArrayList<Map<String,Variable>>();
//1、初始化snmp,并开启监听
initSnmp();
//2、创建目标对象
Target target = createTarget(version);
//3、创建报文
// PDU pdu = createPDU(1, PDU.GETNEXT, oid);
PDU pdu = createPDU(version, PDU.GETNEXT, oid);
//4、发送报文,并获取返回结果
boolean matched = true;
while (matched) {
ResponseEvent responseEvent = snmp.send(pdu, target);
// ResponseEvent responseEvent = snmp.getNext(pdu, target);
if (responseEvent == null || responseEvent.getResponse() == null) {
System.out.println("Send TimeOut...");
break;
}
PDU response = responseEvent.getResponse();
String nextOid = null;
List<? extends VariableBinding> vbs = response.getVariableBindings();
for (VariableBinding variableBinding : vbs) {
Variable variable = variableBinding.getVariable();
nextOid = variableBinding.getOid().toDottedString();
//如果不是这个节点下的oid则终止遍历,否则会输出很多,直到整个遍历完。
if (!nextOid.startsWith(oid)) {
matched = false;
break;
}
//给oid对应的实体类属性赋值
Map<String,Variable> data = new HashMap<String, Variable>();
data.put(nextOid, variable);
list.add(data);
System.out.println("data=>" + data);
}
if (!matched) {
break;
}
pdu.clear();
pdu.add(new VariableBinding(new OID(nextOid)));
}
return list;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* getNext方式请求
*
* @param oid
* @throws Exception
*/
public static List<Map<String,Variable>> snmpGetNext(String[] oids, int version) throws Exception {
try {
System.out.println("first way:");
List<Map<String,Variable>> list = new ArrayList<Map<String,Variable>>();
//1、初始化snmp,并开启监听
initSnmp();
//2、创建目标对象
Target target = createTarget(version);
//3、创建报文
// PDU pdu = createPDU(1, PDU.GETNEXT, oid);
PDU pdu = createMulPDU(version, PDU.GETNEXT, oids);
//获取类属性的注解配置(主要是属性上的oid)
//4、发送报文,并获取返回结果
ResponseEvent responseEvent = snmp.getNext(pdu, target);
if (responseEvent == null || responseEvent.getResponse() == null) {
System.out.println("Send TimeOut...");
return null;
}
PDU response = responseEvent.getResponse();
String nextOid = null;
List<? extends VariableBinding> vbs = response.getVariableBindings();
for (VariableBinding variableBinding : vbs) {
Variable variable = variableBinding.getVariable();
nextOid = variableBinding.getOid().toDottedString();
//如果不是这个节点下的oid则终止遍历,否则会输出很多,直到整个遍历完。
if (StringUtils.isNullOrEmpty(nextOid)) {
break;
}
//给oid对应的实体类属性赋值
Map<String,Variable> data = new HashMap<String, Variable>();
data.put(nextOid, variable);
list.add(data);
System.out.println("data=" + data);
}
return list;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
//开启监控的main方法。
public static void main(String[] args) {
SnmpUtil2 multithreadedtrapreceiver;
try {
multithreadedtrapreceiver = new SnmpUtil2();
multithreadedtrapreceiver.run();
}
catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Thread.sleep(5000);
// multithreadedtrapreceiver.run();
}
private void run() {
try {
System.out.println("开始监听walk信息!");
//v3 协议测试
List<Map<String, Variable>> list = this.snmpWalk("1.3.6.1.4.1.2011.6.139.13.3.3", 3);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}