Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

snmp4j学习笔记

9
Sep
2015

snmp4j学习笔记

By Alex
/ in Java,Network
/ tags SNMP, 学习笔记
0 Comments
snmp4j简介

snmp4j是Java社区首选的SNMP协议的开源实现,支持JDK1.4+版本,该框架受C++库snmp++启发,以面向对象的方式进行设计。snmp4j同时支持Manager、Agent两种角色,支持同步、异步通信方式,以及多种验证方式和加密算法。snmp4j的具体特性如下:

  1. 支持MD5、SHA身份验证
  2. 支持SNMPv3的DES、3DES、AES128、AES192、AES256加密
  3. 支持协议:SNMPv1、SNMPv2c、SNMPv3
  4. 支持全部PDU类型
  5. 支持多种传输层协议:UDP、TCP、TLS
  6. 支持同步和异步请求
  7. 支持基于行的(row-based)异步表格获取
核心类型简介
关系示意图

下图描述了snmp4j最重要的核心类之间的关系,其中蓝色箭头代表向远程snmp实体(Target)发送的请求,红色代表远程snmp实体发送给当前snmp会话的请求。

snmp4j-api-flow

Snmp类,一般称为snmp4j会话,是整个框架的核心,用户代码主要和它进行交互。

snmp4j向外部SNMP实体发送请求的过程如下:

  1. 用户代码(Invoker)通过Snmp.send()方法发起请求
  2. TransportMapping组件通过底层的TCP或者UDP协议将请求传输出去
  3. 请求、应答的载体均是PDU,对应了SNMP报文
  4. Target代表远程SNMP实体,持有它的地址、身份、SNMP版本等信息
  5. 对于同步请求,应答直接返回给Invoker;对于异步请求,应答通过ResponseListener回调,ResponseListener在Invoker发送异步请求时,作为方法参数注册

snmp4j接收外部SNMP实体的请求,并进行处理的过程如下:

  1. snmp4j监听(通过TransportMapping)外部SNMP报文,并转为PDU类型
  2. MessageDispatcher负责将PDU转发给(回调)合适的CommandResponder,自定义CommandResponder可以注册到Snmp会话
  3. CommandResponder负责处理请求PDU,可选的,给出一个应答PDU
API说明

下表列出snmp4核心类的职责、提供的方法以及代码样例:

 类型 说明 
Snmp

SNMP会话类,该类是snmp4j的核心,提供发送/接收SNMP的PDU的功能。支持发送所有类型的PDU,支持异步、同步两种方式发送。

Snmp独立于具体的传输层协议,和传输层有关的工作由TransportMapping负责,有两种方式可以将Snmp关联到某种传输:

Java
1
2
3
4
5
6
7
//一般我们都是用UDP作为传输层协议
TransportMapping transport = new DefaultUdpTransportMapping();
//可以
Snmp snmp = new Snmp( transport );
//或者
Snmp snmp = new Snmp( );
snmp.addTransportMapping( transport );

除了关联传输,还可以为Snmp指定消息分发器:

Java
1
snmp = new Snmp( new MessageDispatcherImpl(), transport );

Snmp准备好后,需要启动监听,以便接收其它SNMP实体发送来的报文:

Java
1
transport.listen();

要发送SNMP报文,首先要构建目标(Taget),目标映射接收方SNMP实体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Address targetAddress = GenericAddress.parse( "udp:127.0.0.1/161" );
 
//基于团体名的目标(SNMPv1、SNMPv2c)
CommunityTarget target = new CommunityTarget();
target.setCommunity( new OctetString( "public" ) );
target.setAddress( targetAddress );
target.setVersion( SnmpConstants.version1 );
 
//基于用户的目标(SNMPv3)
UserTarget target = new UserTarget();
target.setAddress( targetAddress );
target.setVersion( SnmpConstants.version3 );
target.setSecurityLevel( SecurityLevel.AUTH_PRIV );
target.setSecurityName( new OctetString( "ALEX" ) );

然后,构建PDU,即需要发送的报文:

Java
1
2
3
4
5
6
7
8
9
//SNMPv3的PDU
PDU pdu = new ScopedPDU();
//SNMPv2c的PDU
PDU pdu = new PDU();
 
//添加变量绑定到PDU
pdu.add( new VariableBinding( new OID( "1.3.6" ) ) );
//设置PDU类型
pdu.setType( PDU.GETNEXT );

构建好PDU后,就可以通过同步(同步方式不支持广播消息)或者异步方式,将其发送给target了:

Java
1
2
3
4
//发送PDU,同步得到应答
ResponseEvent response = snmp.send( pdu, target );
//异步发送PDU,应答到达时通过listener 回调
snmp.send( pdu, target, null, listener );

对于Manager角色,如果要接收来自Agent的trap,可以:

Java
1
2
3
4
5
6
7
8
9
CommandResponder trapPrinter = new CommandResponder() {
    public synchronized void processPdu( CommandResponderEvent e )
    {
        PDU command = e.getPDU();
        //处理trap
        e.setProcessed( true );
    }
};
snmp.addCommandResponder( trapPrinter );

对于Agent角色,如果要接收来自Manger的请求,并做出应答,还是使用上面的CommandResponder:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public synchronized void processPdu( CommandResponderEvent e )
{
    PDU command = e.getPDU();
    if ( PDU.GET == command.getType() )
    {
        //请求变量的OID
        OID oid = command.get( 0 ).getOid();
        //重用命令对象
        command.setErrorIndex( 0 );
        command.setErrorStatus( 0 );
        command.setType( PDU.RESPONSE );
        int val = query( oid ); //业务方法负责查询OID的值
        command.get( 0 ).setVariable( new Integer32( val ) );
        try
        {
            MessageDispatcher dispatcher = e.getMessageDispatcher();
            //发送应答PDU
            dispatcher.returnResponsePdu(
                    e.getMessageProcessingModel(),
                    e.getSecurityModel(),
                    e.getSecurityName(),
                    e.getSecurityLevel(),
                    command,
                    e.getMaxSizeResponsePDU(),
                    e.getStateReference(),
                    new StatusInformation()
                    );
        }
        catch ( MessageException ex )
        {
            System.out.println( "Failed to send response PDU" );
        }
        finally
        {
            e.setProcessed( true );
        }
    }
}

最后,应当关闭会话和transport:

Java
1
2
snmp.close();
transport.close();
PDU

在TCP/IP协议族中,协议数据单元(Protocol Data Unit,PDU)表示使用某个协议的两个实体之间发送的报文,包括报文头和报文体。由于TCP/IP协议族是分层结构,因而下层协议PDU的载荷部分,就是上层协议的PDU。

