snmp协议及常见问题分析

SNMP:“简单网络管理协议”,用于网络管理的协议。SNMP用于网络设备的管理。SNMP的工作方式:管理员需要向设备获取数据,所以SNMP提供了get操作;管理员需要向设备执行设置操作,所以SNMP提供了set操作;设备需要在重要状况改变的时候,向管理员通报事件的发生,所以SNMP提供了Trap操作。
 

1)操作命令
SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap。
Get:管理站读取代理者处对象的值。它是SNMP协议中使用率最高的一个命令,因为该命令是从网络设备中获得管理信息的基本方式。
Set:管理站设置代理者处对象的值
Trap: 代理者主动向管理站通报重要事件。Trap 消息可以用来通知管理站线路的故障、连接的终端和恢复、认证失败等消息。管理站可相应的作出处理。使用指南
 

2)SNMP的消息构成
一条snmp消息由版本识别符、团体名、PDU组成:
1. 版本识别符:用于说明现在使用的是哪个版本的SNMP协议,确保SNMP代理使用相同的协议,每个SNMP代理都直接抛弃与自己协议版本不同的数据报。
2. 团体名:团体是基本的安全机制,用于实现SNMP网络管理员访问SNMP管理代理时的身份验证。类似于密码,默认值为 public。
3. 协议数据单元(PDU):PDU (协议数据单元)是SNMP消息中的数据区, 即Snmp通信时报文数据的载体。PDU指明了SNMP的消息类型及其相关参数
 

3)MIB与OID
1.MIB(管理信息库)
  管理信息(MIB)库可以理解成为agent维护的管理对象数据库, MIB数据对象以一种树状分层结构进行组织,这个树状结构中的每个分枝都有一个专用的名字和一个数字形式的标识符。使用这个树状分层结构,MIB浏览器能够以一种方便而且简洁的方式访问整个MIB数据库。可以通过其数字标识符来查找MIB中的数据对象,这个数字标识符号从结构树的顶部(或根部)开始,直到各个叶 子节点(即数据对象)为止。
  每一个节点都有一个对象标识符(OID)来唯一的标识,每个节点用数字和字符两种方式显示,其中对象标识符OID是由句点隔开的一组整数,也就是从根节点 通向它的路径。一个带标号节点可以拥有包含其它带标号节点为它的子树,如果没有子树它就是叶子节点,它包含一个值并被称为对象。比如网络设备名的oid 是.1.3.6.1.2.1.1.5.0,其值为设备名称的字符串。
2.OID(Object Identifier)
  每个管理对象都有自己的OID(Object Identifier),管理对象通过树状结构进行组织,OID由树上的一系列整数组成,整数之间用点( . )分隔开,树的叶子节点才是真正能够被管理的对象。

1.2 主要代码分析
这该模块对snmp的get、walk、trap三种方式的业务处理进行介绍。
执行过程概述
驻留在被管设备上的AGENT从UDP端口161接受来自网管站的串行化报文,经解码、团体名验证、分析得到管理变量在MIB树中对应的节点,从相应的模块中得到管理变量的值,再形成响应报文,编码发送回网管站。网管站得到响应报文后,再经同样的处理,最终显示结果。下面根据RFC1157详细介绍Agent接受到报文后采取的动作:首先解码生成用内部数据结构表示的报文,解码依据ASN.1的基本编码规则,如果在此过程中出现错误导致解码失败则丢弃该报文,不做进一步处理。第二步:将报文中的版本号取出,如果与本Agent支持的SNMP版本不一致,则丢弃该报文,不做进一步处理。当前北研的数据通信产品只支持SNMP版本1。第三步:将报文中的团体名取出,此团体名由发出请求的网管站填写。如与本设备认可的团体名不符,则丢弃该报文,不做进一步处理,同时产生一个陷阱报文。SNMPv1只提供了较弱的安全措施,在版本3中这一功能将大大加强。第四步:从通过验证的ASN.1对象中提出协议数据单元PDU,如果失败,丢弃报文,不做进一不处理。否则处理PDU,结果将产生一个报文,该报文的发送目的地址应同收到报文的源地址一致。根据不同的PDU,SNMP协议实体将做不同的处理
 

常见问题:

1.使用get方式请求获取信息,一直返回 noSuchObject说明没有找到该节点,需要确定该oid是否正确。若没有返回说明连接不正常,需要检查各个版本、用户名、密码、协议、团体名等配置信息是否正确。
2. 创建目标对象时,使用v1、v2版本时需要指定团体名,使用v3需要指定安全名。
3.配置v3用户时,最好通过管理口进行配置,web页面配置可能不生效。
4.使用get方式请求获取信息,返回的数据为null,说明oid有错误的,错误的oid后面的返回oid都会为null,需要把错误的oid改正确。(亲身经历过)​​​​​​​

