为什么snmpv3 get PDU在不存在OID的情况下成功返回?

为什么snmpv3 get PDU在不存在OID的情况下成功返回?,snmp,snmp4j,Snmp,Snmp4j,当我使用SNMP4J模拟snmpv3的GET PDU时,我发现虽然我向变量绑定列表中添加了一个不存在的OID,但仍然得到了一个有效的响应。但是当协议版本为v1时,我得到了错误代码2,表示“没有这样的名称”。 当协议版本为v2c时,我没有得到错误,返回的vb列表大小为1,列表(0)变量绑定的语法为exception,这意味着“没有这样的对象” 因此,当使用v1或v2c时,我可以确定它是否存在,但当它是v3时,我得到了以下响应: SNMP-USER-BASED-SM-MIB::usmStatsUnk

当我使用SNMP4J模拟snmpv3的GET PDU时,我发现虽然我向变量绑定列表中添加了一个不存在的OID,但仍然得到了一个有效的响应。
但是当协议版本为v1时,我得到了错误代码2,表示“没有这样的名称”。
当协议版本为v2c时,我没有得到错误,返回的vb列表大小为1,列表(0)变量绑定的语法为exception,这意味着“没有这样的对象”
因此,当使用v1或v2c时,我可以确定它是否存在,但当它是v3时,我得到了以下响应:

SNMP-USER-BASED-SM-MIB::usmStatsUnknownEngineIDs.0 = Counter32: xx
**xx是一个数字