在SNMP协议的报文头中,具有一个字段“PDU类型”,可以理解为报文的类型。

snmp4j使用该类构建SNMP协议报文。该类实现了BERSerializable接口,后者定义了如何将ASN.1数据类型串行化为BER格式的方法。

PDU类定义了以下常量,来代表SNMP报文类型:

  1. GET:get-request报文
  2. GETNEXT:get-next-response报文
  3. RESPONSE:get-response、set-response报文
  4. SET:set-request报文
  5. V1TRAP:SNMPv1的trap报文
  6. GETBULK:SNMPv2c/v3的get-bulk-request报文
  7. INFORM:SNMPv2c/v3的inform报文
  8. TRAP:SNMPv2c/v3的trap报文
  9. NOTIFICATION:SNMPv2c/v3的notification报文
  10. REPORT:SNMPv3的report报文

PDU类提供以下重要方法:

  1. add()、addAll():把变量绑定添加到当前PDU
  2. addOID()、addAllOIDs():把变量绑定添加到当前PDU,只添加变量名(OID),值置NULL
  3. get():得到当前PDU指定索引上的变量绑定
  4. set():设置当前PDU指定索引上的变量绑定
  5. remove():移除当前PDU指定索引上的变量绑定
  6. clear():移除所有变量绑定,设置请求ID为0,可以用来重用PDU对象
  7. size():当前PDU包含的变量绑定的个数
  8. getVariableBindings():得到所有变量绑定的集合
  9. trim():移除最后一个变量绑定,如果至少存在一个绑定
  10. setErrorStatus():设置PDU的错误状态字段
  11. setErrorIndex():设置PDU的错误索引,1表示第一个变量绑定存在错误
  12. isConfirmedPdu():判断该PDU是否是需要确认的PDU(非report、trap、response)
  13. setType():设置PDU类型
  14. setRequestID():设置请求ID,如果设置为0或者空,发送时消息处理模型自动生成

 PDU包含以下子类型:

  1. PDUv1:表示SNMPv1的PDU
  2. ScopedPDU:表示SNMPv3的scoped PDU

注意对于SNMPv2c,直接使用PDU类

下面的代码示例了如何创建一个SNMPv3的PDU:

Java
1
2
3
PDU pdu = new ScopedPDU();
pdu.add( new VariableBinding( new OID( "1.3.6" ) ) );
pdu.setType( PDU.GETNEXT );
Target

目标,该接口代表远程(即对端)SNMP实体(Manager、Agent)。定义了以下方法:

  1. setAddress():设置目标的地址
  2. setVersion():设置目标的SNMP版本号(org.snmp4j.mp.SnmpConstants)
  3. setRetries():设置请求超时前重试的次数
  4. setTimeout():设置请求超时的毫秒数
  5. setMaxSizeRequestPDU():目标支持的最大PDU长度
  6. 对应的getter方法

该接口的缺省实现是AbstractTarget,并且包含几个子类型,这些子类型是按照安全性特征划分的:

  1. CommunityTarget:SNMPv1、SNMPv2c的基于团体名的消息处理模型的目标
  2. SecureTarget:与安全模型独立的目标超类,用来支持安全的SNMP通信。包含子类
    1. UserTarget:用于SNMPv3的,基于用户的目标

下面是创建目标的样例代码:

Java
1
2
3
4
5
6
7
8
9
10
/* SNMPv3基于用户的target */
UserTarget target = new UserTarget();
target.setAddress( targetAddress );
target.setRetries( 1 ); //重试次数
target.setTimeout( 5000 ); //请求超时
target.setVersion( SnmpConstants.version3 );//SNMP版本
//安全级别
target.setSecurityLevel( SecurityLevel.AUTH_PRIV );
//用户名
target.setSecurityName( new OctetString( "ALEX" ) );
ResponseEvent

应答事件类,该类继承了JDK的观察者模式组件。应答事件将一个请求PDU和它的响应,以及一个可选的用户对象进行关联。它提供以下方法:

  1. getRequest():得到请求PDU
  2. getResponse():得到响应PDU
  3. getUserObject():对于异步请求,可读写一个可选的用户自定义对象
  4. getError():如果请求PDU处理出错,可以从中得到异常对象
  5. getPeerAddress():得到对端的地址,即应答发送方的地址

下面的代码示例如何使用该类:

Java
1
2
3
4
5
//发送请求PDU后,可以得到应答事件对象
ResponseEvent response = snmp.send( pdu, target );
//得到应答PDU
PDU responsePDU = response.getResponse();
Address peerAddress = response.getPeerAddress();
ResponseListener

这是一个回调接口,实现它可以异步的处理应答PDU:

Java
1
2
3
4
5
6
7
8
9
10
11
//捕获trap并打印的例子:
ResponseListener listener = new ResponseListener() {
   public void onResponse( ResponseEvent event )
   {
      //当收到响应时,应当总是取消异步请求,否则会导致内存泄漏
      //将请求发送到广播地址时,不立即取消请求
      ((Snmp) event.getSource()).cancel( event.getRequest(),this);
      System.out.println( "Resp PDU is: " + event.getResponse());
   }
};
snmp.send( pdu, target, null, listener );
CommandResponderEvent

该事件由MessageDispatcher负责分发,相应的CommandResponder会接收到并处理与该事件关联的request、report、trap/notification类型的PDU。

该类提供以下方法:

  1. getSecurityModel():得到使用的安全模型
  2. getSecurityLevel():得到安全级别
  3. getPDU():得到PDU
  4. isProcessed():事件是否已经被处理
  5. getPeerAddress():得到对端SNMP实体的地址
CommandResponder

该接口用来处理入站request、report、notification(trap)类型的PDU:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
CommandResponder trapPrinter = new CommandResponder() {
    public synchronized void processPdu( CommandResponderEvent e )
    {
        PDU command = e.getPDU();
        if ( command != null )
        {
            System.out.println( command.toString() );
        }
        //每个命令只能被处理一次,因此再处理完毕后必须设置下面的标记
        e.setProcessed( true );
    }
};
snmp.addCommandResponder( trapPrinter );
MessageDispatcher

消息分发器接口,处理incoming消息并将消息分发给感兴趣的CommandResponder,同时支持发送outgoing消息。

消息分发器至少需要一个TransportMapping、一个MessageProcessingModel对象,以支持处理任何消息。该接口包含两个实现类:

  1. MessageDispatcherImpl:使用MessageProcessingModel解码、分发incoming消息;或者使用适当的TransportMapping来编码、发送outgoing消息
  2. MultiThreadedMessageDispatcher:MessageDispatcher的装饰器,用来提供多线程并行处理的支持