5、通过snmp协议批量请求oid时,会遇到部分设备返回oid信息为空

排查日志后,发现是返回的response为空,但抓包发现response是有值的,而且会重复请求,同一个oid的请求时间和响应时间相差30s左右,然后通过ping命令发现有丢包问题,分析下来是网络问题,于是就调大snmp连接超时时间(亲身经历过)

​​​​​​​

ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
CommunityTarget target = new CommunityTarget();
target.setTimeout(60000);

walk方式获取数据:

 

public class MultiThreadedWalkDemo {
    private static final Logger LOGGER = LogManager.getLogger(MultiThreadedWalkDemo.class);
//用户名
    private String username = "nms2-admin";
    //鉴权密码
    private String authPassword = "hello123";
    //数据加密密码
    private String privPassword = "hello123";
    //trap地址
    private String address = "udp:192.160.0.1/162";
    //get 地址
    private String addressGet = "udp:192.160.0.2/161";

    public MultiThreadedWalkDemo() {

    }



     public void  initSnmp() throws IOException {
         //1、初始化多线程消息转发类
         MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
         //其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping transportMapping) ,就不需要增加
         messageDispatcher.addMessageProcessingModel(new MPv1());
         messageDispatcher.addMessageProcessingModel(new MPv2c());
         //当要支持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);

         usm.addUser(user.getSecurityName(), user);
         messageDispatcher.addMessageProcessingModel(new MPv3(usm));
         //2、创建transportMapping  ip为本地ip,可以不设置
         //UdpAddress updAddr = (UdpAddress) GenericAddress.parse("udp:192.168.22.100/161");
         TransportMapping transportMapping = new DefaultUdpTransportMapping();
         //3、正式创建snmp
         snmp = new Snmp(messageDispatcher, transportMapping);
         //开启监听
         snmp.listen();

     }