代码是:

        import org.snmp4j.*;
        import org.snmp4j.event.ResponseEvent;
        import org.snmp4j.mp.SnmpConstants;
        import org.snmp4j.security.*;
        import org.snmp4j.smi.*;
        import org.snmp4j.transport.DefaultUdpTransportMapping;

          import static org.snmp4j.mp.MPv3.createLocalEngineID;

    /**
     *
     *  Following syntax may helpfull:  SMIConstants
     *    public static final int EXCEPTION_NO_SUCH_OBJECT = BER.NOSUCHOBJECT;
     *    public static final int EXCEPTION_NO_SUCH_INSTANCE = BER.NOSUCHINSTANCE;
     *    public static final int EXCEPTION_END_OF_MIB_VIEW = BER.ENDOFMIBVIEW;
     *
     * give a string and test whether it exists or not
     *
     * Conclusions:
     * (1)When the op is GET,
     *    V1:
     *    errorcode at pdu, AND IS 2, MEANS "no such name"
     *    V2C:
     *    NOT GET ERROR, and returned vb list size is 1, and the list(0) Null.isExceptionSyntax(vb.getSyntax()) == true // means "no such object"
     *    V3:
     *    NOT GET ERROR, like this : SNMP-USER-BASED-SM-MIB::usmStatsUnknownEngineIDs.0 = Counter32: 23
     *    returned varbingds : 1.3.6.1.6.3.15.1.1.3.0 = xxx
     *  (2)When the op is GETNEXT:
     *     V1:
     *     errorcode at pdu, AND IS 2, MEANS "no such name"
     *     V2C:
     *     NOT GET ERROR, and returned vb list size is 1, and the list(0) Null.isExceptionSyntax(vb.getSyntax()) == true // means "endOfMibView"
     *     V3:
     *     NOT GET ERROR, like this : SNMP-USER-BASED-SM-MIB::usmStatsUnknownEngineIDs.0 = Counter32: 23
     *  (3)When the op is GETBULK:
     *
     * Created by edward.gao on 8/9/16.
     */
    public class TestOidExists {

    private static final String _V1_OR_V2_COMMUNITYNAME= "public";
    private static final String _V3_USERNAME = "edward";
    /**
     * authentication protocol MD5 or SHA
     */
    private static final String _V3_AUTHENTICATION_PASSPHRASE = "auth12345678";

    /**
     * privacy protocol DES OR AES
     */
    private static final String _V3_PRIVACY_PASSPHRASE = "encry12345678";

    public static void main(String[] args) throws Exception{

        final String addrString= "udp:192.168.170.150/161";
        byte [] localEngineID = createLocalEngineID();
        //version3 need usm
        USM usm = new USM(SecurityProtocols.getInstance(),new OctetString(localEngineID), 0);
        SecurityModels.getInstance().addSecurityModel(usm);
        Snmp snmp = new Snmp(new DefaultUdpTransportMapping());

        snmp.listen();


        String [] oids = new String[] {/*".1.3.6.1.2.1.1.5.0", ".1.3.6.1.2.1.25.6.3.1.2", ".1.3.6.1.2.1.1.5",*/ ".1.3.999"};
        String [] types = new String[]{ "get","getnext", "getbulk"};
        for (String oid : oids)
        {
            for (String type : types) {
                System.out.println(String.format("VERSION IS 3, oid:%s result: %d", oid, typeOfOID(oid, snmp, SnmpConstants.version3, type)));
                System.out.println(String.format("VERSION IS 2, oid:%s result: %d", oid, typeOfOID(oid, snmp, SnmpConstants.version2c, type)));
                System.out.println(String.format("VERSION IS 1, oid:%s result: %d", oid, typeOfOID(oid, snmp, SnmpConstants.version1, type)));
            }
        }
    }

    /**
     * get the type of matched oid string
     * @param oid
     * @param snmp
     * @return  -1 : not exist , 0, leaf, 1, tree
     */
    public static int typeOfOID(String oid , Snmp snmp, int version, String typeString)  throws  Exception
    {


        boolean isv3 = version == SnmpConstants.version3;
        //build a client
        Address addr = GenericAddress.parse("192.168.170.150/161");
        Target target = null;
        if (isv3) {
            UsmUser usmUser = new UsmUser(new OctetString(_V3_USERNAME),
                    AuthMD5.ID,
                    new OctetString(_V3_AUTHENTICATION_PASSPHRASE),
                    PrivDES.ID,
                    new OctetString(_V3_PRIVACY_PASSPHRASE)
            );
            snmp.getUSM().addUser(usmUser);
            target = new UserTarget();
        }
        else
        {
            target = new CommunityTarget();
            //the default version is 3
            if (version == SnmpConstants.version2c)
            {
                target.setVersion(SnmpConstants.version2c);
            }
            else
            {
                target.setVersion(SnmpConstants.version1);
            }
            ((CommunityTarget)target).setCommunity(new OctetString("public"));
        }


        target.setAddress(addr);
        target.setRetries(2);
        target.setTimeout(30000);

        int type = typeString.equalsIgnoreCase("get") ? PDU.GET : (typeString.equalsIgnoreCase("getnext") ? PDU.GETNEXT : PDU.GETBULK);
        System.out.println("Using version " + (version == SnmpConstants.version1 ? "v1" :(version == SnmpConstants.version2c ? "v2c " : "v3 ")) + " type:" + typeString);
        PDU pdu = version == SnmpConstants.version3 ? new ScopedPDU() : new PDU();
        pdu.setType(type);
        pdu.add(new VariableBinding(new OID(oid)));
        pdu.setRequestID(new Integer32(0));
        ResponseEvent responseEvent = snmp.send(pdu, target);
        if (responseEvent.getError() == null)
        {
            PDU responsePdu = responseEvent.getResponse();
            if (responsePdu.getErrorStatus() == 0)
            {
                responsePdu.getVariableBindings().forEach(vb ->System.out.print(vb + " " + Null.isExceptionSyntax(vb.getSyntax())));
                System.out.println();
                //leaf get only one, other get more than one
                return responsePdu.getVariableBindings().size() == 1 ? 0 : 1;
            }
            else
            {
                System.out.println("error code at pdu," +  responsePdu.getErrorStatus() + " text:" + responsePdu.getErrorStatusText() + " index:" + responsePdu.getErrorIndex());
            }
        }
        else
        {
            System.out.println("error happend,");
            responseEvent.getError().printStackTrace();
        }
        return -1;
    }

}
所以我的问题是:
(1) v3的这种行为是否正确实现?为什么我没有得到某种错误?

(2) 如何使用SNMP4J测试OID是否存在?

您将收到所谓的SNMP报告PDU。发生的情况是,代理从manager接收到请求PDU,并确定某些重要的安全属性(如
引擎id
引擎启动
引擎时间
)丢失,然后发回报告通知manager。经理应完全按照中详细描述的所谓SNMP引擎发现过程来处理报告PDU。

对于问题1,当我设置userTarget的securityName和securityLevel时,它工作正常

        userTarget.setSecurityName(new OctetString(_V3_USERNAME));
        userTarget.setSecurityLevel(SecurityLevel.AUTH_PRIV);
所以对于问题2,现在版本1和2c也是一样的

参考:rfc3414:

3) 如果 securityParameters未知,则: a) 执行发现的非权威SNMP引擎可能会 (可选)在其本地配置中创建新条目 数据存储(LCD)并继续处理; 或 b) usmstatsunnownengineids计数器递增,并且 错误指示(未知发动机ID)以及OID和 递增计数器的值返回给调用 模块