Address

该接口对SNMP协议支持的地址类型进行抽象:

snmp4j-address

TransportMapping

该接口定义了传输机制,传输机制是Snmp使用的某种传输层协议的支持。常用的实现为:DefaultTcpTransportMapping、DefaultUdpTransportMapping,分别代表TCP、UDP传输。该接口定义了以下重要方法:

  1. sendMessage():使用该传输发送消息到某一地址
  2. add/removeTransportListener():添加或删除传输监听器。一般至少有一个传输监听器被配置,以接收incoming消息
  3. listen():启动监听,等待incoming消息的到来
  4. close():关闭该传输,并释放相关的资源

定义传输机制时,可以指定它的监听地址和端口:

Java
1
2
//不使用默认SNMP端口
UdpAddress udpAddress = new UdpAddress( "192.168.0.89/16100" );
TransportListener

该接口定义了一个方法:processMessage(),用来处理incoming消息,该接口由Snmp4j负责回调

MessageProcessingModel

消息处理模型的通用方法定义:

  1. getID():得到消息处理模型的数字常量ID
  2. prepareOutgoingMessage():准备一个RFC3412出站消息
  3. prepareResponseMessage():准备一个RFC3412应答消息
  4. prepareDataElements():准备处理来自RFC3412入站消息的数据元素
  5. isProtocolVersionSupported():检查某个SNMP协议是否被当前模型支持

具体实现类包括:
MPv1为SNMPv1的消息处理模型
MPv2c为SNMPv2c的消息处理模型
MPv3为SNMPv3的消息处理模型

SecurityProtocols

管理SNMP实体支持的所有的身份验证和隐私协议,单例。定义了以下方法:

  1. addDefaultProtocols():添加默认安全协议
  2. addAuthenticationProtocol():添加身份验证协议
  3. getAuthenticationProtocol():根据OID得到某个身份验证协议
  4. removeAuthenticationProtocol():移除某个身份验证协议
  5. add/get/removePrivacyProtocol:添加、得到、删除某个隐私(加解密)协议
AuthenticationProtocol

为所有SNMP身份验证协议定义统一接口

PrivacyProtocol 为所有SNMP隐私(即加解密)协议定义统一接口
SecurityModels

管理SNMP实体支持的所有安全模型,单例。定义了以下方法:

  1. addSecurityModel():添加一个安全模型
  2. removeSecurityModel():移除一个安全模型
  3. getSecurityModel():得到一个安全模型
SecurityModel

该接口映射RFC3411定义的安全模型。该接口定义了以下安全模型ID常量:

SECURITY_MODEL_ANY = 0   
SECURITY_MODEL_SNMPv1 = 1
SECURITY_MODEL_SNMPv2c = 2
SECURITY_MODEL_USM = 3

以及以下方法:

  1. getID():获得当前安全模型的ID
  2. newSecurityParametersInstance():创建一个与当前模型匹配的安全参数实例
  3. generateRequestMessage():依据消息处理模型、安全参数、消息载荷来创建一个请求消息
  4. generateResponseMessage():创建应答消息
  5. processIncomingMsg():处理入站消息

该接口包含一个实现类:USM,对应RFC3414定义的基于用户的安全模型(User Based Security Model)。下面的代码示例了该安全模型的使用方法:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//SMMPv3要求代理定义一个唯一性的引擎ID
OctetString eid = new OctetString( MPv3.createLocalEngineID() );
//创建安全模型
USM usm = new USM( SecurityProtocols.getInstance(), eid , 0 );
//添加到安全模型管理器
SecurityModels.getInstance().addSecurityModel( usm );
OctetString userName = new OctetString( "ALEX" );
OctetString pswd= new OctetString( "ALEXUserPrivPassword" );
OctetString passphrase = new OctetString( "ALEXUserAuthPassword" );
//创建用户
UsmUser user = new UsmUser(
    userName,     // 安全性名称(用户名)
    AuthMD5.ID,   // 身份验证协议
    passphrase ,  // 身份验证短语(密码)
    PrivDES.ID,   // 隐私协议
    pswd          // 加解密短语(密码)
);
snmp.getUSM().addUser( userName, user ); //在安全模型中添加用户
Variable

该接口映射SNMP管理信息结构(SMI)定义的数据类型,包含了一个实现类AbstractVariable,后者有以下子类:

AbstractVariables

OID

OID是一种特殊的Variable,代表对象标识符(不是实例标识符)

VariableBinding

即变量名、值对,变量名即OID,变量值即Variable

BaseAgent

该类定义了一个框架,用户可以扩展它、覆盖其中的抽象方法来创建SNMP代理。该类定义了以下必须扩展点(抽象方法):

  1. registerManagedObjects():注册该代理管理的额外对象
  2. unregisterManagedObjects():解除注册该代理管理的额外对象
  3. addUsmUser(USM usm):添加所有必须的初始用户到安全模型中
  4. addNotificationTargets(SnmpTargetMIB, SnmpNotificationMIB):添加初始的notification目标和过滤器。SnmpTargetMIB提供Target的配置,SnmpNotificationMIB提供notification的配置
  5. addViews(VacmMIB):添加初始的VACM配置。VacmMIB持有代理的视图配置
  6. addCommunities(SnmpCommunityMIB):添加SNMPv1、SNMPv2c的团体名映射

该类还定义了以下可选扩展点(受保护的钩子):

  1. initConfigMIB():初始化配置MIB
  2. getContext(MOGroup):子类覆盖后,可以指定每个MIB模块(组)被注册到的上下文
  3. registerSnmpMIBs():为代理的MOServer注册基本的MIB模块
  4. unregisterSnmpMIBs():解除注册基本的MIB模块
  5. setupDefaultProxyForwarder():创建并注册默认的代理转发器程序(proxy forwarder application)
  6. addShutdownHook():添加JRE标准的关闭钩子,钩子在收到SIGTERM信号后执行
  7. finishInit():完成代理的初始化,默认实现将:连接服务和命令处理器(CommandProcessor)、设立USM、VACM、通知目标。并最终发送coldStart消息给通知目标
  8. sendColdStartNotification():发送冷启动通知给通知目标
  9. initMessageDispatcher():初始化消息分发器
  10. initSnmpSession():初始化SNMP会话
  11. updateSession(Session):为一个会话更新所有对象,如果子类创建了非默认的SNMP MIB实例,必须覆盖此方法
  12. updateEngineBoots():更新引起启动次数计数器
  13. initTransportMappings():初始化该代理使用的传输机制