    private  Target createTarget(String oid) {
        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(this.username));
        } else {
            //snmpV1和snmpV2需要指定团体名名称
            target = new CommunityTarget();
            ((CommunityTarget) target).setCommunity(new OctetString(this.username));
            if (version == SnmpConstants.version2c) {
                target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
            }
        }
        target.setVersion(version);
        //必须指定,没有设置就会报错。
        target.setAddress(GenericAddress.parse(this.addressGet));
        target.setRetries(3);
        target.setTimeout(2000);
        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;
    }


    /**
     * WALK方式请求
     * @param oid
     */
    public void snmpWalk(String oid) {
        try {
            LOGGER.info("walkfangshi");
            List list = new ArrayList();
            //1、初始化snmp,并开启监听
			initSnmp();
            //2、创建目标对象
            Target target = createTarget(oid);
            //3、创建报文
            PDU pdu = createPDU(1, PDU.GETNEXT, oid);
            //4、发送报文,并获取返回结果
            boolean matched = true;
            while (matched) {
                ResponseEvent responseEvent = snmp.send(pdu, target);
                if (responseEvent == null || responseEvent.getResponse() == null) {
                    System.out.println("TimeOut...");
                    break;
                }
                PDU response = responseEvent.getResponse();
                String nextOid = null;
                Vector variableBindings = response.getVariableBindings();
                for (int i = 0; i < variableBindings.size(); i++) {
                    Map map = new HashMap();
                    VariableBinding variableBinding = variableBindings.elementAt(i);
                    Variable variable = variableBinding.getVariable();
                    nextOid = variableBinding.getOid().toDottedString();
                    //如果不是这个节点下的oid则终止遍历,否则会输出很多,直到整个遍历完。
                    if (!nextOid.startsWith(oid)) {
                        matched = false;
                        break;
                    }
                    map.put("oid", nextOid);
                    map.put("value",variable);
                    list.add(map);
                    System.out.println("oid:" + nextOid + ",value:"  + variable);
                    LOGGER.info("oid:" + nextOid + ",value:"  + variable);
                }
                if (!matched) {
                    break;
                }
                pdu.clear();
                pdu.add(new VariableBinding(new OID(nextOid)));
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    //开启监控的main方法。
    public static void main(String[] args) throws IOException {
        MultiThreadedWalkDemo multithreadedtrapreceiver = new MultiThreadedWalkDemo();
        multithreadedtrapreceiver.run();
    }

    public void run() {
        try {
            LOGGER.info("开始监听walk信息!");
            LOGGER.info("开始获取端口名称!");
            String healthOID1 = "1.3.6.1.2.1.2.2.1.2";
            this.snmpWalk(healthOID1);
            LOGGER.info("网口流量");
            String healthOID2 = "1.3.6.1.2.1.2.2.1.10";
            this.snmpWalk(healthOID2);
            LOGGER.info("网口状态");
            String healthOID3 = "1.3.6.1.2.1.2.2.1.16";
            this.snmpWalk(healthOID3);
            LOGGER.info("网口速率");
            String healthOID4 = "1.3.6.1.2.1.31.1.1.1.15";
            this.snmpWalk(healthOID4);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

trap监听:



public class MultiThreadedTrapReceiverDemo implements CommandResponder {

    private static final Logger logger = LogManager.getLogger(MultiThreadedTrapReceiverDemo.class);
	//用户名
    private String username = "nms2-admin";
    //鉴权密码
    private String authPassword = "hello123";
    //数据加密密码
    private String privPassword = "hello123";
    //trap地址
    private String address = "udp:192.160.0.1/162";
    //get 地址
    private String addressGet = "udp:192.160.0.2/161";
	
    private MultiThreadedMessageDispatcher dispatcher;
    private Snmp snmp = null;
    private Address listenAddress;
    private ThreadPool threadPool;
    public MultiThreadedTrapReceiverDemo() {
        
    }
    private void init() throws UnknownHostException, IOException {
        try {
            //创建接收SnmpTrap的线程池,参数: 线程名称及线程数
            threadPool = ThreadPool.create("Trap", 2);
            //创建一个多线程消息分发器,以同时处理传入的消息,该实例将用于分派传入和传出的消息
            dispatcher = new MultiThreadedMessageDispatcher(threadPool,
                    new MessageDispatcherImpl());
            //监听端的 ip地址 和 监听端口号
            listenAddress = GenericAddress.parse(address);
            //在指定的地址上创建UDP传输
            TransportMapping transport;
            if (listenAddress instanceof UdpAddress) {
                //必须是本机地址
                transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
            } else {
                transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
            }
            //初始化snmp需要设置messageDispatcher里面的参数和TransportMapping参数
            snmp = new Snmp(dispatcher, transport);
            //消息分发器添加接收的版本信息
            /*      v1和v2都具有基本的读、写MIB功能。*
             *      v2增加了警报、批量数据获取、管理站和管理站通信能力。*
             *      v3在v2的基础上增加了USM,使用加密的数据和用户验证技术,提高了安全性*/
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
            snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
            //创建具有所提供安全协议支持的USM
            USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3 //根据本地IP地址和其他四个随机字节创建本地引擎ID
                    .createLocalEngineID()), 0);
            SecurityModels.getInstance().addSecurityModel(usm);
            // 添加安全协议,如果没有发过来的消息没有身份认证,可以跳过此段代码
            SecurityProtocols.getInstance().addDefaultProtocols();
            // 创建和添加用户
            OctetString userName1 = new OctetString(username);
            OctetString authPass = new OctetString(authPassword);
            OctetString privPass = new OctetString(privPassword);
            UsmUser usmUser1 = new UsmUser(userName1, AuthMD5.ID, authPass, PrivAES128.ID, privPass);
            //因为接受的Trap可能来自不同的主机,主机的Snmp v3加密认证密码都不一样,所以根据加密的名称,来添加认证信息UsmUser。
            //添加了加密认证信息的便可以接收来自发送端的信息。
            UsmUserEntry userEnty1 = new UsmUserEntry(userName1, usmUser1);
            UsmUserTable userTable = snmp.getUSM().getUserTable();
            // 添加其他用户
            userTable.addUser(userEnty1);
            //开启Snmp监听,可以接收来自Trap端的信息。
            snmp.listen();
            logger.info("开始监听trap告警");
        }catch (Exception e){
            e.printStackTrace();
            logger.info(e.getMessage());
        }
    }


    public void run() {
        try {
            init();
            snmp.addCommandResponder(this);
            System.out.println("开始监听Trap信息!");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 实现CommandResponder的processPdu方法, 用于处理传入的请求、PDU等信息
     * 当接收到trap时,会自动进入这个方法
     *
     * @param respEvnt
     */
    public void processPdu(CommandResponderEvent respEvnt) {
        // 解析Response
            logger.info("trap接受到告警消息,开始对消息进行处理");
        try {
            if (respEvnt != null && respEvnt.getPDU() != null) {
                RtEvent rtEvent = new RtEvent();
                for (int i = 0; i < respEvnt.getPDU().getVariableBindings().size(); i++) {
                    logger.info("消息体oid:"+respEvnt.getPDU().getVariableBindings().elementAt(i).getOid());
                    logger.info("消息体oid对应值:"+respEvnt.getPDU().getVariableBindings().elementAt(i).getVariable());
                }
                //获取对方交换机等设备的ip
                String ip = respEvnt.getPeerAddress().toString().split("/")[0].split(":")[0];
                rtEvent.setIp(ip);
                //获取设备以及trap信息
                DevAndTrapOid devAndTrapOid = ModelCache.getInstance().getDevAndTrapOid(ip);
                
                @SuppressWarnings("unchecked")
                Vector recVBs = (Vector) respEvnt.getPDU().getVariableBindings();
                for (int i = 0; i < recVBs.size(); i++) {
                    VariableBinding recVB = recVBs.elementAt(i);
                    String value = "点号" + recVB.getOid() + ";内容:" + recVB.getVariable();
                    logger.info("二区trap初始snmp,点号:" + recVB.getOid() + ";内容:" + recVB.getVariable());
                    System.out.println(value);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
	    //开启监控的main方法。
    public static void main(String[] args) throws IOException {
        MultiThreadedTrapReceiverDemo multithreadedtrapreceiver = new MultiThreadedTrapReceiverDemo();
        multithreadedtrapreceiver.run();
    }
}

get方式获取数据:

package com.snmp.collection.get_collection.collectionforII;


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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.*;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.UnknownHostException;
import java.util.*;

public class MultiThreadedGetDemo {
    private static final Logger LOGGER = LogManager.getLogger(MultiThreadedGetDemo.class);
	//用户名
    private String username = "nms2-admin";
    //鉴权密码
    private String authPassword = "hello123";
    //数据加密密码
    private String privPassword = "hello123";
    //trap地址
    private String address = "udp:192.160.0.1/162";
    //get 地址
    private String addressGet = "udp:192.160.0.2/161";
	
    private MultiThreadedMessageDispatcher dispatcher;
    private Snmp snmp = null;
    private Address listenAddress;
    private ThreadPool threadPool;

    public MultiThreadedGetDemo() {

    }



     public void  initSnmp() throws IOException {
         //1、初始化多线程消息转发类
         MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
         //其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping transportMapping) ,就不需要增加
         messageDispatcher.addMessageProcessingModel(new MPv1());
         messageDispatcher.addMessageProcessingModel(new MPv2c());
         //当要支持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);

         usm.addUser(user.getSecurityName(), user);
         messageDispatcher.addMessageProcessingModel(new MPv3(usm));
         //2、创建transportMapping  ip为本地ip,可以不设置
         TransportMapping transportMapping = new DefaultUdpTransportMapping();
         //3、正式创建snmp
         snmp = new Snmp(messageDispatcher, transportMapping);
         //开启监听
         snmp.listen();

     }

    private  Target createTarget(String oid) {
        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(this.username));
        } else {
            //snmpV1和snmpV2需要指定团体名名称
            target = new CommunityTarget();
            ((CommunityTarget) target).setCommunity(new OctetString(this.username));
            if (version == SnmpConstants.version2c) {
                target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
            }
        }
        target.setVersion(version);
        //必须指定,没有设置就会报错。
        target.setAddress(GenericAddress.parse(this.addressGet));
        target.setRetries(3);
        target.setTimeout(2000);
        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;
    }

    /**
     * GET方式请求
     * @param oid
     */
    public  List snmpGet(String oid) {
        try {
            LOGGER.info("getfangshi");
            List list = new ArrayList();
            //1、初始化snmp,并开启监听
			initSnmp();
            //2、创建目标对象
            Target target = this.createTarget(oid);
            //3、创建报文
            PDU pdu = createPDU(1, PDU.GET, oid);
            //4、发送报文,并获取返回结果
            ResponseEvent responseEvent = snmp.send(pdu, target);
            PDU response = responseEvent.getResponse();
            if (response == null) {
                System.out.println("TimeOut...");
            } else {
                if (response.getErrorStatus() == PDU.noError) {
					//get方式获取到的返回值
                    Vector vbs = response.getVariableBindings();
                    for (VariableBinding vb : vbs) {
                        Map map = new HashMap();
                        map.put("value",vb.getVariable());
                        System.out.println("vb.getVariable():"  + vb.getVariable());
                        System.out.println("OID:"  + vb.getVariable());
                        LOGGER.info("OIDvALUE"  + vb.getVariable());
                        list.add(map);
                    }
                    return list;
                } else {
                    System.out.println("Error:" + response.getErrorStatusText());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    //开启监控的main方法。
    public static void main(String[] args) throws IOException {
        MultiThreadedGetDemo multithreadedtrapreceiver = new MultiThreadedGetDemo();
        multithreadedtrapreceiver.run();
    }

    public void run() {
        try {
            System.out.println("开始监听get信息!");
            LOGGER.info("内存");
            String devOid = "1.3.6.1.4.1.25506.2.6.1.1.1.1.8";
            this.snmpGet(devOid);
            LOGGER.info("cpu");
            String devOid1 = "1.3.6.1.4.1.25506.2.6.1.1.1.1.6";
            this.snmpGet(devOid1);
            LOGGER.info("运行时间");
            String data1 = "1.3.6.1.2.1.1.3.0";
            this.snmpGet(data1);
           
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

你可能感兴趣的:(java,开发语言)