MOServer

受管对象服务器,负责管理注册的ManagedObject,该接口定义了受管对象仓库必须提供给CommandResponder的服务。包含以下方法:

  1. addContextListener(ContextListener):添加上下文监听器,当上下文被添加或者删除,监听器会被通知
  2. removeContextListener(ContextListener):移除上下文监听器
  3. addContext(OctetString):添加上下文到服务器
  4. removeContext(OctetString):移除上下文
  5. register(ManagedObject, OctetString):为指定上下文注册一个受管对象,一个受管对象可以注册到多个上下文。如果第二个参数为null,则注册到所有上下文(包括默认上下文)
  6. unregister(ManagedObject, OctetString):解除注册受管对象
  7. addLookupListener():添加查找监听器,lookup调用返回前,监听器被通知
  8. removeLookupListener():移除查找监听器
  9. lookup(MOQuery):查找第一个(字典序)匹配的受管对象
  10. iterator():返回一个迭代器,以迭代该服务器上的内容
  11. lock(Object, ManagedObject):锁定一个对象,对其进行lookup将阻塞直到解锁或超时
  12. unlock(Object, ManagedObject):解锁一个对象
  13. getContexts():获得所有上下文
  14. isContextSupported(OctetString):是否支持指定上下文
ManagedObject 该接口抽象“受管对象”,一个受管对象可能对应MIB中的某个分支,因而可以包含多个OID,OID的范围由MOScope指定
MOScope 表示一个连续的OID区域,具有OID上下限
MOGroup

受管对象组,表示一组ManagedObject,可以通过 registerMOs(MOServer server, OctetString context) 注册到MOServer。该接口实际上对应了管理信息库(MIB)对象。包含以下实现(其中MOGroupImpl是一个简单的实现,StaticMOGroup则可以用来轻松的实现静态、只读受管对象组):

MOGroupImpls

NotificationOriginator

通知源,实现该接口的对象负责发送notification,它定义了一个方法:

Java
1
2
3
4
5
6
7
8
//发送notifications(traps)到合适的目标
//目标由SNMP-TARGET-MIB、SNMP-NOTIFICATION-MIB指定
Object notify(
    OctetString context,//通知对应的上下文
    OID notificationID, //唯一代表通知的OID
    TimeTicks sysUpTime, //上下文的系统启动时间
    VariableBinding[] vbs //通知的载荷
);
CommandProcessor

BaseAgent使用的一个实现了CommandResponder、NotificationOriginator的粘合剂类,负责处理请求、发送通知

网络管理站程序(Manager)开发
请求和批量请求操作
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package cc.gmem.study.snmp;
 
import java.util.Vector;
import java.util.concurrent.TimeUnit;
 
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.UserTarget;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.AuthMD5;
import org.snmp4j.security.PrivDES;
import org.snmp4j.security.SecurityLevel;
import org.snmp4j.security.SecurityModel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.security.UsmUser;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TimeTicks;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
 
public class Snmp4jManagerRequestTest
{
    private static Logger           LOGGER = LoggerFactory.getLogger( Snmp4jManagerRequestTest.class );
 
    private static Snmp             session;
 
    private static TransportMapping transport;
 
    private static Address          targetAddr;
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        transport = new DefaultUdpTransportMapping();
        session = new Snmp( transport );
        targetAddr = GenericAddress.parse( "udp:192.168.0.90/161" );
        transport.listen();
 
        //需要在Manager的数据库中添加Agent用户的安全性信息
        SecurityProtocols instance = SecurityProtocols.getInstance();
        instance.addDefaultProtocols();
        USM usm = new USM( instance, new OctetString( MPv3.createLocalEngineID() ), 0 );
        SecurityModels.getInstance().addSecurityModel( usm );
 
        //添加SNMPv3的用户
        OctetString securityName = new OctetString( "snmp" );
        OctetString pswd = new OctetString( "snmppswd" );
        UsmUser user = new UsmUser( securityName, AuthMD5.ID, pswd, PrivDES.ID, pswd );
        session.getUSM().addUser( securityName, user );
    }
 
    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        session.close();
        transport.close();
    }
 
    /**
     * 使用SNMPv2c协议发送一个同步GET请求,获取代理系统的已启动时间
     */
    @Test
    public void testSNMPv2cSyncGetRequest() throws Exception
    {
        Target target = new CommunityTarget(
                targetAddr,
                new OctetString( "authenticated" ) //团体名
        );
        target.setVersion( SnmpConstants.version2c );
 
        PDU req = new PDU(); //SNMPv2c协议数据单元
        req.setType( PDU.GET );
        //注意OID必须使用实例标识,对于简单变量,实例标识就是OID.0
        req.add( new VariableBinding( new OID( "1.3.6.1.2.1.1.3.0" ) ) );
        //同步发送,直接获得返回值
        ResponseEvent respEvent = session.send( req, target );
        PDU resp = respEvent.getResponse();
        //需要此判断,很多时候(例如超时)snmp4j简单的把应答PDU设置为空指针,而缺乏必要的错误信息
        if ( resp != null )
        {
            VariableBinding vb = resp.get( 0 );
            TimeTicks val = (TimeTicks) vb.getVariable();
            LOGGER.debug( "System Uptime: {}s", val.toLong() / 100 ); //转化为秒数
        }
    }
 
    /**
     * 使用SNMPv3协议发送一个异步GET请求,获取代理系统的已启动时间
     */
    @Test
    public void testSNMPv3ASyncGetRequest() throws Exception
    {
        UserTarget target = new UserTarget();
        target.setAddress( targetAddr );
        target.setVersion( SnmpConstants.version3 );
        target.setTimeout( 1000 );
        target.setRetries( 3 );
 
        target.setSecurityName( new OctetString( "snmp" ) );
        target.setSecurityLevel( SecurityLevel.AUTH_PRIV );
        target.setSecurityModel( SecurityModel.SECURITY_MODEL_USM );
 
        ScopedPDU req = new ScopedPDU(); //SNMPv3协议数据单元
        req.setType( PDU.GET );
        req.add( new VariableBinding( new OID( "1.3.6.1.2.1.1.3.0" ) ) );
        session.send( req, target, null, new ResponseListener() {
 
            public void onResponse( ResponseEvent event )
            {
                ( (Snmp) event.getSource() ).cancel( event.getRequest(), this );
                TimeTicks val = (TimeTicks) event.getResponse().get( 0 ).getVariable();
                LOGGER.debug( "System Uptime: {}s", val.toLong() / 100 );
            }
        } );
        TimeUnit.SECONDS.sleep( 1 ); //防止JUnit过早的退出
    }
 
    /**
     * 使用SNMPv2c协议执行GET-BULK请求,获得代理的UDP监听表格
     */
    @SuppressWarnings ( "unchecked" )
    @Test
    public void testSNMPv2cGetBulkRequest() throws Exception
    {
        Target target = new CommunityTarget( targetAddr, new OctetString( "authenticated" ) );
        target.setVersion( SnmpConstants.version2c );
 
        PDU req = new PDU();
        req.setType( PDU.GETBULK );
        //批量请求最大重复变量个数,注意,应答PDU在这里受限于单个UDP数据报的大小,因此不能设置的过大
        //单个UDP数据报的绝对限制是65535字节,此外还收到MTU的限制,可能只有1500字节
        req.setMaxRepetitions( 100 );
        req.setNonRepeaters( 0 );
        req.add( new VariableBinding( new OID( "1.3.6.1.2.1.7.5" ) ) );
        ResponseEvent respEvent = session.send( req, target );
        PDU resp = respEvent.getResponse();
        if ( resp != null && resp.getErrorStatus() == PDU.noError )
        {
            Vector<? extends VariableBinding> vbs = resp.getVariableBindings();
            StringBuilder buf = new StringBuilder();
            for ( VariableBinding vb : vbs )
            {
                Variable var = vb.getVariable();
                String oid = vb.getOid().toString();
                if ( !oid.startsWith( "1.3.6.1.2.1.7.5.1" ) ) break; //超过UDP监听表格的范围
                buf.append( String.format( "%s: (%s)%s\n", vb.getOid(), var.getSyntaxString(), var ) );
            }
            LOGGER.debug( buf.toString() );
        }
    }
}
陷阱/通知监听
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class Snmp4jManagerTrapTest
{
    private static Logger           LOGGER        = LoggerFactory.getLogger( Snmp4jManagerTrapTest.class );
 
    private static Snmp             session;
 
    private static TransportMapping transport;
 
    private static ThreadPool       threadPool;
 
    private Lock                    exitLock      = new ReentrantLock();
 
    private Condition               exitCondition = exitLock.newCondition();
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        UdpAddress localAddr = (UdpAddress) GenericAddress.parse( "udp:0.0.0.0/162" );//Manager的监听地址
        transport = new DefaultUdpTransportMapping( localAddr );
        threadPool = ThreadPool.create( "TrapReceiver", 16 );//创建16线程的池
        //创建多线程的报文分发器
        MessageDispatcher dispatcher = new MultiThreadedMessageDispatcher( threadPool, new MessageDispatcherImpl() );
        dispatcher.addMessageProcessingModel( new MPv2c() ); //添加消息处理模型支持
        dispatcher.addMessageProcessingModel( new MPv3() ); //添加消息处理模型支持
        session = new Snmp( dispatcher, transport );
        transport.listen();
 
        SecurityProtocols instance = SecurityProtocols.getInstance();
        instance.addDefaultProtocols();
        USM usm = new USM( instance, new OctetString( MPv3.createLocalEngineID() ), 0 );
        SecurityModels.getInstance().addSecurityModel( usm );
        OctetString securityName = new OctetString( "user" );
        OctetString pswd = new OctetString( "password" );
        UsmUser user = new UsmUser( securityName, AuthMD5.ID, pswd, PrivDES.ID, pswd );
        session.getUSM().addUser( securityName, user );
    }
 
    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        session.close();
        transport.close();
        threadPool.cancel();
    }
 
 
    @Test
    public void testTrapAndNotification() throws Exception
    {
        session.addCommandResponder( new CommandResponder() {
            private volatile int mscCount;
 
            @SuppressWarnings ( "unchecked" )
            public void processPdu( CommandResponderEvent event )
            {
                String community = new String( event.getSecurityName() );
                int secmod = event.getSecurityModel();
                if ( secmod == SECURITY_MODEL_SNMPv2c )
                {
                    //基于团体名的身份验证
                    if ( !"public".equals( community ) )
                    {
                        LOGGER.error( "Authentication failed." );
                        return;
                    }
 
                }
                else if ( secmod == SECURITY_MODEL_USM )
                {
                    //不用管,snmp4j负责身份验证,如果失败,CommandResponder不会接收到报文
                }
                else
                {
                    LOGGER.error( "Unsupported security model: {}", secmod );
                    return;
                }
                PDU command = event.getPDU();
                if ( command.getType() == PDU.TRAP )
                {
                    Vector<? extends VariableBinding> vbs = command.getVariableBindings();
                    StringBuilder buf = new StringBuilder();
                    for ( VariableBinding vb : vbs )
                    {
                        Variable var = vb.getVariable();
                        buf.append( String.format( "%s: (%s)%s\n", vb.getOid(), var.getSyntaxString(), var ) );
                    }
                    LOGGER.debug( buf.toString() );
                    event.setProcessed( true ); //设置事件的已处理状态
                }
                else if ( command.getType() == ScopedPDU.NOTIFICATION )
                {
                    
                }
                else
                {
                    return;
                }
                mscCount++;
                if ( mscCount == 6 ) //等待Agent发送6个报文
                {
                    exitLock.lock();
                    exitCondition.signalAll();
                    exitLock.unlock();
                }
            }
        } );
        exitLock.lock();
        exitCondition.await();
        exitLock.unlock();
    }
}
被管网络单元程序(Agent)开发
发送陷阱/通知
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class Snmp4jAgentTrapTest
{
    private static final String     TRAP_OID = "1.3.6.1.4.1.3835.1.1";
 
    private static final String     MSG_OID  = "1.3.6.1.4.1.3835.1.3";
 
    private static Logger           LOGGER   = LoggerFactory.getLogger( Snmp4jAgentTrapTest.class );
 
    private static Snmp             session;
 
    private static Address          targetAddr;
 
    private static TransportMapping transport;
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        UdpAddress localAddr = (UdpAddress) GenericAddress.parse( "udp:0.0.0.0/161" );//Agent的监听地址
        targetAddr = GenericAddress.parse( "udp:127.0.0.1/162" ); //Manager的监听地址
        transport = new DefaultUdpTransportMapping( localAddr );
        session = new Snmp( transport );
        transport.listen();
 
        SecurityProtocols instance = SecurityProtocols.getInstance();
        instance.addDefaultProtocols();
        USM usm = new USM( instance, new OctetString( MPv3.createLocalEngineID() ), 0 );
        SecurityModels.getInstance().addSecurityModel( usm );
        OctetString securityName = new OctetString( "user" );
        OctetString pswd = new OctetString( "password" );
        UsmUser user = new UsmUser( securityName, AuthMD5.ID, pswd, PrivDES.ID, pswd );
        session.getUSM().addUser( securityName, user );
    }
 
    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        session.close();
        transport.close();
    }
 
    /**
     * 发送SNMPv2c的TRAP通知给Manager
     */
    @Test
    public void testSNMPv2cTrap() throws Exception
    {
 
        CommunityTarget target = new CommunityTarget();
        target.setAddress( targetAddr );
        target.setCommunity( new OctetString( "public" ) ); //Manager认可的团体名
        target.setVersion( SnmpConstants.version2c );
        target.setTimeout( 3000 );
 
        PDU trap = DefaultPDUFactory.createPDU( SnmpConstants.version2c );
        trap.setType( PDU.TRAP ); //对于SNMPv1,需要使用PDU.V1TRAP类型
 
        for ( int i = 0; i < 5; i++ )
        {
            trap.clear();
            trap.add( new VariableBinding( SnmpConstants.sysUpTime, new TimeTicks( 360000L ) ) );
            trap.add( new VariableBinding( SnmpConstants.snmpTrapOID, new OID( TRAP_OID ) ) );
            trap.add( new VariableBinding( SnmpConstants.snmpTrapAddress, new IpAddress( "192.168.0.89" ) ) );
            String msg = "Trapped " + i;
            trap.add( new VariableBinding( new OID( MSG_OID ), new OctetString( msg ) ) );
            session.send( trap, target );
            LOGGER.debug( "TRAP sent: {}", msg );
            TimeUnit.SECONDS.sleep( 1 );
        }
    }
 
    /**
     * 发送SNMPv3的NOTIFICATION通知给Manager
     */
    @Test
    public void testSNMPv3Notification() throws Exception
    {
        UserTarget target = new UserTarget();
        target.setAddress( targetAddr );
        target.setVersion( SnmpConstants.version3 );
        target.setSecurityName( new OctetString( "user" ) );
        target.setSecurityLevel( SecurityLevel.AUTH_PRIV );
        target.setSecurityModel( SecurityModel.SECURITY_MODEL_USM );
 
        ScopedPDU notify = new ScopedPDU();
        notify.setType( ScopedPDU.NOTIFICATION ); //使用SNMPv2c/SNMPv3的通知方式
 
        notify.add( new VariableBinding( SnmpConstants.sysUpTime, new TimeTicks( 360000L ) ) );
        notify.add( new VariableBinding( SnmpConstants.snmpTrapOID, SnmpConstants.linkDown ) );
        String msg = "Notified";
        notify.add( new VariableBinding( new OID( TRAP_OID ), new OctetString( msg ) ) );
        session.send( notify, target );
        LOGGER.debug( "NOTIFICATION sent: {}", msg );
        TimeUnit.SECONDS.sleep( 1 );
    }
}
应答请求
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class Snmp4jAgentResponderTest
{
    private static Logger           LOGGER        = LoggerFactory.getLogger( Snmp4jAgentResponderTest.class );
 
    private static Snmp             session;
 
    private static ThreadPool       threadPool;
 
    private static TransportMapping transport;
 
    private Lock                    exitLock      = new ReentrantLock();
 
    private Condition               exitCondition = exitLock.newCondition();
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        UdpAddress localAddr = (UdpAddress) GenericAddress.parse( "udp:0.0.0.0/161" );//Agent的监听地址
        transport = new DefaultUdpTransportMapping( localAddr );
        threadPool = ThreadPool.create( "TrapReceiver", 16 );//创建16线程的池
        //创建多线程的报文分发器
        MessageDispatcher dispatcher = new MultiThreadedMessageDispatcher( threadPool, new MessageDispatcherImpl() );
        dispatcher.addMessageProcessingModel( new MPv2c() ); //添加消息处理模型支持
        dispatcher.addMessageProcessingModel( new MPv3() ); //添加消息处理模型支持
        session = new Snmp( dispatcher, transport );
        transport.listen();
    }
 
    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        session.close();
        transport.close();
        threadPool.cancel();
    }
 
    /**
     * 处理GET请求并返回应答
     */
    @Test
    public void testSNMPv2cResponse() throws Exception
    {
        //和TRAP接收方一样,请求接收方同样使用CommandResponder回调接口
        //需要注意的是,为了提升性能,一般需要使用MultiThreadedMessageDispatcher
        session.addCommandResponder( new CommandResponder() {
 
            public void processPdu( CommandResponderEvent event )
            {
                //这里可以限定请求使用的安全模型,并进行身份验证
 
                PDU command = event.getPDU();
                if ( PDU.GET == command.getType() )
                {
                    VariableBinding vb = command.get( 0 );
                    OID oid = vb.getOid();
                    command.setErrorIndex( 0 );
                    command.setErrorStatus( 0 );
                    command.setType( PDU.RESPONSE );
                    //模拟一个应答值,正常业务逻辑中,这里可以调用某种服务获得对象的值
                    String val = "value.of." + oid;
                    command.get( 0 ).setVariable( new OctetString( val ) );
                    try
                    {
                        MessageDispatcher dispatcher = event.getMessageDispatcher();
                        //发送应答报文
                        dispatcher.returnResponsePdu(
                                event.getMessageProcessingModel(),
                                event.getSecurityModel(),
                                //这里我们假设使用团体名的验证方式
                                new OctetString( "public" ).getValue(),
                                event.getSecurityLevel(),
                                command,
                                event.getMaxSizeResponsePDU(),
                                event.getStateReference(),
                                new StatusInformation() );
                        LOGGER.debug( "Responded {} to {}", val, oid );
 
                    }
                    catch ( MessageException e )
                    {
                        LOGGER.debug( "Failed to send response PDU:", e );
                    }
                    finally
                    {
                        event.setProcessed( true );
                        exitLock.lock();
                        exitCondition.signalAll();
                        exitLock.unlock();
                    }
                }
            }
        } );
        exitLock.lock();
        exitCondition.await();
        exitLock.unlock();
    }
 
}

在Ubuntu下做测试:

Shell
1
2
3
snmpget -v 2c -c public -l authNoPriv  192.168.0.89 1.3.6.1.4.1.3835.1.3
#输出:
#SNMPv2-SMI::enterprises.3835.1.3 = STRING: "value.of.1.3.6.1.4.1.3835.1.3"
Agent服务器

继承BaseAgent可以开发自己的Agent服务器,下面是一个简单的Agent服务器:

SnmpAgent.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package cc.gmem.study.snmp;
 
import java.io.File;
import java.io.IOException;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.TransportMapping;
import org.snmp4j.agent.BaseAgent;
import org.snmp4j.agent.CommandProcessor;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.mo.MOTableRow;
import org.snmp4j.agent.mo.snmp.RowStatus;
import org.snmp4j.agent.mo.snmp.SnmpCommunityMIB;
import org.snmp4j.agent.mo.snmp.SnmpNotificationMIB;
import org.snmp4j.agent.mo.snmp.SnmpTargetMIB;
import org.snmp4j.agent.mo.snmp.VacmMIB;
import org.snmp4j.agent.security.MutableVACM;
import org.snmp4j.log.Log4jLogFactory;
import org.snmp4j.log.LogFactory;
import org.snmp4j.mp.MPv3;
import org.snmp4j.security.USM;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.Variable;
import org.snmp4j.transport.TransportMappings;
 
import static org.snmp4j.security.SecurityLevel.*;
import static org.snmp4j.agent.mo.snmp.StorageType.*;
import static org.snmp4j.security.SecurityModel.*;
 
public class SnmpAgent extends BaseAgent
{
    private static final Logger      LOGGER    = LoggerFactory.getLogger( SnmpAgent.class );
 
    private static final OctetString ENGINE_ID = new OctetString( MPv3.createLocalEngineID() );
    static
    {
        //配置使用的日志系统
        LogFactory.setLogFactory( new Log4jLogFactory() );
    }
 
    private String                   address;
 
    /**
     * @param address 代理的监听地址,例如:udp:0.0.0.0/161
     */
    public SnmpAgent( String address ) throws IOException
    {
        //bootCounter.agent:存放启动计数器的文件,如果文件不存在会在代理关闭时自动创建
        //conf.agent:存放代理配置信息的文件,如果文件不存在会在代理关闭时自动创建
        super( new File( "bootCounter.agent" ), new File( "conf.agent" ), new CommandProcessor( ENGINE_ID ) );
        this.address = address;
    }
 
    /**
     * 不注册任何额外的受管对象
     */
    @Override
    protected void registerManagedObjects()
    {
    }
 
    /**
     * 注册一个受管对象到服务器的默认上下文
     * @param mo 受管对象
     */
    public void registerManagedObject( ManagedObject mo )
    {
        try
        {
            server.register( mo, null );
        }
        catch ( DuplicateRegistrationException ex )
        {
            LOGGER.error( "Registration failed: ", ex );
        }
    }
 
    public void unregisterManagedObject( MOGroup moGroup )
    {
        moGroup.unregisterMOs( server, getContext( moGroup ) );
    }
 
    @Override
    protected void addNotificationTargets( SnmpTargetMIB targetMIB, SnmpNotificationMIB notificationMIB )
    {
    }
 
    /**
     * 配置基于视图(View)的访问控制
     */
    @Override
    protected void addViews( VacmMIB vacm )
    {
        //把安全模型v2c和安全名称cpublic的组合添加到组
        OctetString grp = new OctetString( "group" );
        vacm.addGroup( SECURITY_MODEL_SNMPv2c, new OctetString( "cpublic" ), grp, nonVolatile );
        //为组添加访问权限
        OctetString readView = new OctetString( "fullReadView" );
        OctetString writeView = new OctetString( "fullWriteView" );
        OctetString notifyView = new OctetString( "fullNotifyView" );
        vacm.addAccess(
                grp, new OctetString( "public" ), //上下文(前缀)
                SECURITY_MODEL_ANY, //安全模型
                NOAUTH_NOPRIV, //安全级别
                MutableVACM.VACM_MATCH_EXACT, //上上下文名称完全匹配,VACM_MATCH_PREFIX表示前缀匹配
                readView, //用于读访问的视图名称(使用空串表示禁止访问)
                writeView,//用于写访问的视图名称(使用空串表示禁止访问)
                notifyView,//用于通知访问的视图名称(使用空串表示禁止访问)
                nonVolatile ); //存储类型
 
        //添加一个视图到VACM,前两个参数一样的既有条目被静默的覆盖
        vacm.addViewTreeFamily(
                readView, new OID( "1.3" ), //子树的OID
                new OctetString(), //掩码
                VacmMIB.vacmViewIncluded, //表示该子树被包含在MIB View中,vacmViewExcluded表示排除
                nonVolatile );
    }
 
    /**
     * 添加SNMPv3用户
     */
    protected void addUsmUser( USM usm )
    {
    }
 
    /**
     * 初始化传输机制
     */
    protected void initTransportMappings() throws IOException
    {
        transportMappings = new TransportMapping[1];
        Address addr = GenericAddress.parse( address );
        TransportMapping tm = TransportMappings.getInstance().createTransportMapping( addr );
        transportMappings[0] = tm;
    }
 
    /**
     * 启动代码
     */
    public void start() throws IOException
    {
 
        init();
        addShutdownHook();
        //添加一个上下文到受管对象服务器
        getServer().addContext( new OctetString( "public" ) );
        finishInit();
        run();
        sendColdStartNotification();
    }
 
    protected void unregisterManagedObjects()
    {
    }
 
    /**
     * 在代理的Local Configuration Datastore中添加团体名字符串的表
     * 这里仅仅配置一行:public上下文中的public团体名
     */
    protected void addCommunities( SnmpCommunityMIB communityMIB )
    {
        Variable[] com2sec = new Variable[] {
                new OctetString( "public" ), // 团体名
                new OctetString( "cpublic" ), // 安全名称
                getAgent().getContextEngineID(), // 上下文引擎
                new OctetString( "public" ), // 默认上下文
                new OctetString(), // 传输标记
                new Integer32( nonVolatile ), // 存储类型
                new Integer32( RowStatus.active ) // 该行数据的状态
        };
 
        MOTableRow row = communityMIB.getSnmpCommunityEntry().createRow(
                new OctetString( "public2public" ).toSubIndex( true ),//索引OID
                com2sec //列详情
                );
        communityMIB.getSnmpCommunityEntry().addRow( row );
    }
}

下面的测试用例说明了如何注册标量、表格到Agent服务器,并通过GET/GETBULK请求进行验证:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
@FixMethodOrder ( MethodSorters.JVM )
public class AgentTest
{
 
    private static final String OID_SYS_DESCR = "1.3.6.1.2.1.1.1.0";
 
    private static Logger       LOGGER        = LoggerFactory.getLogger( AgentTest.class );
 
    private static final String AGENT_ADDR    = "udp:127.0.0.1/16100";
 
    private static final String MANAGER_ADDR  = "udp:127.0.0.1/16200";
 
    private static UdpAddress   agentAddr     = (UdpAddress) GenericAddress.parse( AGENT_ADDR );
 
    private static UdpAddress   mgrAddr       = (UdpAddress) GenericAddress.parse( MANAGER_ADDR );
 
    private static SnmpAgent    agent;
 
    private static Snmp         mgrSession;
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception
    {
        agent = new SnmpAgent( AGENT_ADDR );
        agent.start();
 
        TransportMapping transportMapping = new DefaultUdpTransportMapping( mgrAddr );
        mgrSession = new Snmp( transportMapping );
        transportMapping.listen();
 
 
    }
 
    @AfterClass
    public static void tearDownAfterClass() throws Exception
    {
        agent.stop();
        mgrSession.close();
    }
 
    private Target createSNMPv2Target()
    {
        Target target = new CommunityTarget( agentAddr, new OctetString( "public" ) );
        target.setVersion( SnmpConstants.version2c );
        target.setAddress( agentAddr );
        return target;
    }
 
    private PDU createSNMPv2PDU_GET( String oid )
    {
        PDU req = new PDU();
        req.setType( PDU.GET );
        req.add( new VariableBinding( new OID( oid ) ) );
        return req;
    }
 
    private PDU createSNMPv2PDU_GETBULK( String oid, int maxRepetitions )
    {
        PDU req = new PDU();
        req.setType( PDU.GETBULK );
        req.setMaxRepetitions( maxRepetitions );
        req.setNonRepeaters( 0 );
        req.add( new VariableBinding( new OID( oid ) ) );
        return req;
    }
 
    private String getAsString( String oid ) throws Exception
    {
        ResponseEvent respEvent = mgrSession.send( createSNMPv2PDU_GET( oid ), createSNMPv2Target() );
        PDU resp = respEvent.getResponse();
        return resp.get( 0 ).getVariable().toString();
    }
 
    private String variableToString( Variable var ) throws UnsupportedEncodingException
    {
        if ( var instanceof OctetString )
        {
            return new String( ( (OctetString) var ).getValue() );
        }
        else
        {
            return var.toString();
        }
    }
 
    /**
     * BaseAgent注册了一些默认的MIB,我们这里发起一个1.3.6.1.2.1.1.1请求(sysDescr)测试其是否正常工作
     */
    @Test
    public void testDefaultMIB() throws Exception
    {
        //测试BaseAgent注册的默认MIB-II
        assertEquals( "SNMP4J-Agent - Windows 7 - amd64 - 6.1", getAsString( OID_SYS_DESCR ) );
    }
 
    /**
     * 创建一个标量并且注册到Agent
     */
    @Test
    public void testUserDefinedScalar() throws Exception
    {
        //解除注册默认的Snmpv2 MIB
        agent.unregisterManagedObject( agent.getSnmpv2MIB() );
        String sysDescr = "Ubuntu 14 - amd64";
        MOScalar mo = new MOScalar( //创建一个标量
                new OID( OID_SYS_DESCR ), //OID
                MOAccessImpl.ACCESS_READ_ONLY,//最高访问级别
                new OctetString( sysDescr ) //值
                );
        agent.registerManagedObject( mo );
        assertEquals( sysDescr, getAsString( OID_SYS_DESCR ) );
    }
 
    /**
     * 创建一个表格并且注册到Agent,该用例将打印:
     * 1.3.6.1.4.1.8808.8.1.1: 19
     * 1.3.6.1.4.1.8808.8.1.2: 20
     * 1.3.6.1.4.1.8808.8.2.1: 汪震
     * 1.3.6.1.4.1.8808.8.2.2: 汪静好
     * 1.3.6.1.4.1.8808.8.3.1: 198609
     * 1.3.6.1.4.1.8808.8.3.2: 201411
     *
     * 可以看到SNMP表这种按列遍历的顺序以及Table中变量OID的组织方式
     */
    @SuppressWarnings ( "unchecked" )
    @Test
    public void testUserDefinedTable() throws Exception
    {
        final String STAFF_TAB_OID = "1.3.6.1.4.1.8808.8";
        OID staffTableOID = new OID( STAFF_TAB_OID ); //员工表
        MOTableSubIndex[] subIndexes = new MOTableSubIndex[] { new MOTableSubIndex( SMIConstants.SYNTAX_INTEGER ) };
        MOColumn[] columns = new MOColumn[] {
                new MOColumn( 1, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY ),//工号
                new MOColumn( 2, SMIConstants.SYNTAX_OCTET_STRING, MOAccessImpl.ACCESS_READ_ONLY ),//姓名
                new MOColumn( 3, SMIConstants.SYNTAX_INTEGER, MOAccessImpl.ACCESS_READ_ONLY ) //出生年月
        };
        DefaultMOTable table = new DefaultMOTable( staffTableOID, new MOTableIndex( subIndexes, false ), columns );
        MOMutableTableModel model = (MOMutableTableModel) table.getModel();
        model.addRow( new DefaultMOMutableRow2PC( new OID( "1" ), new Variable[] {
                new Integer32( 19 ), new OctetString( "汪震" ), new Integer32( 198609 )
        } ) );
        model.addRow( new DefaultMOMutableRow2PC( new OID( "2" ), new Variable[] {
                new Integer32( 20 ), new OctetString( "汪静好" ), new Integer32( 201411 )
        } ) );
        table.setVolatile( true );
        agent.registerManagedObject( table );
 
        PDU req = createSNMPv2PDU_GETBULK( STAFF_TAB_OID, 6 );
        ResponseEvent respEvent = mgrSession.send( req, createSNMPv2Target() );
        PDU resp = respEvent.getResponse();
        if ( resp != null && resp.getErrorStatus() == PDU.noError )
        {
            Vector<? extends VariableBinding> vbs = resp.getVariableBindings();
            StringBuilder buf = new StringBuilder();
            for ( VariableBinding vb : vbs )
            {
                Variable var = vb.getVariable();
                buf.append( String.format( "%s: %s\n", vb.getOid(), variableToString( var ) ) );
            }
            LOGGER.debug( buf.toString() );
        }
    }
}
← 基于Spring Test和Mockito进行单元测试
Ubuntu下安装SNMP组件 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Ubuntu下安装SNMP组件
  • AsyncHttpClient知识集锦
  • 基于Broadway的HTML5视频监控
  • 使用Java进行网络编程
  • Spring对WebSocket的支持

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2